/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package woolpack.sql;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import junit.framework.TestCase;
import woolpack.adapter.HSQLDBUtils;
import woolpack.convert.ConvertUtils;
import woolpack.factory.FactoryUtils;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.sql.fn.PreparedStatementInfo;
import woolpack.sql.fn.SqlFnUtils;
import woolpack.sql.meta.SqlMetaUtils;
import woolpack.sql.meta.TableInfo;
import woolpack.sql.meta.TableInfoUtils;
import woolpack.sql.tx.SqlTxUtils;
import woolpack.sql.tx.TxBuilder;
import woolpack.test.TestBean;
import woolpack.utils.Utils;

public class SqlFnUtilsTest extends TestCase {

	public void testNormal() throws Exception {
		final DataSource dataSource = HSQLDBUtils.getDataSource(false);
		final TxBuilder builder = new TxBuilder(dataSource);
		
		assertEquals(Integer.valueOf(0), SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"create memory table my_table (" +
				"my_id integer, " +
				"my_seq integer, " +
				"my_string varchar, " +
				"my_int integer, " +
				"constraint MYTALBE0P primary key (my_id, my_seq))",
				SqlFnUtils.GET_COUNT, FnUtils.fix(null))
		).exec(null));
		
		assertEquals(Integer.valueOf(1), SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputBean(
				builder.getTmpDataSource(),
				new PreparedStatementInfo(
					"insert into my_table (my_id, my_seq, my_string, my_int) values (?, ?, ?, ?)",
					Arrays.asList("myId", "mySeq", "myString", "myInt")),
				SqlFnUtils.GET_COUNT, FnUtils.fix(null))
		).exec(Utils.map("myId", (Object) 0).map("mySeq", 1).map("myString", "c0").map("myInt", 2)));
		
		assertEquals("c0", SqlTxUtils.tx(
			builder.getTxDataSource(),
			SqlFnUtils.inputSingle(
				builder.getTmpDataSource(),
				"select my_string from my_table where my_id = ?",
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(0));
		assertEquals("c0", SqlTxUtils.tx(
			builder.getTxDataSource(),
			SqlFnUtils.inputSingle(
				builder.getTmpDataSource(),
				FnUtils.fix("select my_string from my_table where my_id = ?"),
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(0));
		
		assertEquals(
			Utils.list(Utils.map("MY_ID", (Object) 0).map("MY_SEQ", 1).map("MY_STRING", "c0").map("MY_INT", 2)),
			SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputBean(
				builder.getTmpDataSource(),
				FnUtils.fix(new PreparedStatementInfo(
						"select * from my_table where my_id = ? and my_seq = ?",
						Arrays.asList("myId", "mySeq"))),
				SqlFnUtils.getList(SqlFnUtils.getBeanResult(FactoryUtils.LINKED_MAP_FACTORY), FnUtils.fix(null)), FnUtils.fix(null))
		).exec(Utils.map("myId", (Object) 0).map("mySeq", 1)));
		
		// エラー発生時ロールバックのテスト
		try {
			SqlTxUtils.tx(builder.getTxDataSource(),
				SqlFnUtils.inputStatement(
					builder.getTmpDataSource(),
					"insert into my_table (my_id, my_seq, my_string, my_int) values (0, 2, 'c1', 2)",
					FnUtils.throwing(new IllegalStateException("hoge")), FnUtils.fix(null))
			).exec(null);
			fail();
		} catch (final IllegalStateException e) {
			assertEquals("hoge", e.getMessage());
		}

		assertNull(SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				FnUtils.fix("select my_string from my_table where my_id = 0 and my_seq = 2"),
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));

		// 業務内ロールバックのテスト
		SqlTxUtils.tx(builder.getTxDataSource(),
				SqlFnUtils.inputStatement(
					builder.getTmpDataSource(),
					"insert into my_table (my_id, my_seq, my_string, my_int) values (0, 2, 'c1', 2)",
					SqlTxUtils.rollback(builder.getTmpDataSource(false)), FnUtils.fix(null))
			).exec(null);

		assertNull(SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_string from my_table where my_id = 0 and my_seq = 2",
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));
		
		// 手動コミットのテスト
		SqlTxUtils.tx(builder.getTxDataSource(false),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"insert into my_table (my_id, my_seq, my_string, my_int) values (0, 2, 'c1', 2)",
				SqlFnUtils.GET_COUNT, FnUtils.fix(null))
		).exec(null);

		assertNull(SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_string from my_table where my_id = 0 and my_seq = 2",
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));
		
		SqlTxUtils.tx(builder.getTxDataSource(false),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"insert into my_table (my_id, my_seq, my_string, my_int) values (0, 2, 'c1', 2)",
				SqlTxUtils.commit(builder.getTmpDataSource(false)), FnUtils.fix(null))
		).exec(null);

		assertEquals("c1", SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_string from my_table where my_id = 0 and my_seq = 2",
				SqlFnUtils.getOne(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));
		
		// 取得件数制御のテスト
		assertEquals(Arrays.asList("c0", "c1"), SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_string from my_table",
				SqlFnUtils.getList(SqlFnUtils.GET_SINGLE, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));

		assertEquals(Arrays.asList("c0"), SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_string from my_table",
				SqlFnUtils.getList(SqlFnUtils.GET_SINGLE, 1, FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null));
		
		// BeanResultのテスト
		{
			final TestBean result = SqlTxUtils.tx(builder.getTxDataSource(),
				SqlFnUtils.inputStatement(
					builder.getTmpDataSource(),
					"select * from my_table where my_id = 0 and my_seq = 2",
					SqlFnUtils.getOne(
							SqlFnUtils.getBeanResult(FactoryUtils.newInstance(TestBean.class)),
							FnUtils.fix(null)), FnUtils.fix(null))
			).exec(null);
			assertEquals(2, result.getMyInt());
		}
		// metaのテスト
		{
			final List<TableInfo> list = TableInfoUtils.getTableInfoList(dataSource);
			final TableInfo tableInfo = TableInfoUtils.convertToMap(list).get("MY_TABLE");
			{
				final Fn<Map<String, ?>, List<Map<String, Object>>, Exception> fn = SqlFnUtils.inputBean(
					dataSource,
					FnUtils.join(
						ConvertUtils.<String>keySet(),
						SqlMetaUtils.SELECT_FACTORY_FACTORY.exec(tableInfo)),
						SqlFnUtils.getList(SqlFnUtils.getBeanResult(FactoryUtils.LINKED_MAP_FACTORY), FnUtils.fix(null)), FnUtils.fix(null));
				assertEquals(
					Utils.list(Utils.map("MY_ID", (Object) 0).map("MY_SEQ", 1).map("MY_STRING", "c0").map("MY_INT", 2)),
					fn.exec(Utils.map("MY_ID", (Object) 0).map("MY_SEQ", 1)));
			}
		}
		// beanのテスト
		assertEquals(2, SqlTxUtils.tx(builder.getTxDataSource(),
			SqlFnUtils.inputStatement(
				builder.getTmpDataSource(),
				"select my_int from my_table where my_id = 0 and my_seq = 2",
				SqlFnUtils.getOne(
						SqlFnUtils.getBeanResult(FactoryUtils.newInstance(TestBean.class)),
						FnUtils.fix(null)), FnUtils.fix(null))
		).exec(null).getMyInt());
	}
}
