package eu.dnetlib.actionmanager.actions;

import java.io.StringReader;
import java.util.List;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import eu.dnetlib.actionmanager.ActionManagerConstants.ACTION_TYPE;
import eu.dnetlib.actionmanager.common.Agent;
import eu.dnetlib.actionmanager.common.Operation;
import eu.dnetlib.actionmanager.common.Provenance;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.DocumentResult;
import org.dom4j.io.DocumentSource;
import org.dom4j.io.SAXReader;

public class XsltInfoPackageAction extends AbstractAction {

	private static final Log log = LogFactory.getLog(XsltInfoPackageAction.class); // NOPMD by marko on 11/24/08 5:02 PM

	private Operation operation;
	private String infoPackage;
	private Provenance provenance;
	private String trust;
	private ActionFactory actionFactory;
	private Transformer transformer;

	public XsltInfoPackageAction(final String rawSet, final Agent agent, final Operation operation, final String infoPackage, final Provenance provenance,
			final String trust, final Transformer transformer, final ActionFactory actionFactory) {
		super(ACTION_TYPE.pkg, rawSet, agent);

		this.transformer = transformer;
		this.operation = operation;
		this.infoPackage = infoPackage;
		this.provenance = provenance;
		this.trust = trust;
		this.actionFactory = actionFactory;
		this.transformer = transformer;
	}

	public List<AtomicAction> asAtomicActions() throws TransformerException, DocumentException {
		return Lists.newArrayList(Iterables.filter(calculateAtomicActions(), new Predicate<AtomicAction>() {
			@Override
			public boolean apply(final AtomicAction action) {
				return action != null && action.isValid();
			}
		}));
	}

	private Document applyXslt(final String xml, final Provenance provenance, final String trust) throws DocumentException,
			TransformerException {
		final Document doc = (new SAXReader()).read(new StringReader(xml));

		final DocumentResult result = new DocumentResult();

		transformer.setParameter("trust", trust);
		transformer.setParameter("provenance", provenance.toString());

		transformer.transform(new DocumentSource(doc), result);

		return result.getDocument();
	}

	protected List<AtomicAction> calculateAtomicActions() throws TransformerException, DocumentException {
		final List<AtomicAction> list = Lists.newArrayList();

		final Document doc = applyXslt(getInfoPackage(), provenance, trust);
		for (Object o : doc.selectNodes("//ROW")) {
			list.add(createAtomicAction((Element) o));
		}

		return list;
	}

	private AtomicAction createAtomicAction(final Element elem) {
		String key = elem.valueOf("./@key");
		String colFamily = elem.valueOf("./@columnFamily");
		String col = elem.valueOf("./QUALIFIER/@name");
		final String text = elem.valueOf("./QUALIFIER");
		byte[] value = StringUtils.isBlank(text) ? null : Base64.decodeBase64(text.trim());

		return getActionFactory().createAtomicAction(getRawSet(), getAgent(), key, colFamily, col, value);
	}

	public Operation getOperation() {
		return operation;
	}

	public void setOperation(final Operation operation) {
		this.operation = operation;
	}

	public ActionFactory getActionFactory() {
		return actionFactory;
	}

	public void setActionFactory(final ActionFactory actionFactory) {
		this.actionFactory = actionFactory;
	}

	protected byte[] getBytesContent(final String content) {
		return content.getBytes();
	}

	public String getInfoPackage() {
		return infoPackage;
	}

	public void setInfoPackage(final String infoPackage) {
		this.infoPackage = infoPackage;
	}

	public Provenance getProvenance() {
		return provenance;
	}

	public void setProvenance(final Provenance provenance) {
		this.provenance = provenance;
	}

	public String getTrust() {
		return trust;
	}

	public void setTrust(final String trust) {
		this.trust = trust;
	}

	public Transformer getTransformer() {
		return transformer;
	}

	public void setTransformer(final Transformer transformer) {
		this.transformer = transformer;
	}

}
