/**
 * Copyright 2008-2009 DRIVER PROJECT (ICM UW)
 * Original author: Marek Horst
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.dnetlib.resultset.impl;

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

import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

import eu.dnetlib.resultset.impl.CreatePullRSType;
import eu.dnetlib.data.index.dmf.DMFHeaderFields;
import eu.dnetlib.resultset.impl.containers.IResultSetObjectContainer;
import eu.dnetlib.data.index.ws.yadda.impl.SearchModuleMock;
import eu.dnetlib.data.index.ws.dataprov.CreateSearchBulkDataDTO;
import eu.dnetlib.common.utils.EprUtils;
import eu.dnetlib.common.ws.dataprov.DataProviderException;
import eu.dnetlib.data.index.ws.dataprov.DataProviderSearchProperties;
import eu.dnetlib.data.index.ws.dataprov.SimpleIndexDataProvider;
import eu.dnetlib.data.index.utils.SpringUtils;
import pl.edu.icm.yadda.service.search.searching.ResultField;
import pl.edu.icm.yadda.service.search.searching.SearchResult;
import pl.edu.icm.yadda.service.search.searching.SearchResults;

/**
 * Test class for IndexResultSet.
 * All tests are compatibile with 1.1.0 index version.
 * 
 * @author mhorst
 * 
 */
public class IndexPullResultSetTest {
	
	public static final Logger log = Logger.getLogger(IndexPullResultSetTest.class);

	ApplicationContext ctx;

	IndexResultSet resultSet;

	SimpleIndexDataProvider dataProvider;

	SearchModuleMock searchModuleMock;
	
	int predefinedSearchResultSize = 10;
	
	String predefinedIdPrefix = "dummyId";

	@Before
	public void setUp() throws Exception {
		ctx = SpringUtils.getSpringContext(SpringUtils.DEFAULT_JUNIT_RESOURCE);
		resultSet = (IndexResultSet) ctx.getBean("InternalResultSet");
		dataProvider = (SimpleIndexDataProvider) ctx
				.getBean("SimpleIndexDataProvider");
		if (!(dataProvider.getSearchModuleFacade() instanceof SearchModuleMock)) {
//			injecting mock search module if not set
			log.info("injecting mock search module");
			SearchModuleMock searchModuleMock = (SearchModuleMock) ctx.getBean("searchModuleMock");
			dataProvider.setSearchModuleFacade(searchModuleMock);
		}
		searchModuleMock = (SearchModuleMock) dataProvider
				.getSearchModuleFacade();
		searchModuleMock.setDummySearchResults(prepareSearchResults(predefinedIdPrefix));
	}

	List<SearchResult> prepareSearchResults(String idPrefing) {
		List<SearchResult> searchResults = new ArrayList<SearchResult>();
		for (int i=0; i< predefinedSearchResultSize; i++) {
			SearchResult currentResult = new SearchResult(predefinedIdPrefix+i);
			List<ResultField> fields = new ArrayList<ResultField>();
			fields.add(new ResultField(DMFHeaderFields.DR_objectIdentifier,
					new String[] {predefinedIdPrefix+i}, null));
			currentResult.setFields(fields);
			searchResults.add(currentResult);
		}
		return searchResults;
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testCreatePullRSForUnsupportedDataProvider() {
		try {
			String dataProviderServiceAddress = "unsupportedDataProviderServiceAddress";
			String bdId = "dummyBdId";
			int initialPageSize = 50;
			int expiryTime = Integer.MAX_VALUE;
			resultSet.createPullRS(dataProviderServiceAddress, bdId,
					initialPageSize, expiryTime, null);
			fail("Exception should be thrown when creating pull RS "
					+ "for unsupported data provider!");
		} catch (IndexResultSetFaultMessage e) {
//			OK
		}
	}

	@Test
	public void testCreatePullRS() throws DataProviderException, 
		IndexResultSetFaultMessage {
		String rsId = null;
		try {
			// creating mock data inside data provider
			CreateSearchBulkDataDTO bulkDataDTO = new CreateSearchBulkDataDTO();
			bulkDataDTO.setIndexIds(new String[] {DataProviderSearchProperties.INDEX_IDS_ALL});
			SearchResults cache = new SearchResults();
			cache.setCount(predefinedSearchResultSize);
			bulkDataDTO.setCache(cache);
			String bdId = dataProvider.createBulkData(bulkDataDTO);
			String dataProviderServiceAddress = resultSet.getInternalDataProviderServiceAddress();
			int initialPageSize = 2;
			int expiryTime = Integer.MAX_VALUE;
			CreatePullRSType pullRSType = null;
			pullRSType = new CreatePullRSType();
			pullRSType.setTotal(30);
			pullRSType.setKeepAliveTime(50);
			
			W3CEndpointReference rsEpr = resultSet.createPullRS(dataProviderServiceAddress, bdId,
					initialPageSize, expiryTime, pullRSType);
			rsId = EprUtils.extractResultSetId(rsEpr.toString());
			assertNotNull(rsEpr);
			log.debug(rsEpr);
			
		} finally {
			if (rsId!=null)
				resultSet.deleteRS(rsId);
		}
	}

	@Test
	public void testGetNumberOfElements() throws IndexResultSetFaultMessage, DataProviderException {
			String rsId = null;
			try {
				CreateSearchBulkDataDTO bulkDataDTO = new CreateSearchBulkDataDTO();
				bulkDataDTO.setIndexIds(new String[] {DataProviderSearchProperties.INDEX_IDS_ALL});
				SearchResults cache = new SearchResults();
				cache.setCount(predefinedSearchResultSize);
				bulkDataDTO.setCache(cache);
				// creating mock data inside data provider
				String bdId = dataProvider.createBulkData(bulkDataDTO);
				String dataProviderServiceAddress = resultSet.getInternalDataProviderServiceAddress();
				int initialPageSize = 2;
				int expiryTime = Integer.MAX_VALUE;
				
				W3CEndpointReference rsEpr = resultSet.createPullRS(dataProviderServiceAddress, bdId,
						initialPageSize, expiryTime, null);
				rsId = EprUtils.extractResultSetId(rsEpr.toString());
				assertNotNull(rsEpr);
				int rsElementsCount = resultSet.getNumberOfElements(rsId);
				assertEquals(predefinedSearchResultSize, rsElementsCount);
			} finally {
				if (rsId!=null)
					resultSet.deleteRS(rsId);
			}
	}

	@Test
	public void testGetResult() throws IndexResultSetFaultMessage, DataProviderException {
		String rsId = null;
		try {
			CreateSearchBulkDataDTO bulkDataDTO = new CreateSearchBulkDataDTO();
			bulkDataDTO.setIndexIds(new String[] {DataProviderSearchProperties.INDEX_IDS_ALL});
			SearchResults cache = new SearchResults();
			cache.setCount(predefinedSearchResultSize);
			bulkDataDTO.setCache(cache);
			// creating mock data inside data provider
			String bdId = dataProvider.createBulkData(bulkDataDTO);
			// creating mock data inside data provider
			String dataProviderServiceAddress = resultSet.getInternalDataProviderServiceAddress();
			int initialPageSize = 2;
			int expiryTime = Integer.MAX_VALUE;
			
			W3CEndpointReference rsEpr = resultSet.createPullRS(dataProviderServiceAddress, bdId,
					initialPageSize, expiryTime, null);
			rsId = EprUtils.extractResultSetId(rsEpr.toString());
			assertNotNull(rsEpr);
			int rsElementsCount = resultSet.getNumberOfElements(rsId);
			assertEquals(predefinedSearchResultSize, rsElementsCount);
			
//			retrieving all data
			String[] result = resultSet.simpleGetResult(rsId, 1, rsElementsCount, null, null);
			assertNotNull(result);
			assertEquals(rsElementsCount, result.length);
			for (int i=0; i< predefinedSearchResultSize; i++) {
				assertNotNull(result[i]);
				assertTrue(result[i].equals(generateExpectedResult(predefinedIdPrefix+i)));
			}
			
//			retrieving cached data
			result = resultSet.simpleGetResult(rsId, 1, rsElementsCount, null, null);
			assertNotNull(result);
			assertEquals(rsElementsCount, result.length);
			for (int i=0; i< predefinedSearchResultSize; i++) {
				assertNotNull(result[i]);
				assertTrue(result[i].equals(generateExpectedResult(predefinedIdPrefix+i)));
			}
			
//			checking different ranges:
			int elementPos = 1;
			result = resultSet.simpleGetResult(rsId, elementPos, elementPos, null, null);
			assertNotNull(result);
			assertEquals(1,result.length);
			assertNotNull(result[0]);
			
			
			assertTrue(result[0].equals(generateExpectedResult((predefinedIdPrefix+(elementPos-1)))));
			elementPos = rsElementsCount;
			result = resultSet.simpleGetResult(rsId, elementPos, elementPos, null, null);
			assertNotNull(result);
			assertEquals(1,result.length);
			assertNotNull(result[0]);
			assertTrue(result[0].equals(generateExpectedResult(predefinedIdPrefix+(elementPos-1))));
			int start = 2;
			int end = 3;
			result = resultSet.simpleGetResult(rsId, start, end, null, null);
			assertNotNull(result);
			assertEquals((end-start+1),result.length);
			
			for (int i=0; i< end-start; i++) {
				assertNotNull(result[i]);
				assertTrue(result[i].equals(generateExpectedResult(predefinedIdPrefix+(start+i-1))));
			}
			
//			getting out of range results
			result = resultSet.simpleGetResult(rsId, -1, -2, null, null);
			assertNotNull(result);
			assertEquals(0,result.length);
			
			result = resultSet.simpleGetResult(rsId, -2, -1, null, null);
			assertNotNull(result);
			assertEquals(0,result.length);
			
			result = resultSet.simpleGetResult(rsId, Integer.MAX_VALUE-1, Integer.MAX_VALUE, null, null);
			assertNotNull(result);
			assertEquals(0,result.length);

			result = resultSet.simpleGetResult(rsId, 1, Integer.MAX_VALUE, null, null);
			assertNotNull(result);
			assertEquals(rsElementsCount,result.length);
			for (int i=0; i< predefinedSearchResultSize; i++) {
				assertNotNull(result[i]);
				assertTrue(result[i].equals(generateExpectedResult(predefinedIdPrefix+i)));
			}
			
		} finally {
			if (rsId!=null)
				resultSet.deleteRS(rsId);
		}
	}

	@Test
	public void testKeepAliveTimeSupport() throws Exception {
		String rsId = null;
		try {
			// creating mock data inside data provider
			CreateSearchBulkDataDTO bulkDataDTO = new CreateSearchBulkDataDTO();
			bulkDataDTO.setIndexIds(new String[] {DataProviderSearchProperties.INDEX_IDS_ALL});
			SearchResults cache = new SearchResults();
			cache.setCount(predefinedSearchResultSize);
			bulkDataDTO.setCache(cache);
			String bdId = dataProvider.createBulkData(bulkDataDTO);
			String dataProviderServiceAddress = resultSet.getInternalDataProviderServiceAddress();
			int initialPageSize = 2;
			int expiryTime = 1;
			
			CreatePullRSType createPullRS_type = null;
			createPullRS_type = new CreatePullRSType();
			int keepAliveTime = 2;
			createPullRS_type.setTotal(30);
			createPullRS_type.setKeepAliveTime(keepAliveTime);
			
			W3CEndpointReference rsEpr = resultSet.createPullRS(dataProviderServiceAddress, bdId,
					initialPageSize, expiryTime, createPullRS_type);
			rsId = EprUtils.extractResultSetId(rsEpr.toString());
			assertNotNull(rsEpr);
			
			IResultSetObjectContainer objContainer = resultSet.getRsObjectContainer();
			assertEquals(0,objContainer.cleanup());
			Thread.sleep(keepAliveTime * 1000);
			assertEquals(1,objContainer.cleanup());
			
		} finally {
			if (rsId!=null)
				resultSet.deleteRS(rsId);
		}
	}
	
	String generateExpectedResult(String sourceStr) {
		SearchResult searchResult = new SearchResult("someDocId",0);
		ResultField resultField = new ResultField("someName", 
				new String[] {sourceStr}, null);
		List<ResultField> resultFields = new ArrayList<ResultField>();
		resultFields.add(resultField);
		searchResult.setFields(resultFields);
		return SimpleIndexDataProvider.parseResultFieldWithRank(searchResult);
	}
}
