package eu.dnetlib.dlms.impl.daos;

import static org.junit.Assert.assertEquals;
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.util.Arrays;

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.model.Value;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.repository.RepositoryException;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import eu.dnetlib.dlms.impl.hibobjects.RepositoryWrapper;
import eu.dnetlib.dlms.lowlevel.DorotyConstants;
import eu.dnetlib.dlms.lowlevel.objects.Atom;
import eu.dnetlib.dlms.lowlevel.objects.AtomDAO;
import eu.dnetlib.dlms.lowlevel.objects.Set;
import eu.dnetlib.dlms.lowlevel.objects.SetDAO;
import eu.dnetlib.dlms.lowlevel.types.AtomType;
import eu.dnetlib.dlms.lowlevel.types.AtomTypeDAO;
import eu.dnetlib.dlms.lowlevel.types.SetType;
import eu.dnetlib.dlms.lowlevel.types.SetTypeDAO;
import eu.dnetlib.dlms.union.objects.UnionSet;
import eu.dnetlib.dlms.union.objects.UnionSetDAO;
import eu.dnetlib.dlms.union.types.UnionSetType;
import eu.dnetlib.dlms.union.types.UnionSetTypeDAO;
import eu.dnetlib.dlms.union.types.UnionType;
import eu.dnetlib.dlms.union.types.UnionTypeDAO;

/**
 * @author lexis
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class UnionSetDAOImplTest {

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(UnionSetDAOImplTest.class);
	/** Entity under test. */
	@Resource
	private UnionSetDAO simpleUnionSetDAO;
	/** DAO for UnionType. */
	@Resource
	private UnionTypeDAO unionTypeDAO;
	@Resource
	private UnionSetTypeDAO unionSetTypeDAO;

	/** DAO for AtomTypes. */
	@Resource
	private AtomTypeDAO atomTypeDAO;
	/** AtomTypes. */
	@Resource
	private AtomType atomType1;

	/** DAO for SetType. */
	@Resource
	private SetTypeDAO setTypeDAO;
	/**
	 * SetTypes for Sets that contain atoms. setType1 has contentType atomType1; setType2 has contentType atomType2,
	 * setType3 has contentType atomType3, setType4 has contentType atomType1.
	 */
	@Resource
	private SetType setType1;

	/** DAO for Set. */
	@Resource
	private SetDAO extSetDAO;
	/**
	 * Sets to contain atoms. atomSet1 has setType setType1; atomSet2 has setType setType2; atomSet3 has contentType
	 * setType3; atomSet4 has contentType setType4 and is compatible with atomSet1.
	 */
	@Resource
	private Set atomSet1;
	@Resource
	private AtomDAO atomDAO;
	@Resource
	private Atom atom1, atom2;

	@Resource
	private RepositoryWrapper repositoryWrapper;

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.GeneralHibernateDAO#create()}.
	 */
	@Test
	public void testCreate() {
		assertNotNull(this.simpleUnionSetDAO.create());
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.daos.UnionSetDAOImpl#save(eu.dnetlib.dlms.union.objects.UnionSet)}.
	 */
	@Test
	@DirtiesContext
	public void testSaveUnionSet() {
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForSave");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName("MyUnion");
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		this.extSetDAO.save(this.atomSet1);
		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);

		//let's check if we can get it back:
		final UnionSet persisted = this.simpleUnionSetDAO.getByID(uSet.getId());
		assertNotNull(persisted);
		assertEquals(persisted, uSet);
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.daos.UnionSetDAOImpl#delete(eu.dnetlib.dlms.union.objects.UnionSet)}.
	 * Must check also there are no more triples with the union set name as context.
	 * 
	 * @throws RepositoryException
	 */
	@Test
	@DirtiesContext
	public void testDeleteUnionSet() throws RepositoryException {
		//SAVE---
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForDelete");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName("MyUnion");
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		this.extSetDAO.save(this.atomSet1);
		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);

		//let's check if we can get it back:
		UnionSet persisted = this.simpleUnionSetDAO.getByID(uSet.getId());
		assertNotNull(persisted);
		assertEquals(persisted, uSet);
		//-----
		//DELETE----
		this.simpleUnionSetDAO.delete(uSet);
		persisted = this.simpleUnionSetDAO.getByID(uSet.getId());
		assertNull(persisted);
		assertFalse(this.repositoryWrapper.getConnection().getStatements(null, null, (Value) null, false,
				new URIImpl(DorotyConstants.DOROTYNAMESPACE + uSet.getId())).hasNext());
		this.repositoryWrapper.closeConnection();
		//----
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.daos.UnionSetDAOImpl#getUnionSetByName(java.lang.String)} .
	 */
	@Test
	@DirtiesContext
	public void testGetUnionSetByName() {
		//SAVE---
		final String unionName = "MyUnionToGet";
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForGet");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName(unionName);
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		this.extSetDAO.save(this.atomSet1);
		//PUT OBJECT IN ATOMSET1
		this.extSetDAO.save(this.atomSet1);
		this.atomDAO.save(this.atom1);
		this.extSetDAO.addToSet(this.atomSet1, this.atom1);
		this.atomDAO.save(this.atom1);
		this.extSetDAO.save(this.atomSet1);

		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);
		this.atomDAO.save(this.atom2);
		this.extSetDAO.addToSet(this.atomSet1, this.atom2);
		this.atomDAO.save(this.atom2);
		this.extSetDAO.save(this.atomSet1);
		//this.unionSetDAO.save(uSet);
		//----
		//let's check if we can get it back:
		final UnionSet persisted = this.simpleUnionSetDAO.getUnionSetByName(unionName);
		assertNotNull(persisted);
		assertEquals(persisted, uSet);
		this.simpleUnionSetDAO.load(persisted);
		assertTrue(this.atom1.isInSet(uSet));
		assertTrue(this.atom1.isInSet(persisted));
		assertTrue(this.atom2.isInSet(persisted));
		assertTrue(this.atom2.isInSet(uSet));

		log.debug(Arrays.toString(persisted.getObjectsInUnion().toArray(new Atom[0])));
		//-----
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.daos.UnionSetDAOImpl#load(eu.dnetlib.dlms.union.objects.UnionSet)}.
	 */
	@Test
	@DirtiesContext
	public void testLoadUnionSet() {
		//SAVE---
		final String unionName = "MyUnionToGet";
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForGet");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName(unionName);
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		//PUT OBJECT IN ATOMSET1
		this.extSetDAO.save(this.atomSet1);
		this.atomDAO.save(this.atom1);
		this.extSetDAO.addToSet(this.atomSet1, this.atom1);
		this.atomDAO.save(this.atom1);
		this.extSetDAO.save(this.atomSet1);
		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);
		//----
		//LOAD ---
		assertNull(uSet.getObjectsInUnion());
		this.simpleUnionSetDAO.load(uSet);
		assertTrue(uSet.getObjects().isEmpty());
		assertNotNull(uSet.getObjectsInUnion());
		assertFalse(uSet.getObjectsInUnion().isEmpty());
		final int inSet = uSet.getObjectsInUnion().size();
		assertEquals(uSet.count(), inSet);
		//----
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.GeneralHibernateDAO#list()}.
	 */
	@Test
	@DirtiesContext
	@Transactional
	public void testList() {
		assertTrue(this.simpleUnionSetDAO.list().size() == 1);
		//SAVE---
		final String unionName = "MyUnionToGet";
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForList");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName(unionName);
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		this.extSetDAO.save(this.atomSet1);
		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);
		assertTrue(this.simpleUnionSetDAO.list().size() == 2);
	}

	/**
	 * Test method for {@link eu.dnetlib.dlms.impl.GeneralHibernateDAO#list()}.
	 */
	@Test
	@DirtiesContext
	public void testGetUnionSetsIncluding() {
		final String unionName = "MyUnionSet";
		this.atomTypeDAO.save(this.atomType1);
		final UnionType ut = this.unionTypeDAO.create();
		ut.addType(this.atomType1);
		ut.setName("UnionTypeForList");
		this.unionTypeDAO.save(ut);

		this.setTypeDAO.save(this.setType1);
		final UnionSetType ust = this.unionSetTypeDAO.create(ut);
		ust.setName(unionName);
		ust.addSetType(this.setType1);
		this.unionSetTypeDAO.save(ust);

		this.extSetDAO.save(this.atomSet1);
		final UnionSet uSet = this.simpleUnionSetDAO.create();
		uSet.setObjectType(ust);
		uSet.addSet(this.atomSet1);
		this.simpleUnionSetDAO.save(uSet);

		//now let's see where atomSet1 is included
		final java.util.Collection<UnionSet> includedIn = this.simpleUnionSetDAO.getUnionSetsIncluding(this.atomSet1);
		assertTrue(includedIn.size() == 1);
		log.debug(Arrays.toString(includedIn.toArray(new UnionSet[1])));
	}
}
