package eu.dnetlib.data.cleaner;

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

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import eu.dnetlib.rmi.data.CleanerException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.springframework.beans.factory.annotation.Required;

public abstract class XPATHCleaningRule {

	private String xpath;
	private boolean strict = false;
	private Map<String, String> namesapceMap = Maps.newHashMap();

	private static final Log logCleaningRules = LogFactory.getLog("VOCABULARY_RULES");

	public List<Map<String, String>> applyXpathRule(final Document doc) throws CleanerException {
		final List<Map<String, String>> errors = Lists.newArrayList();

		final String id = doc.valueOf("//*[local-name()='objIdentifier']");

		XPath xpath = DocumentHelper.createXPath(this.xpath);
		xpath.setNamespaceURIs(getNamesapceMap());


		for (final Object o : xpath.selectNodes(doc)) {
			final Node node = (Node) o;

			final String oldValue = node.getText().trim();

			final String newValue = calculateNewValue(oldValue);
			if (this.strict) {
				final Map<String, String> err = verifyValue(newValue);
				if (err != null) {
					errors.add(err);

					if (logCleaningRules.isInfoEnabled()) {
						logCleaningRules.info("[" + newValue + "] is INVALID, " + "RULE: " + toString() + ", " + "RECORD: " + id + ", " + "XPATH: "
								+ this.getXpath());
					}
				}
			}

			if (logCleaningRules.isInfoEnabled() && !newValue.equals(oldValue)) {
				logCleaningRules.info("[" + oldValue + "] => [" + newValue + "], " + toString() + ", " + "RECORD: " + id + ", " + "XPATH: " + this.getXpath());
			}

			node.setText(newValue);
		}

		return errors;
	}

	protected abstract Map<String, String> verifyValue(final String value) throws CleanerException;

	protected abstract String calculateNewValue(final String oldValue) throws CleanerException;

	public String getXpath() {
		return this.xpath;
	}

	@Required
	public void setXpath(final String xpath) {
		this.xpath = xpath;
	}

	public boolean isStrict() {
		return this.strict;
	}

	public void setStrict(final boolean strict) {
		this.strict = strict;
	}

	public Map<String, String> getNamesapceMap() {
		return namesapceMap;
	}

	public void setNamesapceMap(final Map<String, String> namesapceMap) {
		this.namesapceMap = namesapceMap;
	}
}
