package eu.dnetlib.msro.workflows.nodes.index;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.annotation.Resource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import com.googlecode.sarasvati.NodeToken;

import eu.dnetlib.data.provision.index.rmi.IndexService;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.resultset.XSLTMappedResultSetFactory;
import eu.dnetlib.enabling.resultset.client.utils.EPRUtils;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.tools.blackboard.BlackboardJob;
import eu.dnetlib.msro.workflows.nodes.BlackboardJobNode;
import eu.dnetlib.msro.workflows.nodes.ProgressJobNode;
import eu.dnetlib.msro.workflows.resultset.ProcessCountingResultSetFactory;
import eu.dnetlib.msro.workflows.util.ProgressProvider;
import eu.dnetlib.msro.workflows.util.ResultsetProgressProvider;

public class UpdateIndexJobNode extends BlackboardJobNode implements ProgressJobNode {

	private static final Log log = LogFactory.getLog(UpdateIndexJobNode.class);

	private String eprParam;
	private String indexId;
	private String format;
	private String layout;
	private String feedingType;
	private String defaultIndexId;

	/**
	 * xslt mapped resultset factory.
	 */
	private XSLTMappedResultSetFactory xsltRSFactory;

	private ProcessCountingResultSetFactory processCountingResultSetFactory;
	private ResultsetProgressProvider progressProvider;

	/**
	 * Stylesheet which transforms a layout to another stylesheet which converts a input record to a index record.
	 */
	private org.springframework.core.io.Resource layoutToRecordStylesheet;

	/**
	 * service locator.
	 */
	@Resource
	private UniqueServiceLocator serviceLocator;

	@Override
	protected String obtainServiceId(final NodeToken token) {
		return getServiceLocator().getServiceId(IndexService.class);
	}

	@Override
	protected void prepareJob(final BlackboardJob job, final NodeToken token) throws ResultSetException, ISLookUpException, IOException, TransformerException {
		log.info("preparing blackboard job update index: " + getIndexId());

		final W3CEndpointReference epr = new EPRUtils().getEpr(token.getEnv().getAttribute(getEprParam()));

		final W3CEndpointReference mappedEpr = prepareForIndexing(epr, getFormat(), getLayout());

		progressProvider = processCountingResultSetFactory.createProgressProvider(token.getProcess(), mappedEpr);

		job.setAction("FEED");
		job.getParameters().put("resultset_epr", encode(progressProvider.getEpr().toString()));
		job.getParameters().put("id", getIndexId());
		job.getParameters().put("feeding_type", getFeedingType());
		job.getParameters().put("backend_Id", defaultIndexId);
	}

	// helpers

	/**
	 * Transforms each mdstore record into a index record.
	 * 
	 * @param mdStoreRsetEpr
	 *            mdstore resulsetset
	 * @param layout
	 *            layout
	 * @param format
	 *            format
	 * @return resultset with transformed records
	 * @throws ISLookUpException
	 *             could happen
	 * @throws IOException
	 *             could happen
	 * @throws TransformerException
	 *             could happen
	 */
	protected W3CEndpointReference prepareForIndexing(final W3CEndpointReference mdStoreRsetEpr, final String format, final String layout)
			throws ISLookUpException, IOException, TransformerException {

		final TransformerFactory factory = TransformerFactory.newInstance();
		final Transformer layoutTransformer = factory.newTransformer(new StreamSource(getLayoutToRecordStylesheet().getInputStream()));

		final DOMResult layoutToXsltXslt = new DOMResult();
		layoutTransformer.setParameter("format", format);
		layoutTransformer.transform(new StreamSource(new StringReader(getLayoutSource(format, layout))), layoutToXsltXslt);

		dumpXslt(factory, layoutToXsltXslt);

		return getXsltRSFactory().createMappedResultSet(mdStoreRsetEpr, new DOMSource(layoutToXsltXslt.getNode()),
				"dynamic layout xslt for " + format + ", " + layout);
	}

	private String getLayoutSource(final String format, final String layout) throws ISLookUpDocumentNotFoundException, ISLookUpException {
		return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(
				"collection('')//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value = 'MDFormatDSResourceType' and .//NAME='" + format + "']//LAYOUT[@name='" + layout
						+ "']");
	}

	private void dumpXslt(final TransformerFactory factory, final DOMResult layoutToXsltXslt) throws TransformerConfigurationException, TransformerException {
		if (log.isDebugEnabled()) {
			final StringWriter buffer = new StringWriter();
			factory.newTransformer().transform(new DOMSource(layoutToXsltXslt.getNode()), new StreamResult(buffer));
			log.debug(buffer.toString());
		}
	}

	private String encode(final String epr) {
		return new String(Base64.encodeBase64(epr.getBytes()));
	}

	// setters and getters

	public String getIndexId() {
		return indexId;
	}

	public void setIndexId(final String indexId) {
		this.indexId = indexId;
	}

	public String getEprParam() {
		return eprParam;
	}

	public void setEprParam(final String eprParam) {
		this.eprParam = eprParam;
	}

	public String getFeedingType() {
		return feedingType;
	}

	public void setFeedingType(final String feedingType) {
		this.feedingType = feedingType;
	}

	public ProcessCountingResultSetFactory getProcessCountingResultSetFactory() {
		return processCountingResultSetFactory;
	}

	@Required
	public void setProcessCountingResultSetFactory(final ProcessCountingResultSetFactory processCountingResultSetFactory) {
		this.processCountingResultSetFactory = processCountingResultSetFactory;
	}

	@Override
	public ProgressProvider getProgressProvider() {
		return progressProvider;
	}

	public org.springframework.core.io.Resource getLayoutToRecordStylesheet() {
		return layoutToRecordStylesheet;
	}

	@Required
	public void setLayoutToRecordStylesheet(final org.springframework.core.io.Resource layoutToRecordStylesheet) {
		this.layoutToRecordStylesheet = layoutToRecordStylesheet;
	}

	public String getFormat() {
		return format;
	}

	public void setFormat(final String format) {
		this.format = format;
	}

	public String getLayout() {
		return layout;
	}

	public void setLayout(final String layout) {
		this.layout = layout;
	}

	public XSLTMappedResultSetFactory getXsltRSFactory() {
		return xsltRSFactory;
	}

	@Required
	public void setXsltRSFactory(final XSLTMappedResultSetFactory xsltRSFactory) {
		this.xsltRSFactory = xsltRSFactory;
	}

	/**
	 * @return the defaultIndexId
	 */
	public String getDefaultIndexId() {
		return defaultIndexId;
	}

	/**
	 * @param defaultIndexId
	 *            the defaultIndexId to set
	 */
	@Required
	public void setDefaultIndexId(final String defaultIndexId) {
		this.defaultIndexId = defaultIndexId;
	}

}
