package eu.dnetlib.actionmanager.actions;

import java.util.List;
import java.util.Map;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;

import org.apache.hadoop.hbase.util.Bytes;
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;

import com.google.common.collect.Lists;

import eu.dnetlib.actionmanager.common.Agent;
import eu.dnetlib.actionmanager.common.Operation;
import eu.dnetlib.actionmanager.rmi.ActionManagerException;
import eu.dnetlib.data.proto.RelTypeProtos.RelType;
import eu.dnetlib.data.proto.TypeProtos.Type;
import eu.dnetlib.miscutils.datetime.DateUtils;

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

	private Map<String, Resource> xslts;

	final TransformerFactory tFactory = TransformerFactory.newInstance();

	/**
	 * Creates a list of AtomicAction(s) to handle an UPDATE. 1 body and 2 similarRel(s)
	 * 
	 * @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 updateId
	 *            new and unique identifier, must be the same used into "updateContent".
	 * @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. 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.
	 */
	@Deprecated
	public List<AtomicAction> createUpdateActions(String set, Agent agent, String originalId, String updateId, Type entityType, byte[] updateContent) {
		List<AtomicAction> res = Lists.newArrayList();

		if (originalId.equals(updateId)) {
			throw new IllegalArgumentException("originalId and updateId must be different");
		}

		res.add(createAtomicAction(set, agent, updateId, entityType.toString(), "body", updateContent));
		res.add(createAtomicAction(set, agent, updateId, RelType.similarRel.toString(), originalId, Bytes.toBytes("")));
		res.add(createAtomicAction(set, agent, originalId, RelType.similarRel.toString(), updateId, Bytes.toBytes("")));

		return res;
	}

	/**
	 * 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(String set, Agent agent, String originalId, Type entityType, byte[] updateContent) {
		List<AtomicAction> res = Lists.newArrayList();

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

		return res;
	}

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

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

		return action;
	}

	public XsltInfoPackageAction generateInfoPackageAction(String xsltResource, String set, Agent agent, Operation operation, String infoPackage)
			throws ActionManagerException {
		Transformer transformer = prepareXsltTransformer(xsltResource);
		return new XsltInfoPackageAction(set, agent, operation, infoPackage, transformer, this);
	}

	public Transformer prepareXsltTransformer(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());
			return tFactory.newTransformer(new DocumentSource(doc));
		} catch (Exception e) {
			throw new ActionManagerException("Problem with xslt resource " + xsltName, e);
		}
	}

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

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

}
