package eu.dnetlib.data.utility.objectpackaging;

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

import javax.annotation.Resource;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import static org.junit.Assert.*; // NOPMD

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.dnetlib.data.utility.objectpackaging.rmi.ObjectPackagingException;
import eu.dnetlib.data.utility.objectpackaging.rmi.ObjectPackagingService;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.enabling.tools.ServiceResolver;
import eu.dnetlib.test.AbstractIntegrationContainerTest;

/**
 * Test for ObjectPackagingService.
 *
 * @author michele
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
public class ObjectPackagingServiceUnpackTest extends AbstractIntegrationContainerTest {
	/**
	 * retrieval page size.
	 */
	private static final int PAGE_SIZE = 10;

	/**
	 * Service under test.
	 */
	private ObjectPackagingService objectPackagingService;

	/**
	 * result set service locator.
	 */
	@Resource(name = "resultSetLocator")
	private transient ServiceLocator<ResultSetService> resultSetLocator;

	/**
	 * Object Packaging service locator.
	 */
	@Resource(name = "objectPackagingLocator")
	private transient ServiceLocator<ObjectPackagingService> objectPackagingLocator;

	/**
	 * service resolver.
	 */
	@Resource
	private transient ServiceResolver serviceResolver;

	/**
	 * prepare for testing.
	 *
	 * @throws Exception
	 */
	@Before
	public void setUp() {
		objectPackagingService = objectPackagingLocator.getService();
	}


	/**
	 * Test with input list = null.
	 *
	 * @throws ObjectPackagingException
	 *             expected
	 * @throws ResultSetException
	 *             not expected
	 */
	@Test(expected = ObjectPackagingException.class)
	public void testObjectPackagingNull() throws ObjectPackagingException, ResultSetException {
		objectPackagingService.splitPackages(null);
	}
	/**
	 * Test simple
	 *
	 * @throws ObjectPackagingException
	 *             not expected
	 * @throws ResultSetException
	 *             not expected
	 * @throws DocumentException
	 *             not expected
	 */
	@Test(expected = Exception.class)
	public void testObjectPackagingSplit_error() throws ObjectPackagingException, ResultSetException, DocumentException {
		final List<String> list = new ArrayList<String>();
		
		list.add("<objectRecord><elem>a</elem><elem>b</elem><elem>c</elem></objectRecord>");
		list.add("<error><elem>a</elem><elem>b</elem><elem>c</elem></error>");
		
		W3CEndpointReference epr = getEPR(list);

		execute(epr);
	}
	
	
	/**
	 * Test simple
	 *
	 * @throws ObjectPackagingException
	 *             not expected
	 * @throws ResultSetException
	 *             not expected
	 * @throws DocumentException
	 *             not expected
	 */
	@Test
	public void testObjectPackagingSplit() throws ObjectPackagingException, ResultSetException, DocumentException {
		
		final int size = 56;

		final List<String> list = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			list.add("<objectRecord><elem><id value='" + i + "' /></elem></objectRecord>");
		}
		W3CEndpointReference epr = getEPR(list);

		final List<String> response = execute(epr);
		assertEquals(size, response.size());

		final SAXReader reader = new SAXReader();
		for (int i = 0; i < size; i++) {
			final Document doc = reader.read(new StringReader(response.get(i)));
			assertEquals(i + "", doc.valueOf("/elem/id/@value"));
		}
	}

	/**
	 * Test simple
	 *
	 * @throws ObjectPackagingException
	 *             not expected
	 * @throws ResultSetException
	 *             not expected
	 * @throws DocumentException
	 *             not expected
	 */
	@Test
	public void testObjectPackagingSplit_advanced() throws ObjectPackagingException, ResultSetException, DocumentException {
		
		final int size = 10;

		final List<String> list = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			list.add("<objectRecord><elem>a</elem><elem>b</elem><elem>c</elem></objectRecord>");
		}
		W3CEndpointReference epr = getEPR(list);

		final List<String> response = execute(epr);
		assertEquals(size * 3, response.size());

		final SAXReader reader = new SAXReader();
		for (int i = 0; i < size; i++) {
			final Document doc = reader.read(new StringReader(response.get(i)));
			String c = doc.valueOf("/elem").trim();
			assertTrue(c.equals("a") || c.equals("b")  || c.equals("c"));
		}
	}
	
	
	/**
	 * Test repeated
	 *
	 * @throws ObjectPackagingException
	 *             not expected
	 * @throws ResultSetException
	 *             not expected
	 * @throws DocumentException
	 *             not expected
	 */
	@Test
	public void testObjectPackagingSplit_repeated() throws ObjectPackagingException, ResultSetException, DocumentException {
		
		final int size = 100;

		final List<String> list = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			list.add("<objectRecord><elem>a</elem><elem>b</elem><elem>c</elem></objectRecord>");
		}
		W3CEndpointReference epr = getEPR(list);

		final List<String> response1 = execute(epr);
		final List<String> response2 = execute(epr);
		final List<String> response3 = execute(epr);
		
		assertEquals(size * 3, response1.size());
		assertEquals(size * 3, response2.size());
		assertEquals(size * 3, response3.size());
		
		for (int i = 0; i < response1.size(); i++) {
			assertNotNull(response1.get(i));
			assertEquals(response1.get(i),response2.get(i));
			assertEquals(response1.get(i),response3.get(i));
		}
	}
	
	
	/**
	 * execute the object packaging service invocation.
	 *
	 * @param eprs
	 *            list of eprs
	 * @param xpath
	 *            xpath
	 * @return list of items
	 * @throws ObjectPackagingException
	 *             shouldn't happen
	 * @throws ResultSetException
	 *             shouldn't happen
	 */
	private List<String> execute(final W3CEndpointReference eprIn) throws ObjectPackagingException, ResultSetException {
		final W3CEndpointReference epr = objectPackagingService.splitPackages(eprIn);

		final ResultSetService resultSet = serviceResolver.getService(ResultSetService.class, epr);
		final String rsId = serviceResolver.getResourceIdentifier(epr);

		final List<String> obtained = new ArrayList<String>();

		int total = 0;
		boolean allDone = false;

		while (true) {
			total = resultSet.getNumberOfElements(rsId);
			allDone = resultSet.getRSStatus(rsId).equals("closed");

			if ((obtained.size() < total) && (allDone)) {
				obtained.addAll(resultSet.getResult(rsId, obtained.size() + 1, total, "WAITING"));
				break;
			} else if (allDone) {
				break;
			} else {
				obtained.addAll(resultSet.getResult(rsId, obtained.size() + 1, obtained.size() + PAGE_SIZE, "WAITING"));
			}
		}
		return obtained;
	}


	/**
	 * get the epr for a push resultset containing 'elements'.
	 *
	 * @param elements
	 *            elements
	 * @return EPR to a resultset
	 * @throws ResultSetException
	 *             shouldn't happen
	 */
	private W3CEndpointReference getEPR(final List<String> elements) throws ResultSetException {
		final W3CEndpointReference epr = resultSetLocator.getService().createPushRS(0, 0);
		final ResultSetService resultSet = serviceResolver.getService(ResultSetService.class, epr);
		final String rsId = serviceResolver.getResourceIdentifier(epr);
		resultSet.populateRS(rsId, elements);
		resultSet.closeRS(rsId);

		assertEquals(elements.size(), resultSet.getNumberOfElements(rsId));

		return epr;
	}

	
	
}
