package eu.dnetlib.enabling.is.registry;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.ServiceLocator;

/**
 * implement the compatibility pending profile policy.
 * 
 * <p>
 * pending profiles receive their own special resource kind and are 'moved' to a different collection
 * </p>
 * 
 * @author marko
 * 
 */
public class CompatPendingResourceManagerImpl implements PendingResourceManager {

	/**
	 * service locator for IS Store service. Necessary because this implementation of the pending resource manager needs
	 * to move profiles from one collection to the other.
	 */
	private ServiceLocator<ISRegistryService> registryLocator;

	/**
	 * information space application profile. Contains, among others, mappings for the.. TODO: move to a DAO.
	 * 
	 */
	private Document appProfile;

	/**
	 * read the application profile from disk.
	 */
	public CompatPendingResourceManagerImpl() {
		try {
			appProfile = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
					getClass().getResourceAsStream("DRIVERInformationSpaceApplicationProfile.xml"));
		} catch (SAXException e) {
			throw new IllegalStateException("cannot parse information space application profile", e);
		} catch (IOException e) {
			throw new IllegalStateException("cannot parse information space application profile", e);
		} catch (ParserConfigurationException e) {
			throw new IllegalStateException("cannot parse information space application profile", e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setPending(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	public void setPending(final OpaqueResource resource, final boolean local) {
		try {
			resource.setResourceKind(getPendingKindForType(resource.getResourceType()));

			if (!local) {
				registryLocator.getService().deleteProfile(resource.getResourceId());

				final String newId = registryLocator.getService().registerProfile(resource.asString());
				resource.setResourceId(newId);
			}

		} catch (XPathExpressionException e) {
			throw new IllegalStateException(e);
		} catch (ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setPending(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	public void setPending(final OpaqueResource resource) {
		setPending(resource, false);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setValid(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	public void setValid(final OpaqueResource resource) {
		try {
			if (resource.getResourceKind() != null && resource.getResourceKind().equals(getNormalKindForType(resource.getResourceType()))) {
				throw new IllegalArgumentException("trying to validate an already valid resource");
			}

			resource.setResourceKind(getNormalKindForType(resource.getResourceType()));

			registryLocator.getService().deleteProfile(resource.getResourceId());

			final String newId = registryLocator.getService().registerProfile(resource.asString());
			resource.setResourceId(newId);

		} catch (XPathExpressionException e) {
			throw new IllegalStateException(e);
		} catch (ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * get the default resource kind associated with a given resource type.
	 * 
	 * @param resourceType
	 *            resource type
	 * @return resourceType
	 * @throws XPathExpressionException
	 *             happens?
	 */
	String getNormalKindForType(final String resourceType) throws XPathExpressionException {
		final XPath xpath = XPathFactory.newInstance().newXPath();
		final String res = xpath.evaluate("//RESOURCE_TYPE[text() = '" + resourceType + "']/../../RESOURCE_KIND", appProfile);
		if (res.isEmpty())
			return null;
		return res;
	}

	/**
	 * find the associated pending typology for a given resource type.
	 * 
	 * @param resourceType
	 *            resource type
	 * @return pending resource kind
	 * @throws XPathExpressionException
	 *             shouldn't happen
	 */
	String getPendingKindForType(final String resourceType) throws XPathExpressionException {
		final XPath xpath = XPathFactory.newInstance().newXPath();
		final String res = xpath.evaluate("//RESOURCE_TYPE[text() = '" + resourceType + "']/../../PENDING_TYPOLOGY", appProfile);
		if (res.isEmpty())
			throw new IllegalStateException("no pending category for " + resourceType);
		return res;
	}

	public Document getAppProfile() {
		return appProfile;
	}

	public void setAppProfile(final Document appProfile) {
		this.appProfile = appProfile;
	}

	public ServiceLocator<ISRegistryService> getRegistryLocator() {
		return registryLocator;
	}

	public void setRegistryLocator(final ServiceLocator<ISRegistryService> registryLocator) {
		this.registryLocator = registryLocator;
	}
}
