package eu.dnetlib.dlms.jdbc;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.dnetlib.dlms.jdbc.parser.DummyParserFactory;

/**
 * Test class for DOLPreparedStatement.
 * 
 * @author lexis
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TestDOLStatement {

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(TestDOLStatement.class);
	/** DataSource istance. */
	@Resource
	private DorotyDataSource dataSource;

	/** PreparedStatement instance under test. */
	private DOLStatement statement;

	/** Fake dolString with two parameters placeholders used in test methods. */
	private final String dolString = "Fake dolString ";

	/** Parser factory used by the engine to create parsers. */
	@Resource
	private DummyParserFactory parserFactory;

	/**
	 * Init this.statement with a statement that, when executed will not generate a Resultset.
	 * 
	 * @throws SQLException
	 *             getting connection from the datasorurce.
	 */
	@Before
	public void beforeMethod() throws SQLException {
		this.statement = new DOLStatement(this.dataSource.getConnection());
		this.statement.setResultSetFactory(this.dataSource.getResultSetFactory());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#close()}.
	 * 
	 * @throws SQLException
	 *             closing statement
	 */
	@Test
	@DirtiesContext
	public void testClose() throws SQLException {
		assertFalse(this.statement.isClosed());
		this.statement.close();
		assertTrue(this.statement.isClosed());
		this.statement = null;
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#execute(java.lang.String)}.
	 * 
	 * @throws SQLException
	 *             executing statement
	 */
	@Test
	@DirtiesContext
	public void testExecuteStringRS() throws SQLException {
		this.parserFactory.setResultSetExpected(true);
		this.dataSource.getDolEngineFactory().setParserFactory(this.parserFactory);
		assertTrue(this.statement.execute(this.dolString));
		DOLResultSet dolRs = this.statement.getResultSet();
		assertNotNull(dolRs);
		log.debug("Consuming elements of a DOLResultSet");
		while (dolRs.next()) {
			dolRs.getObject(1);
		}
		log.debug("DOLResultSet consumed");
		dolRs.close();
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#execute(java.lang.String)}.
	 * 
	 * @throws SQLException
	 *             executing statement
	 */
	@Test
	@DirtiesContext
	public void testExecuteStringNORS() throws SQLException {
		this.parserFactory.setResultSetExpected(false);
		this.dataSource.getDolEngineFactory().setParserFactory(this.parserFactory);
		assertFalse(this.statement.execute(this.dolString));
		assertNull(this.statement.getResultSet());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#executeQuery(java.lang.String)}.
	 * 
	 * @throws SQLException
	 *             executing statement
	 */
	@Test
	@DirtiesContext
	public void testExecuteQuery() throws SQLException {
		this.parserFactory.setResultSetExpected(true);
		this.dataSource.getDolEngineFactory().setParserFactory(this.parserFactory);
		ResultSet rs = this.statement.executeQuery(this.dolString);
		assertNotNull(rs);
		ResultSetMetaData rsMd = rs.getMetaData();
		int columnNum = rsMd.getColumnCount();
		while (rs.next()) {
			for (int i = 1; i <= columnNum; i++)
				log.info(rs.getObject(i));
		}
		rs.close();
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#executeQuery(java.lang.String)}. I expect an exception
	 * if I call executeQuery on a statement with a dolString that does not generate a ResultSet.
	 * 
	 * @throws SQLException
	 *             executing statement
	 */
	@Test(expected = SQLException.class)
	@DirtiesContext
	public void testExecuteQueryExce() throws SQLException {
		this.parserFactory.setResultSetExpected(false);
		this.dataSource.getDolEngineFactory().setParserFactory(this.parserFactory);
		this.statement.executeQuery(this.dolString);
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getConnection()}.
	 * 
	 * @throws SQLException
	 *             getting Connection from the DOLStatement instance
	 */
	@Test
	@DirtiesContext
	public void testGetConnection() throws SQLException {
		assertNotNull(this.statement.getConnection());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getFetchDirection()}.
	 * 
	 * @throws SQLException
	 *             getting fetch direction
	 */
	@Test
	public void testGetFetchDirection() throws SQLException {
		log.info("Fetch Direction is: " + this.statement.getFetchDirection());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getFetchSize()}.
	 * 
	 * @throws SQLException
	 *             getting fetch size
	 */
	@Test
	public void testGetFetchSize() throws SQLException {
		log.info("Fetch Size is: " + this.statement.getFetchSize());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getQueryTimeout()}.
	 * 
	 * @throws SQLException
	 *             getting query timeout
	 */
	@Test
	public void testGetQueryTimeout() throws SQLException {
		log.info("Query Timeout is: " + this.statement.getQueryTimeout());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getResultSet()}.
	 * 
	 * @throws SQLException
	 *             executing statement or getting the result set
	 */
	@Test
	@DirtiesContext
	public void testGetResultSet() throws SQLException {
		this.parserFactory.setResultSetExpected(true);
		this.dataSource.getDolEngineFactory().setParserFactory(this.parserFactory);
		assertTrue(this.statement.execute(this.dolString));
		ResultSet rs = this.statement.getResultSet();
		assertNotNull(rs);
		ResultSetMetaData rsMd = rs.getMetaData();
		int columnNum = rsMd.getColumnCount();
		while (rs.next()) {
			for (int i = 1; i <= columnNum; i++)
				log.info(rs.getObject(i));
		}
		rs.close();
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getResultSetConcurrency()}.
	 * 
	 * @throws SQLException
	 *             getting result set concurrency
	 */
	@Test
	@DirtiesContext
	public void testGetResultSetConcurrency() throws SQLException {
		log.info("Result Set concurrency: " + this.statement.getResultSetConcurrency());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getResultSetHoldability()}.
	 * 
	 * @throws SQLException
	 *             getting result set holdability
	 */
	@Test
	@DirtiesContext
	public void testGetResultSetHoldability() throws SQLException {
		log.info("Result Set holdability: " + this.statement.getResultSetHoldability());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.jdbc.DOLStatement#getResultSetType()}.
	 * 
	 * @throws SQLException
	 *             getting result set type
	 */
	@Test
	@DirtiesContext
	public void testGetResultSetType() throws SQLException {
		log.info("Result Set type: " + this.statement.getResultSetType());
	}
}
