package eu.dnetlib.actionmanager.actions;

import java.util.List;
import java.util.Map;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;

import com.google.common.collect.Lists;
import eu.dnetlib.actionmanager.common.Agent;
import eu.dnetlib.actionmanager.common.Operation;
import eu.dnetlib.actionmanager.common.Provenance;
import eu.dnetlib.actionmanager.rmi.ActionManagerException;
import eu.dnetlib.data.proto.TypeProtos.Type;
import eu.dnetlib.miscutils.datetime.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.io.DocumentSource;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;

/**
 * Factory bean to create Actions bypassing the Action Manager API
 * 
 * @author michele
 */
public class ActionFactory {

	private static final Log log = LogFactory.getLog(ActionFactory.class);
	final TransformerFactory tFactory = TransformerFactory.newInstance();
	private Map<String, Resource> xslts;

	/**
	 * Creates a list of AtomicAction(s) to handle an UPDATE. Produces one Oaf, stored in CF = [entity]:update_[timestamp]
	 * 
	 * @param set
	 *            the given set.
	 * @param agent
	 *            the given agent.
	 * @param originalId
	 *            main entity identifier to be updated (already existing in the information space).
	 * @param entityType
	 *            one of: result, person, project, datasource, organization {@link eu.dnetlib.data.proto.TypeProtos.Type}.
	 * @param updateContent
	 *            content to be updated over the original main entity. Oaf#OafEntity#[entity]#id must be consistent with the originalId.
	 *            note that: the given trust level has to be [>.9 | NEUTRAL | INFINITE] in order to gain visibility after the merging
	 *            process.
	 * @return the list of AtomicActions.
	 */
	public List<AtomicAction> createUpdateActions(final String set,
			final Agent agent,
			final String originalId,
			final Type entityType,
			final byte[] updateContent) {
		final List<AtomicAction> res = Lists.newArrayList();

		res.add(createAtomicAction(set, agent, originalId, entityType.toString(), "update_" + DateUtils.now(), updateContent));

		return res;
	}

	public AtomicAction createAtomicAction(final String set,
			final Agent agent,
			final String targetKey,
			final String targetColFamily,
			final String targetCol,
			final byte[] targetContent) {
		final AtomicAction action = new AtomicAction(set, agent);

		action.setTargetRowKey(targetKey);
		action.setTargetColumnFamily(targetColFamily);
		action.setTargetColumn(targetCol);
		action.setTargetValue(targetContent);

		return action;
	}

	public XsltInfoPackageAction generateInfoPackageAction(final String xsltResource,
			final String rawSet,
			final Agent agent,
			final Operation operation,
			final String infoPackage,
			final Provenance provenance,
			final String trust) throws ActionManagerException {

		final Transformer transformer = prepareXsltTransformer(xsltResource);

		return new XsltInfoPackageAction(rawSet, agent, operation, infoPackage, provenance, trust, transformer, this);
	}

	public Transformer prepareXsltTransformer(final String xsltName) throws ActionManagerException {
		try {
			if (!xslts.containsKey(xsltName)) { throw new ActionManagerException("XSLT " + xsltName + " not found"); }

			final Document doc = new SAXReader().read(xslts.get(xsltName).getInputStream());
			if (log.isDebugEnabled()) {
				log.debug("using transformer factory: '" + tFactory.getClass().getName() + "'");
				log.debug("xslt: \n" + doc.asXML());
			}
			return tFactory.newTransformer(new DocumentSource(doc));
		} catch (final Throwable e) {
			throw new ActionManagerException("Problem with xslt resource " + xsltName, e);
		}
	}

	public Map<String, Resource> getXslts() {
		return xslts;
	}

	@Required
	public void setXslts(final Map<String, Resource> xslts) {
		this.xslts = xslts;
	}

}
