package nuts.core.orm.sql.engine;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

import nuts.core.beans.BeanHandlerFactory;
import nuts.core.beans.FastBeanHandlerFactory;
import nuts.core.lang.ClassUtils;
import nuts.core.orm.sql.SqlExecutor;
import nuts.core.orm.sql.TestA;
import nuts.core.orm.sql.TestSupport;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

/**
 * PerformanceTest
 */
public class PerformanceTest extends TestCase {
	private static Log log = LogFactory.getLog(PerformanceTest.class);

	private final static int PCOUNT = 10;
	private final static int ECOUNT = 1000;
	
	private String loadSql(String sql) throws Exception {
		return IOUtils.toString(this.getClass().getResourceAsStream(sql));
	}

	private void executeSimple(SqlExecutor executor) throws Exception {
		String[] sqls = new String[] {
				"SELECT * FROM TEST WHERE ID=:id ORDER BY ID",
				"SELECT * FROM TEST WHERE NAME=:name ORDER BY ID", 
				"SELECT * FROM TEST WHERE ID IN (:idList) ORDER BY ID", 
				"SELECT * FROM TEST WHERE ID IN (:idList) ORDER BY ::orderCol ::orderDir"
		};
		executeSqls(executor, sqls);
	}

	private void executeSql(SqlExecutor executor, String sql) throws Exception {
		String[] sqls = new String[] { sql, sql, sql, sql };
		executeSqls(executor, sqls);
	}
	
	private void executeSqls(SqlExecutor executor, String[] sqls) throws Exception {
		int i = 0;
		
		TestA param;
		List<Integer> idList;
		
		List<TestA> results;
		
		param = new TestA();
		param.setId(1005);
		results = executor.queryForList(sqls[i++], param, TestA.class);
		assertEquals(1, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		param.setIdList(idList);
		param.setName("NAME 1001");
		results = executor.queryForList(sqls[i++], param, TestA.class);
		assertEquals(1, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		idList.add(1002);
		idList.add(1003);
		param.setIdList(idList);
		results = executor.queryForList(sqls[i++], param, TestA.class);
		assertEquals(2, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		idList.add(1003);
		idList.add(1004);
		param.setIdList(idList);
		param.setOrderCol("NAME");
		param.setOrderDir("DESC");
		results = executor.queryForList(sqls[i++], param, TestA.class);
		assertEquals(2, results.size());
	}

	private void executeSql(SqlMapClient sqlMapClient, String sql) throws Exception {
		String[] sqls = new String[] { sql, sql, sql, sql };
		executeSqls(sqlMapClient, sqls);
	}
	
	@SuppressWarnings("unchecked")
	private void executeSqls(SqlMapClient sqlMapClient, String[] sqls) throws Exception {
		int i = 0;
		
		TestA param;
		List<Integer> idList;
		
		List<TestA> results;
		
		param = new TestA();
		param.setId(1005);
		results = sqlMapClient.queryForList(sqls[i++], param);
		assertEquals(1, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		param.setIdList(idList);
		param.setName("NAME 1001");
		results = sqlMapClient.queryForList(sqls[i++], param);
		assertEquals(1, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		idList.add(1002);
		idList.add(1003);
		param.setIdList(idList);
		results = sqlMapClient.queryForList(sqls[i++], param);
		assertEquals(2, results.size());

		param = new TestA();
		idList = new ArrayList<Integer>();
		idList.add(1003);
		idList.add(1004);
		param.setIdList(idList);
		param.setOrderCol("NAME");
		param.setOrderDir("DESC");
		results = sqlMapClient.queryForList(sqls[i++], param);
		assertEquals(2, results.size());
	}

	/**
	 */
	public void testPrepare() {
		BeanHandlerFactory.setInstance(new BeanHandlerFactory());
	}

	/**
	 * testSimple
	 */
	public void testSimple() {
		try {
			SqlExecutor exe = new SimpleSqlExecutor(TestSupport.getHsqldbConnection());;
			for (int i = 0; i < PCOUNT; i++) {
				executeSimple(exe);
			}

			long start = System.currentTimeMillis();
			for (int i = 0; i < ECOUNT; i++) {
				executeSimple(exe);
			}
			long end = System.currentTimeMillis();

			log.debug("SimpleSqlExecutor - elapsed - " + (end - start));
		}
		catch (Exception e) {
			log.error(e.getMessage(), e);
			fail(e.getMessage());
		}
	}

	/**
	 * testExtend
	 */
	public void testExtend() {
		try {
			SqlExecutor exe = new ExtendSqlExecutor(TestSupport.getHsqldbConnection());;

			String sql = loadSql("test.sql");
			for (int i = 0; i < PCOUNT; i++) {
				executeSql(exe, sql);
			}

			long start = System.currentTimeMillis();
			for (int i = 0; i < ECOUNT; i++) {
				executeSql(exe, sql);
			}
			long end = System.currentTimeMillis();

			log.debug("ExtendSqlExecutor - elapsed - " + (end - start));
		}
		catch (Exception e) {
			log.error(e.getMessage(), e);
			fail(e.getMessage());
		}
	}
	
	/**
	 * testJavascript
	 */
	public void testJavascript() {
		try {
			SqlExecutor exe = new JavaScriptSqlExecutor(TestSupport.getHsqldbConnection());;

			String sql = loadSql("test.sql.js");
			for (int i = 0; i < PCOUNT; i++) {
				executeSql(exe, sql);
			}

			long start = System.currentTimeMillis();
			for (int i = 0; i < ECOUNT; i++) {
				executeSql(exe, sql);
			}
			long end = System.currentTimeMillis();

			log.debug("JavaScriptSqlExecutor - elapsed - " + (end - start));
		}
		catch (Exception e) {
			log.error(e.getMessage(), e);
			fail(e.getMessage());
		}
	}
	
	/**
	 * testFreemarker
	 */
	public void testFreemarker() {
		try {
			SqlExecutor exe = new FreemarkerSqlExecutor(TestSupport.getHsqldbConnection());;

			String sql = ClassUtils.getResourcePath(this.getClass(), "test.sql.ftl");
			for (int i = 0; i < PCOUNT; i++) {
				executeSql(exe, sql);
			}

			long start = System.currentTimeMillis();
			for (int i = 0; i < ECOUNT; i++) {
				executeSql(exe, sql);
			}
			long end = System.currentTimeMillis();

			log.debug("FreemarkerSqlExecutor - elapsed - " + (end - start));
		}
		catch (Exception e) {
			log.error(e.getMessage(), e);
			fail(e.getMessage());
		}
	}

	/**
	 */
	public void testPrepare2() {
		BeanHandlerFactory.setInstance(new FastBeanHandlerFactory());
	}

	/**
	 * testSimple2
	 */
	public void testSimple2() {
		testSimple();
	}

	/**
	 * testExtend2
	 */
	public void testExtend2() {
		testExtend();
	}
	
	/**
	 * testJavascript2
	 */
	public void testJavascript2() {
		testJavascript();
	}
	
	/**
	 * testFreemarker2
	 */
	public void testFreemarker2() {
		testFreemarker();
	}
	
	/**
	 * testIbatis
	 */
	public void testIbatis() {
		InputStream is = null;
		try {
			TestSupport.getHsqldbConnection();
			
			is = this.getClass().getResourceAsStream("ibatis-sqlmap-config.xml");
			SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(is);
			
			String sql = "test.select";
			for (int i = 0; i < PCOUNT; i++) {
				executeSql(sqlMapClient, sql);
			}

			long start = System.currentTimeMillis();
			for (int i = 0; i < ECOUNT; i++) {
				executeSql(sqlMapClient, sql);
			}
			long end = System.currentTimeMillis();

			log.debug("Ibatis - elapsed - " + (end - start));
		}
		catch (Exception e) {
			log.error(e.getMessage(), e);
			fail(e.getMessage());
		}
		finally {
			IOUtils.closeQuietly(is);
		}
	}

}
