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

import java.io.IOException;
import java.util.Map;

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

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

import com.google.common.collect.Maps;
import com.googlecode.sarasvati.Arc;
import com.googlecode.sarasvati.NodeToken;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.resultset.MappedResultSetFactory;
import eu.dnetlib.enabling.resultset.client.utils.EPRUtils;
import eu.dnetlib.msro.rmi.MSROException;
import eu.dnetlib.msro.workflows.nodes.SimpleJobNode;
import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;

public class GroovyJobNode extends SimpleJobNode {

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

	/**
	 * used to transform the records using Groovy.
	 */

	private MappedResultSetFactory mappedResultSetFactory;

	private String inputEprParam;
	private String outputEprParam;
	private String transformationRuleId;
	// private String groovyParams;

	@Resource
	private UniqueServiceLocator serviceLocator;

	private Map<String, String> retrieveGroovyParameter() {
		Map<String, String> out = Maps.newHashMap();

		String query = "for $x in collection('/db/DRIVER/GroovyProcessingDSResource/GroovyProcessingDSResourceType')"
				+ "where $x[.//RESOURCE_IDENTIFIER/@value='" + transformationRuleId + "']"
				+ "return concat($x//GROOVY_CLASSPATH/text(),':::',$x//GROOVY_DNETCLASS/text())";
		try {
			String result = serviceLocator.getService(ISLookUpService.class).quickSearchProfile(query).get(0);
			if (result == null) { return null; }
			String[] data = result.trim().split(":::");
			if (data.length == 2) {
				out.put("classpath", data[0]);
				out.put("mainClass", data[1]);
			}

			return out;
		} catch (Exception e) {
			log.error(e);
			return null;
		}
	}

	@Override
	protected String execute(final NodeToken token) throws Exception {
		final String inputEprString = token.getEnv().getAttribute(inputEprParam);
		if (inputEprString == null || inputEprString.isEmpty()) { throw new MSROException("InputEprParam (" + inputEprParam + ") not found in ENV"); }
		final W3CEndpointReference inputEpr = new EPRUtils().getEpr(inputEprString);
		String groovyClasspath, groovyDnetClass;
		Map<String, String> prop = retrieveGroovyParameter();
		groovyClasspath = prop.get("classpath");
		groovyDnetClass = prop.get("mainClass");
		W3CEndpointReference epr = transformGroovy(inputEpr, groovyClasspath, groovyDnetClass, parseJsonParameters(token));
		token.getEnv().setAttribute(outputEprParam, epr.toString());
		return Arc.DEFAULT_ARC;
	}

	private W3CEndpointReference transformGroovy(final W3CEndpointReference source,
			final String groovyClasspath,
			final String groovyDnetClass,
			final Map<String, String> params) throws ClassNotFoundException, IOException {

		GroovyScriptEngine gse = new GroovyScriptEngine(groovyClasspath);
		gse.getGroovyClassLoader().loadClass(groovyDnetClass);
		log.info("***********************************************");
		log.info("Loaded Groovy classes:");
		for (Class<?> c : gse.getGroovyClassLoader().getLoadedClasses()) {
			log.info(c.getCanonicalName());
		}
		log.info("***********************************************");
		GroovyShell groovyShell = new GroovyShell(gse.getGroovyClassLoader());

		Object go = groovyShell.evaluate("new " + groovyDnetClass + "()");
		if (go instanceof GroovyUnaryFunction) {
			GroovyUnaryFunction groovyUnaryFunction = (GroovyUnaryFunction) go;
			if (params != null) {
				groovyUnaryFunction.setParams(params);
			}
			return mappedResultSetFactory.createMappedResultSet(source, groovyUnaryFunction);
		} else {
			throw new RuntimeException("Groovy object " + go + " is not supported");
		}
	}

	public MappedResultSetFactory getMappedResultSetFactory() {
		return mappedResultSetFactory;
	}

	@Required
	public void setMappedResultSetFactory(final MappedResultSetFactory mappedResultSetFactory) {
		this.mappedResultSetFactory = mappedResultSetFactory;
	}

	public String getInputEprParam() {
		return inputEprParam;
	}

	public void setInputEprParam(final String inputEprParam) {
		this.inputEprParam = inputEprParam;
	}

	public String getOutputEprParam() {
		return outputEprParam;
	}

	public void setOutputEprParam(final String outputEprParam) {
		this.outputEprParam = outputEprParam;
	}

	public String getTransformationRuleId() {
		return transformationRuleId;
	}

	public void setTransformationRuleId(final String transformationRuleId) {
		this.transformationRuleId = transformationRuleId;
	}

}
