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

import java.io.StringReader;
import java.util.Iterator;
import java.util.Map;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import com.googlecode.sarasvati.Engine;
import com.googlecode.sarasvati.NodeToken;
import com.googlecode.sarasvati.env.Env;
import eu.dnetlib.data.objectstore.rmi.MetadataObjectRecord;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreService;
import eu.dnetlib.enabling.resultset.IterableResultSetFactory;
import eu.dnetlib.enabling.resultset.client.ResultSetClientFactory;
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.nodes.blackboard.BlackboardWorkflowJobListener;
import eu.dnetlib.msro.workflows.resultset.ProcessCountingResultSetFactory;
import eu.dnetlib.msro.workflows.util.ProgressProvider;
import eu.dnetlib.msro.workflows.util.ResultsetProgressProvider;
import eu.dnetlib.msro.workflows.util.WorkflowsConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;

public class DownloadIntoObjectStoreJobNode extends BlackboardJobNode implements ProgressJobNode {

	private static final Log log = LogFactory.getLog(DownloadIntoObjectStoreJobNode.class);
	private String eprParam;
	private String objectStoreId;
	private String idXpath; // "//*[local-name()='objIdentifier']
	private String mimeType;
	private String objectIsInsideEpr;
	private String storageType;
	private IterableResultSetFactory iterableResultSetFactory;
	private ResultSetClientFactory resultSetClientFactory;
	private ResultsetProgressProvider progressProvider;
	private ProcessCountingResultSetFactory processCountingResultSetFactory;

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

	public String getEprParam() {
		return eprParam;
	}

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

	public String getObjectStoreId() {
		return objectStoreId;
	}

	public void setObjectStoreId(final String objectStoreId) {
		this.objectStoreId = objectStoreId;
	}

	public ProgressProvider getProgressProvider(final NodeToken token) {

		return progressProvider;
	}

	@Override
	protected void prepareJob(final BlackboardJob job, final NodeToken token) throws Exception {

		job.setAction("FEEDOBJECT");
		final String eprS = token.getEnv().getAttribute(getEprParam());
		job.getParameters().put("obsID", getObjectStoreId());
		job.getParameters().put("mime", getMimeType());
		final Iterator<String> client = resultSetClientFactory.getClient(eprS).iterator();

		final W3CEndpointReference epr = iterableResultSetFactory.createIterableResultSet(
				() -> new MetadataObjectIterator(client, "//*[local-name()='objIdentifier']", "xml"));
		this.progressProvider = processCountingResultSetFactory.createProgressProvider(token.getProcess(), epr);
		job.getParameters().put("epr", progressProvider.getEpr().toString());

	}

	@Override
	protected BlackboardWorkflowJobListener generateBlackboardListener(final Engine engine, final NodeToken token) {
		return new BlackboardWorkflowJobListener(engine, token) {

			@Override
			protected void populateEnv(final Env env, final Map<String, String> responseParams) {
				log.info("Number of stored records: " + responseParams.get("total"));
				env.setAttribute(WorkflowsConstants.MAIN_LOG_PREFIX + "total", responseParams.get("total"));
			}
		};
	}

	public String getObjectIsInsideEpr() {
		return objectIsInsideEpr;
	}

	public void setObjectIsInsideEpr(final String objectIsInsideEpr) {
		this.objectIsInsideEpr = objectIsInsideEpr;
	}

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

	public void setProgressProvider(final ResultsetProgressProvider progressProvider) {
		this.progressProvider = progressProvider;
	}

	public ProcessCountingResultSetFactory getProcessCountingResultSetFactory() {
		return processCountingResultSetFactory;
	}

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

	public ResultSetClientFactory getResultSetClientFactory() {
		return resultSetClientFactory;
	}

	public void setResultSetClientFactory(final ResultSetClientFactory resultSetClientFactory) {
		this.resultSetClientFactory = resultSetClientFactory;
	}

	public IterableResultSetFactory getIterableResultSetFactory() {
		return iterableResultSetFactory;
	}

	public void setIterableResultSetFactory(final IterableResultSetFactory iterableResultSetFactory) {
		this.iterableResultSetFactory = iterableResultSetFactory;
	}

	public String getIdXpath() {
		return idXpath;
	}

	public void setIdXpath(final String idXpath) {
		this.idXpath = idXpath;
	}

	public String getMimeType() {
		return mimeType;
	}

	public void setMimeType(final String mimeType) {
		this.mimeType = mimeType;
	}

	public String getStorageType() {
		return storageType;
	}

	public void setStorageType(final String storageType) {
		this.storageType = storageType;
	}

	class MetadataObjectIterator implements Iterator<String> {

		private Iterator<String> inputIterator;

		private String mime;

		public MetadataObjectIterator(final Iterator<String> inputIterator, final String xpath, final String mime) {
			this.inputIterator = inputIterator;
		}

		@Override
		public boolean hasNext() {
			return inputIterator.hasNext();
		}

		@Override
		public String next() {
			try {
				String record = inputIterator.next();
				XPath xpath = XPathFactory.newInstance().newXPath();
				InputSource doc = new InputSource(new StringReader(record));
				String identifier = xpath.evaluate(getIdXpath(), doc);
				MetadataObjectRecord objectrecord = new MetadataObjectRecord(identifier, record, mime);
				return objectrecord.toJSON();
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public void remove() {

		}

	}

}
