package eu.dnetlib.dlms.benchmark;

import static org.junit.Assert.assertTrue;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.repository.RepositoryException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import eu.dnetlib.dlms.benchmark.driver.QueryExecutorInterface;
import eu.dnetlib.dlms.lowlevel.objects.structures.BasicValue;
import eu.dnetlib.dlms.lowlevel.objects.structures.DateBasicValue;
import eu.dnetlib.dlms.lowlevel.objects.structures.StringBasicValue;

/**
 * Test class for query executor. launching this testcase will execute the queries defined by the tested class. FIXME:
 * The way RecursiveLoader loads xmls chenged, but the queries in this class didn't so I do not expect results for many
 * of them. However it should still compile and run without exception.
 * 
 * @author lexis
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class QueryExecutorTest {

	/** Logger. */
	private static final Log log = LogFactory.getLog(QueryExecutorTest.class);
	/** Entity under test. */
	@Resource
	private QueryExecutorInterface queryExecutor;
	/**
	 * If true the test methods will call the count method that iterates over the QueryResult to count the structures.
	 * If false the count method will not be called. It is useful when testing on a great data set and we do not need to
	 * know how many elements are retrieved.
	 */
	public static final boolean COUNT_RESULTS = true;

	/**
	 * Counts the elements in the given DorotyQueryResult. The count is done by iterating over all elements in the
	 * DorotyQueryResult, so that at the end its content is consumed.
	 * 
	 * @param dqr
	 *            DorotyQueryResult whose elements are to count
	 * @return the number of elements that can be iterated with iterator.
	 */
	@Transactional
	private int count(final DorotyQueryResult dqr) {
		int size = 0;
		while (dqr.hasNext()) {
			size++;
			dqr.next();
		}
		return size;
	}

	/**
	 * Method that queries for a structure by its dmf identifier (field dr:objIdentifier).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 * @throws MalformedQueryException
	 *             if the query built is malformed
	 * @throws QueryEvaluationException
	 *             when error on executing query occurs
	 */
	@Test
	@Transactional
	public void queryByObjectId() throws RepositoryException, MalformedQueryException, QueryEvaluationException {
		log.info(this.getClass() + ".queryByObjectId");
		StringBasicValue objId = new StringBasicValue(
				"7-115066ea-49df-4f18-881f-9904dcd85675_UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=::oai:UJI.es:TDX-0721108-103420");
		// DorotyQueryResult matchingStructures =
		// this.queryExecutor.queryForStructuresWith("header.dri:objIdentifier",
		// objId);
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith("dr:objectIdentifier", objId);
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
			assertTrue(size == 1);
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by repository identifier (field dri:repositoryId).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 * @throws MalformedQueryException
	 *             if the query built is malformed
	 * @throws QueryEvaluationException
	 *             when error on executing query occurs
	 */
	@Test
	@Transactional
	public void queryByRepositoryId() throws RepositoryException, MalformedQueryException, QueryEvaluationException {
		log.info(this.getClass() + ".queryByRepositoryId");
		StringBasicValue repId = new StringBasicValue(
				"7-115066ea-49df-4f18-881f-9904dcd85675_UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=");

		// DorotyQueryResult matchingStructures =
		// this.queryExecutor.queryForStructuresWith("header.dr:repositoryId",
		// repId);
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith("dri:repositoryId", repId);
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by country (field dr:repositoryCountry).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 * @throws MalformedQueryException
	 *             if the query built is malformed
	 * @throws QueryEvaluationException
	 *             when error on executing query occurs
	 */
	@Test
	@Transactional
	public void queryByCountry() throws RepositoryException, MalformedQueryException, QueryEvaluationException {
		log.info(this.getClass() + ".queryByCountry");
		StringBasicValue country = new StringBasicValue("ES");
		// DorotyQueryResult matchingStructures = this.queryExecutor
		// .queryForStructuresWith("metadata.dr:repositoryCountry",
		// country);
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith("dr:repositoryCountry", country);
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by date of the collection (field dr:dateOfCollection).
	 */
	@Test
	@Transactional
	public void queryByDate() {
		log.info(this.getClass() + ".queryByDate");
		String datestring = "2008-08-29T20:50:40Z";
		DateBasicValue dateOfCollection = new DateBasicValue();
		dateOfCollection.setTheValueFromString(datestring);
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith("dr:dateOfCollection", dateOfCollection);
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by subject and date of the collection (fields dc:subject and
	 * dr:dateOfCollection).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 */
	@Test
	@Transactional
	public void queryBySubjectAndDate() throws RepositoryException {
		log.info(this.getClass() + ".queryBySubjectAndDate");
		StringBasicValue subject = new StringBasicValue("Departament de Ciències Experimentals");
		String datestring = "2008-08-29T20:50:40Z";
		DateBasicValue dateOfCollection = new DateBasicValue();
		dateOfCollection.setTheValueFromString(datestring);
		// DorotyQueryResult matchingStructures = this.queryExecutor
		// .queryForStructuresWith(new String[] {
		// "header.dr:dateOfCollection", "metadata.dc:subject" },
		// new StringBasicValue[] { dateOfCollection, subject });
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith(new String[] { "dr:dateOfCollection", "dc:subject" },
				new BasicValue[] { dateOfCollection, subject });
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by country and contributor (fields dr:repositoryCountry and dc:contributor).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 */
	@Test
	@Transactional
	public void queryByCountryAndContributor() throws RepositoryException {
		log.info(this.getClass() + ".queryByCountryAndContributor");
		StringBasicValue country = new StringBasicValue("ES");
		StringBasicValue contributor = new StringBasicValue("Marzal Felici, Javier");
		// DorotyQueryResult matchingStructures = this.queryExecutor
		// .queryForStructuresWith(new String[] {
		// "metadata.dr:repositoryCountry",
		// "metadata.dc:contributor" }, new StringBasicValue[] {
		// country, contributor });
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith(new String[] { "dr:repositoryCountry", "dc:contributor" },
				new StringBasicValue[] { country, contributor });
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

	/**
	 * Method that queries for structures by country and language (fields dr:repositoryCountry and dc:language).
	 * 
	 * @throws RepositoryException
	 *             accessing triplestore
	 */
	@Test
	@Transactional
	public void queryByCountryAndLanguage() throws RepositoryException {
		log.info(this.getClass() + ".queryByCountryAndLanguage");
		StringBasicValue country = new StringBasicValue("ES");
		StringBasicValue lang = new StringBasicValue("esl/spa");
		// DorotyQueryResult matchingStructures = this.queryExecutor
		// .queryForStructuresWith(
		// new String[] { "metadata.dr:repositoryCountry",
		// "metadata.dc:language" },
		// new StringBasicValue[] { country, lang });
		DorotyQueryResult matchingStructures = this.queryExecutor.queryForStructuresWith(new String[] { "dr:repositoryCountry", "dc:language" },
				new StringBasicValue[] { country, lang });
		if (COUNT_RESULTS) {
			long startTime = System.currentTimeMillis();
			int size = this.count(matchingStructures);
			long afterCount = System.currentTimeMillis();
			log.debug("iter (count elements):  " + (afterCount - startTime) + " ms");
			log.debug("Found " + size + " matching structures. ");
		}
		matchingStructures.close();
		log.info("--------------------------------------------------------------------");
	}

}
