package eu.dnetlib.dlms.epub.queries;

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

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openrdf.model.Value;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.dnetlib.dlms.impl.hibobjects.RepositoryWrapper;
import eu.dnetlib.dlms.jdbc.DorotyDataSource;
import eu.dnetlib.dlms.jdbc.InformationObject;

/**
 * Example on using the Doroty JDBC interface with the enhanced publication data model.
 * 
 * @author alessia
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ComplexQueryTest {
	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(ComplexQueryTest.class);

	@Resource
	private DorotyDataSource dorotyDataSource;

	@Resource
	private RepositoryWrapper repositoryWrapper;

	//@After
	public void logTriples() throws RepositoryException {
		final RepositoryConnection c = this.repositoryWrapper.getConnection();
		final RepositoryResult<org.openrdf.model.Statement> res = c.getStatements(null, null, (Value) null, false);
		int count = 0;
		while (res.hasNext()) {
			log.debug(res.next());
			count++;
		}
		log.debug("Total number of triples: " + count);
		res.close();
		this.repositoryWrapper.closeConnection();
	}

	@Test
	public void testSelectSimpleAttr2() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		stm.execute("select ePrints/attribute::dc:title/attribute::ciao");
		final ResultSet rs = stm.getResultSet();
		while (rs.next()) {
			log.debug(rs.getObject(1));
		}
		rs.close();
		stm.close();
		con.close();
	}

	@Test
	public void testSelectSimpleAttr2_2() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		stm.execute("select ePrints/attribute::dc:title[attribute::ciao]");
		final ResultSet rs = stm.getResultSet();
		while (rs.next()) {
			log.debug(rs.getObject(1));
		}
		rs.close();
		stm.close();
		con.close();
	}

	/**
	 * target oriented with simple predicate. OK.
	 * 
	 * @throws SQLException
	 */
	@Test
	public void testSelectTargetPred() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs/hasEPrint[@dc:title]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	/**
	 * target oriented with binpredicate. OK.
	 * 
	 * @throws SQLException
	 */
	@Test
	public void testSelectTargetBinPred() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs/composedBy[@dc:title='theTitle']"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	//TODO: // unimplemented
	@Test
	@Ignore
	public void testSelectDoubleSlash() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs//composedBy"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectStarNavigation() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs[./*/reverse::aggregates]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectSourcePred1() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs/hasEPrint[./composedBy/@ciao = 1]/cites[./composedBy/@xxx = 2]/aggregates"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj.getId());
		}
		rs.close();
		con.close();
	}

	/**
	 * Select source with simple predicate. Expected sparql: <code>SELECT ?obj1 WHERE { 
	 * ?obj1 dorotyPrivate:belongsTo doroty:28 .
	 * ?obj1 doroty:composedBy ?obj2_1 .  
	 * ?obj2_1 doroty:dc_language ?obj2_2 . }</code>. OK.
	 * 
	 * @throws SQLException
	 */
	@Test
	public void testSelectSourcePred2() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs[./composedBy/@dc:language]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectSourceBinColl() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		//assertTrue(stm.execute("new ePrints({dc:subject=['subject', 'subject2']}); select ePrints[@dc:subject contains 'subject']"));
		assertTrue(stm.execute("new ePrints({dc:subject=['subject', 'subject2']}); select ePrints[attribute::dc:subject contains 'subject']"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectIDAttr2() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select AggregationObjects[./aggregates/@stuff contains ./cites/@puppa]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectIDAttr3() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("Object o = new ePrints({dc:title='title'}); new cites(o,o); select ePrints[@id = ./cites/@id]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	/**
	 * Select source with binpred where left and right are both xPath of one component (attribute selection). Expected
	 * sparql: <code>SELECT ?obj1 WHERE { 
	 * ?obj1 dorotyPrivate:belongsTo doroty:26 .
	 * ?obj1 doroty:composedBy ?obj2_1 . ?obj2_1  doroty:dc_language ?obj3_1 . 
	 * ?obj1 doroty:dc_language ?obj3_11 .
	 * FILTER(?obj3_1!=?obj3_11) }</code> OK.
	 * 
	 * @throws SQLException
	 */
	@Test
	public void testSelectSourceBinPred4() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs[./composedBy/@dc:language != @dc:language]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	/**
	 * Select source with binpred with left and right both xpaths. Left is just an attribute selection, right side is a
	 * composite xpath. Expected sparql : <code>SELECT ?obj1 WHERE { 
	 * ?obj1 dorotyPrivate:belongsTo doroty:26 .
	 * ?obj1  doroty:dc_language ?obj4_11.  
	 * ?obj4_1 doroty:dc_language ?obj4_111 .
	 * FILTER(?obj4_11<?obj4_111)
	 * ?obj1 doroty:composedBy ?obj4_1 .
	 * }</code>
	 * 
	 * OK.
	 * 
	 * @throws SQLException
	 */
	@Test
	public void testSelectSourceBinPred5() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs[@dc:language = ./composedBy/@dc:language]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testSelectSourceBinPred6() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePubs[./hasEPrint/@dc:country < ./(composedBy | hasEPrint)/@dc:language]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testMultipleRelPath1() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select(ePrints | hasEPrint | ePubs)/composedBy"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testMultipleRelPath2() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement stm = con.createStatement();
		assertTrue(stm.execute("select ePrints/(cites |hasEPrint)[@ciao]"));
		final ResultSet rs = stm.getResultSet();
		InformationObject obj = null;
		while (rs.next()) {
			obj = (InformationObject) rs.getObject(1);
			assertNotNull(obj);
			log.debug(obj);
		}
		rs.close();
		con.close();
	}

	@Test
	public void testAxisMulRel() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement st = con.createStatement();
		st.execute("select ePrints[relation::(cites|composedBy)/aggregates]/hasEPrint");
		final ResultSet rs = st.getResultSet();
		log.debug("Reading result");
		while (rs.next())
			log.debug(rs.getObject(1));
		rs.close();
		con.close();
	}

	@Test
	public void testAxisRelStar() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement st = con.createStatement();
		st.execute("select ePrints[relation::*/composedBy]/cites");
		final ResultSet rs = st.getResultSet();
		log.debug("Reading result");
		while (rs.next())
			log.debug(rs.getObject(1));
		rs.close();
		con.close();
	}

	@Test
	public void testAxisReverse() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement st = con.createStatement();
		st.execute("select ePrints[./reverse::hasEPrints/@foo != ./@pippo]");
		final ResultSet rs = st.getResultSet();
		log.debug("Reading result");
		while (rs.next())
			log.debug(rs.getObject(1));
		rs.close();
		con.close();
	}

	@Test
	public void testAxisReverseStar() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement st = con.createStatement();
		st.execute("select ePrints[./reverse::*/@foo != ./@pippo]");
		final ResultSet rs = st.getResultSet();
		log.debug("Reading result");
		while (rs.next())
			log.debug(rs.getObject(1));
		rs.close();
		con.close();
	}

	@Test
	public void testAxisMulReverse() throws SQLException {
		final Connection con = this.dorotyDataSource.getConnection();
		final Statement st = con.createStatement();
		st.execute("select ePrints[./reverse::(hasEPrints|composedBy)/@foo != ./@pippo]");
		final ResultSet rs = st.getResultSet();
		log.debug("Reading result");
		while (rs.next())
			log.debug(rs.getObject(1));
		rs.close();
		con.close();
	}

}
