/**
 * 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.contract.index;

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

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.log4j.Logger;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import eu.dnetlib.data.index.IIndexService;
import eu.dnetlib.data.index.IndexServiceException;
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;

/**
 * The IndexContractsHelper class.
 * 
 * @author <a href="mailto:marek.imialek at uni-bielefeld.de">Marek Imialek</a>
 */
public class IndexContractsHelper {
	
	/** The Constant log. */
	protected static final Logger log = Logger.getLogger(IndexContractsHelper.class);
	
	/** The index service facade. */
	private IIndexService indexServiceFacade;
	
	/** Result Set service location. */
	private String resultSetLocation;

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

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

	/** The split lines at. */
	public static int splitLinesAt = 76;
	
	
	/**
	 * Delete index.
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 */
	public boolean deleteIndex() throws IndexServiceException{
		String indexId = proceedCreateIndex() ;
		boolean response = indexServiceFacade.deleteIndex(indexId);
		return response;
	}
	
	/**
	 * Feed index.
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean feedIndex() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex() ;
		boolean response = proceedFeedIndex(indexId);
		indexServiceFacade.deleteIndex(indexId);
		return response; 	
	}
	
	/**
	 * Index lookup.
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean indexLookup() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex() ;
		boolean response = proceedFeedIndex(indexId);
		
		if (!response)
			return false;
		
		boolean lookupResp = proccedIndexLookUp(indexId);
		
		indexServiceFacade.deleteIndex(indexId);	
		
		return lookupResp;
	}
	
	/**
	 * Procced index look up.
	 * 
	 * @param indexId the index id
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean proccedIndexLookUp(String indexId) throws IndexServiceException, ResultSetException {

		W3CEndpointReference epr = indexServiceFacade.indexLookup(indexId, "creator=Dante",
				"DMF", "index");
		if (epr == null) {
			log.debug("indexLookUp EPR is null!");
			return false;
		}	
		
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		if (serviceResolver.getResourceIdentifier(epr) == null) {
			log.debug("Could not extract resultset id!");
			return false;
		} else
			return true;
	}
	
	/**
	 * Gets the browsing statistics.
	 * 
	 * @return the browsing statistics
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean getBrowsingStatistics() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex() ;
		boolean response = proceedFeedIndex(indexId);
		
		if (!response)
			return false;
		
		boolean lookupResp = proccedBrowsingStatistics(indexId);
		
		indexServiceFacade.deleteIndex(indexId);	
		
		return lookupResp;
	}
	
	/**
	 * Procced browsing statistics.
	 * 
	 * @param indexId the index id
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean proccedBrowsingStatistics(String indexId) throws IndexServiceException, ResultSetException {

		W3CEndpointReference epr = indexServiceFacade.getBrowsingStatistics("creator=Dante", indexId,
				"DMF", "index");
		if (epr == null) {
			log.debug("getBrowsingStatistics EPR is null!");
			return false;
		}	
		
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		if (serviceResolver.getResourceIdentifier(epr) == null) {
			log.debug("Could not extract resultset id!");
			return false;
		} else
			return true;
	}
	
	/**
	 * Gets the index statistics.
	 * 
	 * @return the index statistics
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public String getIndexStatistics() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex() ;
		String stat = indexServiceFacade.getIndexStatistics(indexId);
		indexServiceFacade.deleteIndex(indexId);
		return stat;
	}
	
	/**
	 * Gets the list of indices.
	 * 
	 * @return the list of indices
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public int getListOfIndices() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex();
		int length = indexServiceFacade.getListOfIndices().length;
		indexServiceFacade.deleteIndex(indexId);
		return length;
	}
	
	/**
	 * Delete records.
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean deleteRecords() throws IndexServiceException, ResultSetException {
		String indexId = proceedCreateIndex() ;
		boolean response = proceedFeedIndex(indexId);
		
		if (!response)
			return response;
		
		String recordId= "69-372b3e81-a1a4-4ae9-acc5-85f5c9154936_" +
			"UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVw" +
			"b3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=::oa" +
			"i:elib.suub.uni-bremen.de:DISS/00000910";
		
		Collection<String> coll =  new ArrayList<String>();;
		coll.add(recordId);
		indexServiceFacade.deleteRecords(indexId, coll);
		indexServiceFacade.deleteIndex(indexId);	

		return true;
	}
	
	/**
	 * Proceed create index.
	 * 
	 * @return the string
	 * 
	 * @throws IndexServiceException the index service exception
	 */
	public String proceedCreateIndex() throws IndexServiceException {
		String interpretationName = "";
		String layout = "index";
		String format = "DMF";
		return indexServiceFacade.createIndex(format, layout, interpretationName);
	}
	
	/**
	 * Proceed feed index.
	 * 
	 * @param indexId the index id
	 * 
	 * @return true, if successful
	 * 
	 * @throws IndexServiceException the index service exception
	 * @throws ResultSetException the result set exception
	 */
	public boolean proceedFeedIndex(String indexId) throws IndexServiceException, ResultSetException{
		
		JaxWsProxyFactoryBean factory0 = new JaxWsProxyFactoryBean();
		factory0.setServiceClass(ResultSetService.class);
		factory0.setAddress(resultSetLocation);
		ResultSetService resultSetService1 = (ResultSetService) factory0.create();
		
		W3CEndpointReference rsEPR = resultSetService1.createPushRS(70, 70);
		if (rsEPR == null) {
			log.debug("Could not create push resultset at:"+ resultSetLocation);
			return false;
		}
		
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		final String rsId = serviceResolver.getResourceIdentifier(rsEPR);
		resultSetService1.populateRS(rsId, objectsForStoring());
		resultSetService1.closeRS(rsId);

		boolean response = indexServiceFacade.feedIndex(indexId, "REFRESH", encode(rsEPR.toString()));
		
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		return response;
	}
	
	/**
	 * 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/contract/index/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.
	 */
	public static String readFile(String sourceFilePath) throws ResourceNotFoundException, IOException{

		ClasspathResourceLoader loader = new ClasspathResourceLoader();
		String sourceContent = read(loader.getResourceStream(sourceFilePath));
		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();
	}
	
	
	/**
	 * 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));

	}
	
	/**
	 * 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;
	}
	
	/**
	 * 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;

	}
	
	/**
	 * Gets the index service facade.
	 * 
	 * @return the index service facade
	 */
	public IIndexService getIndexServiceFacade() {
		return indexServiceFacade;
	}

	/**
	 * Sets the index service facade.
	 * 
	 * @param indexServiceFacade the new index service facade
	 */
	public void setIndexServiceFacade(IIndexService indexServiceFacade) {
		this.indexServiceFacade = indexServiceFacade;
	}
	
	/**
	 * Returns target Result Set service location.
	 * 
	 * @return target Result Set service location
	 */
	public String getResultSetLocation() {
		return resultSetLocation;
	}

	/**
	 * Sets target Result Set service location.
	 * 
	 * @param resultSetLocation the result set location
	 */
	public void setResultSetLocation(String resultSetLocation) {
		this.resultSetLocation = resultSetLocation;
	}
}
