package eu.dnetlib.enabling.manager.msro;

import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.tools.ServiceLocator;

/**
 * Persistent manager map maintained in the IS.
 * 
 * @author marko
 * 
 */
public class ISManagerMapImpl implements ManagerMap {
	private static final Log log = LogFactory.getLog(ISManagerMapImpl.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * lookup locator.
	 */
	private ServiceLocator<ISLookUpService> lookUpLocator;

	/**
	 * registry locator.
	 */
	private ServiceLocator<ISRegistryService> registryLocator;

	/**
	 * empty map template.
	 */
	private StringTemplate emptyMapTemplate;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#indexForMDStore(java.lang.String)
	 */
	@Override
	public String indexForMDStore(final String mdId, final String format, final String layout, final String interpretation) {
		try {
			ensureMapExists();
			final String query = "collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP/INDEX[MDSTORE/@id='"
					+ mdId + "' and @format = '" + format + "' and @layout = '" + layout + "' and @interpretation = '" + interpretation
					+ "']/@id/string()";

			return lookUpLocator.getService().getResourceProfileByQuery(query);

		} catch (final ISLookUpDocumentNotFoundException e) {
			return null;
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#registerIndexForMDStore(java.lang.String, java.lang.String)
	 */
	@Override
	public void registerIndexForMDStore(final String mdId, final String indexId, final String format, final String layout, final String interpretation) {
		try {
			ensureMapExists();

			final String xupdate = "for $x in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP "
					+ "return update insert <INDEX format=\"" + format + "\" layout=\"" + layout + "\" interpretation=\"" + interpretation
					+ "\" parentId=\"\" hiId=\"\" id=\"" + indexId + "\">" + "<MDSTORE parentId=\"\" id=\"" + mdId + "\"/>" + "</INDEX> into $x";
			registryLocator.getService().executeXUpdate(xupdate);

		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#removeIndex(java.lang.String)
	 */
	@Override
	public void removeIndex(final String indexId) {
		try {
			final String xupdate = "for $x in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP "
					+ "return update delete $x//INDEX[@id = '" + indexId + "']";

			registryLocator.getService().executeXUpdate(xupdate);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#removeMDStore(java.lang.String)
	 */
	@Override
	public void removeMDStore(final String mdId) {
		try {
			final String xupdate = "for $x in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP "
					+ "return update delete $x//INDEX[MDSTORE[@id = '" + mdId + "']]";

			registryLocator.getService().executeXUpdate(xupdate);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * ensure that the manager map exists.
	 * 
	 * @throws ISLookUpException
	 *             could happen
	 * @throws ISRegistryException
	 *             could happen
	 */
	protected void ensureMapExists() throws ISLookUpException, ISRegistryException {
		ensureMapExistsWithMaster(null);
	}

	protected void ensureMapExistsWithMaster(String candidateMaster) throws ISLookUpException, ISRegistryException {
		log.debug("ENSURING MAP EXISTS: " + candidateMaster);
		try {
			lookUpLocator.getService().getResourceProfileByQuery("collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')");

		} catch (final ISLookUpDocumentNotFoundException e) {
			registryLocator.getService().registerProfile(emptyMapTemplate.toString());
		}

		String master;
		try {
			master = lookUpLocator.getService().getResourceProfileByQuery(
					"string(collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//MASTER_INDEX)");
		} catch (final ISLookUpDocumentNotFoundException e) {
			master = "";
		}

		log.debug("MASTER WAS: " + master);
		if (master.isEmpty()) {
			setMasterIndexService(candidateMaster == null ? electMasterIndexService() : candidateMaster);
		}
	}

	@Override
	public void offerMasterIndexService(final String id) {
		try {
			ensureMapExistsWithMaster(id);
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	private String electMasterIndexService() {
		final String query = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value = 'IndexServiceResourceType']//RESOURCE_IDENTIFIER/@value/string()";
		try {
			return lookUpLocator.getService().getResourceProfileByQuery(query);
		} catch (final ISLookUpDocumentNotFoundException e) {
			return null;
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#getMasterIndexService()
	 */
	@Override
	public String getMasterIndexService() {
		try {
			return lookUpLocator.getService().getResourceProfileByQuery(
					"collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//MASTER_INDEX/text()");
		} catch (final ISLookUpDocumentNotFoundException e) {
			return null;
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.manager.msro.ManagerMap#setMasterIndexService(java.lang.String)
	 */
	@Override
	public void setMasterIndexService(final String id) {
		log.debug("SETTING MASTER TO: " + id);

		final String indexServiceId = (id == null) ? "" : id;

		try {
			final String xupdate = "for $x in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType') "
					+ "return update value $x//MASTER_INDEX with '" + indexServiceId + "'";
			log.debug(xupdate);
			registryLocator.getService().executeXUpdate(xupdate);
		} catch (final ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	public ServiceLocator<ISLookUpService> getLookUpLocator() {
		return lookUpLocator;
	}

	@Required
	public void setLookUpLocator(final ServiceLocator<ISLookUpService> lookUpLocator) {
		this.lookUpLocator = lookUpLocator;
	}

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

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

	public StringTemplate getEmptyMapTemplate() {
		return emptyMapTemplate;
	}

	@Required
	public void setEmptyMapTemplate(final StringTemplate emptyMapTemplate) {
		this.emptyMapTemplate = emptyMapTemplate;
	}

}
