/**
 * Copyright 2008-2009 DRIVER PROJECT (Bielefeld University)
 * Original author: Marek Imialek <marek.imialek at uni-bielefeld.de>
 *
 * 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.data.index;

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

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.junit.Before;
import org.junit.Test;


import eu.dnetlib.data.sts.das.DataAccessServiceException;
import eu.dnetlib.data.sts.das.IDataAccessService;
import eu.dnetlib.data.sts.ds.DepotServiceException;
import eu.dnetlib.data.sts.ds.IDepotService;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.enabling.tools.JaxwsServiceResolverImpl;
import eu.dnetlib.enabling.tools.ServiceResolver;
import eu.dnetlib.resultset.impl.IndexResultSetFaultMessage;


/**
 * Simple Index Client Test.
 * 
 * @author <a href="mailto:marek.imialek at uni-bielefeld.de">Marek Imialek</a>
 * @version 0.0.1
 */
public class TestFullTextIndexing {

	/** The Constant ixAddress1. */
	private final static String ixAddress1 = "http://localhost:8380/dnet-index/services/IndexService";
	
	/** The Constant rsAddress. */
	private final static String rsAddress = "http://localhost:8090/app/services/resultSet";

	/** The base64code. */
	public static String base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +

		"abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/";

	/** The split lines at. */
	public static int splitLinesAt = 76;
	
	/** The ix. */
	private IIndexService ix;

	/** The result set service. */
	private ResultSetService resultSetService;
	
	/** The ix id. */
	private String ixId;
	
	private IDepotService dS;
	
	private IDataAccessService dAS;

	/**
	 * Sets the up.
	 * 
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setServiceClass(IIndexService.class);
		factory.setAddress(ixAddress1);
		ix = (IIndexService) factory.create();
		assertNotNull(ix.identify());
		System.out.println("Index Service Identify: " + ix.identify() );

		JaxWsProxyFactoryBean factory0 = new JaxWsProxyFactoryBean();
		factory0.setServiceClass(ResultSetService.class);
		factory0.setAddress(rsAddress);
		resultSetService = (ResultSetService) factory0.create();
		assertNotNull(resultSetService.identify());
		System.out.println("Identify Push RS: " + resultSetService.identify() );
		
		JaxWsProxyFactoryBean factory1 = new JaxWsProxyFactoryBean();
		factory1.setServiceClass(IDepotService.class);
		factory1.setAddress(
				//"http://localhost:8980/dnet-sts-ds/services/DepotService");
				"http://129.70.40.102:8380/dnet-sts-ds/services/DepotService");
		dS = (IDepotService) factory1.create();
		assertNotNull(dS.identify());
		System.out.println("Identify DService: " + dS.identify() );
		
		JaxWsProxyFactoryBean factory2 = new JaxWsProxyFactoryBean();
		factory2.setServiceClass(IDataAccessService.class);
		factory2.setAddress(
				//"http://localhost:8780/dnet-sts-das/services/DataAccessService");
				"http://129.70.40.102:8480/dnet-sts-das/services/DataAccessService");
		dAS = (IDataAccessService) factory2.create();
		assertNotNull(dAS.identify());
		System.out.println("Identify DA Service: " + dAS.identify() );
	}

	@Test
	public void testFeedingProcess() throws ResultSetException, 
		IndexResultSetFaultMessage, IndexServiceException {
		
		System.out.println("CREATING INDEX");
		createIndex();
		System.out.println("FEEDING INDEX");
		feedIndex();
		System.out.println("SEARCHING INDEX");
		indexLookUp();
		System.out.println("DELETING INDEX");
		deleteIndex();
	}
	
	/**
	 * Creates the index.
	 * 
	 * @throws IndexServiceException the index service exception
	 */
	public void createIndex() throws IndexServiceException{

		String format = "DMF";
		String layout = "index";
		String interpretationName = "";
		ixId = ix.createIndex(format, layout, interpretationName);
		//ixId = "89c2a3a3-0a96-49bd-ab5c-cc69364d1c7d_SW5kZXhEU1Jlc291cmNlcy9JbmRleERTUmVzb3VyY2VUeXBl";
		System.out.println("index identifier: " + ixId);
	}
//<fulltext>http://blogs.talis.com/nodalities/wp-content/plugins/podpress/readme.txt</fulltext>
//<FIELD indexable="true" name="fturl" result="true" fulltext="true" stat="false" xpath="//dc:fturl"/>
	
	/**
	 * Feed index.
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public void feedIndex() throws IndexServiceException, ResultSetException {

		W3CEndpointReference rsEPR = resultSetService.createPushRS(70, 70);
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		final ResultSetService resultSetService = serviceResolver.getService(
				ResultSetService.class, rsEPR);
		final String rsId = serviceResolver.getResourceIdentifier(rsEPR);
		resultSetService.populateRS(rsId, objectsForStoring());
		resultSetService.closeRS(rsId);

		System.out.println("Number of objects stored in RS: "+ 
				resultSetService.getNumberOfElements(rsId));

		String feedingType = "REFRESH";

		String resultSetEPRBase64Enc = encode(rsEPR.toString());

		assertTrue(ix.feedIndex(ixId, feedingType, resultSetEPRBase64Enc,true));
		
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * Test index look up.
	 * 
	 * @throws ResultSetException the result set exception
	 * @throws IndexResultSetFaultMessage the index result set fault message
	 * @throws IndexServiceException the index service exception
	 */
	
	public void indexLookUp() throws ResultSetException,
	IndexResultSetFaultMessage, IndexServiceException {

		W3CEndpointReference epr = ix.indexLookup(ixId, "fturl=WordPress",
				"DMF", "index");
		assertNotNull(epr);
		System.out.println("LookUp EPR: " + epr);

		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		final ResultSetService resultSetService = serviceResolver.getService(
				ResultSetService.class, epr);
		final String rsId = serviceResolver.getResourceIdentifier(epr);
		int recNumber = resultSetService.getNumberOfElements(rsId);
		System.out.println("LookUp records number: " + recNumber);
		if (recNumber != 0) {
			List<String> results = resultSetService.getResult(rsId, 1,
					recNumber, "waiting");
			System.out.println(results.get(0));
		}

	}

	/**
	 * Delete index.
	 * 
	 * @throws IndexServiceException the index service exception
	 */
	public void deleteIndex() throws IndexServiceException{
		ix.deleteIndex(ixId);
	}
	
	@Test
	public void storeObjects() throws DepotServiceException, DataAccessServiceException {
		System.out.println("\nCREATE STORE!");
		List<String> predefinedObjectTypes = new ArrayList<String>();
		predefinedObjectTypes.add("text/html");
		predefinedObjectTypes.add("text/plain");
		long maxSizeStDS = 10;
		String storeIdentifer = dS.createStore(predefinedObjectTypes,maxSizeStDS);
		assertNotNull(storeIdentifer);
		
		System.out.println("STORING OBJECTS IN STORE!");
		List<String> objectsForStoring = new ArrayList<String>();
		String objId = "fulltextTestIdentifier"; 
		objectsForStoring.add(
				"<storeRecord><storeRecordIdentifier>"+objId+"</storeRecordIdentifier>" +
				"<storeObject datatype=\"URI\">http://wordpress.org/extend/plugins/about/readme.txt</storeObject>" +
				"</storeRecord>");
		
		dS.storeObjects(storeIdentifer, objectsForStoring, "REFRESH");
		
		System.out.println("Juppi!!! I am out of this job! :) \n");
		System.out.println("Sleeping to give some time for finishing junit job!");
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("WakeUp!");
		
		String objURL = dAS.getObjectUrl(objId, storeIdentifer);
		System.out.println("GOT OBJECT URL: " + objURL);
		
	}
	
	/**
	 * Objects for storing.
	 * 
	 * @return the list< string>
	 */
	private List<String> objectsForStoring() {
		List<String> list = new ArrayList<String>();
		String fileCont = null;
		try {
			fileCont = readFile("eu/dnetlib/data/index/ws/harv/indexRecord.xml");
		} catch (ResourceNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		list.add(fileCont);

		return list;
	}

	/**
	 * Read file.
	 * 
	 * @param sourceFilePath the source file path
	 * 
	 * @return the string
	 * 
	 * @throws ResourceNotFoundException the resource not found exception
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	private String readFile(String sourceFilePath) throws ResourceNotFoundException, IOException{

		ClasspathResourceLoader loader = new ClasspathResourceLoader();
		String sourceContent = read(loader.getResourceStream(sourceFilePath));
		//System.out.println(sourceContent);
		return sourceContent;
	}

	/**
	 * Read.
	 * 
	 * @param in the in
	 * 
	 * @return the string
	 * 
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	private static String read(InputStream in) throws IOException {
		StringBuffer out = new StringBuffer();
		byte[] b = new byte[4096];
		for (int n; (n = in.read(b)) != -1;) {
			out.append(new String(b, 0, n));
		}
		return out.toString();
	}

	/**
	 * Zero pad.
	 * 
	 * @param length the length
	 * @param bytes the bytes
	 * 
	 * @return the byte[]
	 */
	private static byte[] zeroPad(int length, byte[] bytes) {
		byte[] padded = new byte[length]; // initialized to zero by JVM
		System.arraycopy(bytes, 0, padded, 0, bytes.length);
		return padded;
	}

	/**
	 * Encode.
	 * 
	 * @param string the string
	 * 
	 * @return the string
	 */
	private static String encode(String string) {

		String encoded = "";
		byte[] stringArray;
		try {
			stringArray = string.getBytes("UTF-8");  // use appropriate encoding string!
		} catch (Exception ignored) {
			stringArray = string.getBytes();  // use locale default rather than croak
		}
		// determine how many padding bytes to add to the output
		int paddingCount = (3 - (stringArray.length % 3)) % 3;
		// add any necessary padding to the input
		stringArray = zeroPad(stringArray.length + paddingCount, stringArray);
		// process 3 bytes at a time, churning out 4 output bytes
		// worry about CRLF insertions later
		for (int i = 0; i < stringArray.length; i += 3) {
			int j = (stringArray[i] << 16) + (stringArray[i + 1] << 8) + 
			stringArray[i + 2];
			encoded = encoded + base64code.charAt((j >> 18) & 0x3f) +
			base64code.charAt((j >> 12) & 0x3f) +
			base64code.charAt((j >> 6) & 0x3f) +
			base64code.charAt(j & 0x3f);
		}
		// replace encoded padding nulls with "="
		return splitLines(encoded.substring(0, encoded.length() -
				paddingCount) + "==".substring(0, paddingCount));

	}
	
	/**
	 * Split lines.
	 * 
	 * @param string the string
	 * 
	 * @return the string
	 */
	private static String splitLines(String string) {

		String lines = "";
		for (int i = 0; i < string.length(); i += splitLinesAt) {

			lines += string.substring(i, Math.min(string.length(), i + splitLinesAt));
			lines += "\r\n";

		}
		return lines;

	}

}
