package eu.dnetlib.oai.conf;

import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;

import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.oai.PublisherField;
import eu.dnetlib.oai.info.SetInfo;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISRegistryException;
import eu.dnetlib.rmi.enabling.ISRegistryService;
import eu.dnetlib.rmi.provision.MDFInfo;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class OAIConfigurationWriter {

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

	@Autowired
	private UniqueServiceLocator serviceLocator;

	@Resource(name = "oaiConfigurationExistReader")
	private OAIConfigurationReader configuration;

	private boolean execute(final String xUpdate) throws ISRegistryException {
		log.debug("Running XUpdate:\n" + xUpdate);
		boolean done = this.serviceLocator.getService(ISRegistryService.class).executeXUpdate(xUpdate);
		if (!done) {
			log.fatal("Could not perform the following Xupdate:\n" + xUpdate);
		}
		return done;
	}

	public boolean updateMetadataFormat(final String mdPrefix, final MDFInfo newInfo) throws ISRegistryException {

		final String xUpdate = "update replace //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']"
				+ "//CONFIGURATION//METADATAFORMAT[./@metadataPrefix/string()='" + mdPrefix + "'] with <METADATAFORMAT exportable=\"" + newInfo.isEnabled()
				+ "\" metadataPrefix=\"" + newInfo.getPrefix() + "\">" + "<NAMESPACE>" + newInfo.getNamespace() + "</NAMESPACE><SCHEMA>" + newInfo.getSchema()
				+ "</SCHEMA><SOURCE_METADATA_FORMAT interpretation=\"" + newInfo.getSourceInterpretation() + "\" layout=\""
				+ newInfo.getSourceLayout() + "\" name=\"" + newInfo.getSourceFormat() + "\"/><TRANSFORMATION_RULE>"
				+ newInfo.getTransformationRuleID() + "</TRANSFORMATION_RULE><BASE_QUERY>" + StringEscapeUtils.escapeXml11(newInfo.getBaseQuery())
				+ "</BASE_QUERY></METADATAFORMAT>";
		return this.execute(xUpdate);
	}

	public boolean addMetadataFormat(final MDFInfo newInfo) throws ISRegistryException {

		String action = "update insert ";
		String newNode = "<METADATAFORMAT metadataPrefix='" + newInfo.getPrefix() + "' exportable='" + newInfo.isEnabled() + "' ><NAMESPACE>"
				+ newInfo.getNamespace() + "</NAMESPACE><SCHEMA>" + newInfo.getSchema() + "</SCHEMA>" + "<SOURCE_METADATA_FORMAT interpretation='"
				+ newInfo.getSourceInterpretation() + "' layout='" + newInfo.getSourceLayout() + "' name='" + newInfo.getSourceFormat()
				+ "' /><TRANSFORMATION_RULE>" + newInfo.getTransformationRuleID() + "</TRANSFORMATION_RULE><BASE_QUERY>"
				+ StringEscapeUtils.escapeXml11(newInfo.getBaseQuery()) + "</BASE_QUERY></METADATAFORMAT>";
		String targetNode = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION/METADATAFORMATS";

		String xUpdate = action + newNode + " into " + targetNode;
		return this.execute(xUpdate);
	}

	public boolean deleteMetadataFormat(final String mdPrefix) throws ISRegistryException {
		String xUpdate =
				"update delete //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION//METADATAFORMATS/METADATAFORMAT[./@metadataPrefix/string()='"
						+ mdPrefix + "']";
		return this.execute(xUpdate);
	}

	public boolean updateOAISet(final String setSpec, final SetInfo newInfo) throws ISRegistryException {
		final String xUpdate = "update replace //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']"
				+ "//CONFIGURATION//OAISET[spec/text()='" + setSpec + "'] with " + "<OAISET enabled=\"" + newInfo.isEnabled() + "\">" + "<spec>"
				+ newInfo.getSetSpec() + "</spec>" + "<name>" + newInfo.getSetName() + "</name>" + "<description>" + newInfo.getSetDescription()
				+ "</description>" + "<query>" + StringEscapeUtils.escapeXml11(newInfo.getQuery()) + "</query></OAISET>";

		return this.execute(xUpdate);
	}

	public boolean addOAISet(final SetInfo newInfo) throws ISRegistryException {
		String action = "update insert ";
		String newNode = "<OAISET enabled=\"" + newInfo.isEnabled() + "\">" + "<spec>" + newInfo.getSetSpec() + "</spec>" + "<name>" + newInfo.getSetName()
				+ "</name>" + "<description>" + newInfo.getSetDescription() + "</description>" + "<query>" + StringEscapeUtils.escapeXml11(newInfo.getQuery())
				+ "</query></OAISET>";
		String targetNode = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION/OAISETS";

		String xUpdate = action + newNode + " into " + targetNode;
		return this.execute(xUpdate);
	}

	public boolean deleteOAISet(final String setSpec) throws ISRegistryException {
		String xUpdate =
				"update delete //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION//OAISET[spec/text()='"
						+ setSpec + "']";
		return this.execute(xUpdate);
	}

	public boolean updateIndices(final String format, final String layout, final String interpretation, final List<PublisherField> indexes)
			throws ISRegistryException {
		boolean dropped = this.dropIndexesFor(format, layout, interpretation);
		String xUpdateAction = "update insert ";
		String nodeTemplate = " <SOURCE name='" + format + "' layout='" + layout + "' interpretation='" + interpretation + "' ";
		String targetTemplate = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION/INDICES/INDEX";
		if (!dropped) { return false; }
		for (PublisherField field : indexes) {
			String targetNode = targetTemplate + "[@name='" + field.getFieldName() + "']";
			Collection<String> paths = field.getSources().get(format + "-" + layout + "-" + interpretation);
			for (String p : paths) {
				// insert one SOURCE node at a time, otherwise Exist complaints.
				String node = nodeTemplate + " path=\"" + p + "\" />";
				String xUpdate = xUpdateAction + node + " into " + targetNode;
				boolean done = this.execute(xUpdate);
				if (!done) { return false; }
			}
			// finally let's update the repeatability
			String updateRepeatability = "update value " + targetNode + "/@repeatable with '" + field.isRepeatable() + "'";
			boolean done = this.execute(updateRepeatability);
			if (!done) { return false; }
		}
		return true;
	}

	private boolean dropIndexesFor(final String format, final String layout, final String interpretation) throws ISRegistryException {
		String xUpdate =
				"update delete //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION//INDEX/SOURCE[./@interpretation/string()='"
						+ interpretation + "' and ./@name/string()='" + format + "' and ./@layout/string()='" + layout + "'] ";
		return this.execute(xUpdate);
	}

	public boolean addNewIndex(final String format,
			final String layout,
			final String interpretation,
			final String indexName,
			final boolean isRepeatable,
			final String[] paths) throws ISLookUpException, ISRegistryException {
		String action = "update insert ";
		String nodeTemplate = " <SOURCE name='" + format + "' layout='" + layout + "' interpretation='" + interpretation + "' ";
		String targetTemplate = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='OAIPublisherConfigurationDSResourceType']//CONFIGURATION/INDICES";
		if (this.configuration.getFieldNames().contains(indexName)) {
			String targetNode = targetTemplate + "/INDEX[@name/string()='" + indexName + "']";
			for (String p : paths) {
				// insert one SOURCE node at a time, otherwise Exist complaints.
				String node = nodeTemplate + " path=\"" + p + "\" />";
				String xUpdate = action + node + " into " + targetNode;
				boolean done = this.execute(xUpdate);
				if (!done) { return false; }
			}
		} else {
			String targetNode = targetTemplate;
			String node = "<INDEX name='" + indexName + "' repeatable='" + isRepeatable + "'>";
			for (String p : paths) {
				node += nodeTemplate + " path=\"" + p + "\" />";
			}
			node += "</INDEX>";
			String xUpdate = action + node + " into " + targetNode;
			boolean done = this.execute(xUpdate);
			if (!done) { return false; }
		}
		return true;
	}

	public OAIConfigurationReader getConfiguration() {
		return configuration;
	}

	public void setConfiguration(final OAIConfigurationReader configuration) {
		this.configuration = configuration;
	}

}
