package eu.dnetlib.data.mdstore;

import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.Endpoint;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

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.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.resultset.ResultSetFactory;
import eu.dnetlib.enabling.resultset.ResultSetListener;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.enabling.tools.blackboard.NotificationHandler;
import eu.dnetlib.soap.EndpointReferenceBuilder;

/**
 * This service implements the MDStore service interface and provides a mock useful for the MS_RO and other services
 * depending on the MDStore.
 *
 * @author marko
 *
 */
public class MDStoreServiceMockImpl extends MDStoreServiceMockImplDeprecatedStuff implements IMDStoreService {

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

	/**
	 * resultset factory for producing resultset results.
	 */
	private ResultSetFactory resultSetFactory;

	/**
	 * mdstore dao.
	 */
	private MDStoreDao mdstoreDao;

	/**
	 * notification handler.
	 */
	private NotificationHandler notificationHandler;

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

	/**
	 * lookup service locator.
	 */
	private ServiceLocator<ISLookUpService> lookupLocator;

	/**
	 * mdstore ds template.
	 */
	private StringTemplate mdstoreDsTemplate;

	/**
	 * service endpoint.
	 */
	private Endpoint endpoint;

	/**
	 * endpoint builder.
	 */
	private EndpointReferenceBuilder<Endpoint> eprBuilder;

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.IMDStoreService#deliverMDRecords(java.lang.String, java.lang.String,
	 *      java.lang.String, java.lang.String, java.lang.String)
	 */
	public W3CEndpointReference deliverMDRecords(
			final String mdId,
			final String mdFormat,
			final String from,
			final String until,
			final String recordFilter) throws MDStoreServiceException {
		return resultSetFactory.createResultSet(new ResultSetListener() {

			public List<String> getResult(final int fromPosition, final int toPosition) {
				try {
					return deliverMDRecordsDirect_Range(mdId, fromPosition, toPosition);
				} catch (final MDStoreServiceException e) {
					throw new IllegalStateException(e);
				}
			}

			public int getSize() {
				try {
					return mdstoreDao.getMDStore(mdId).getSize();
				} catch (final MDStoreServiceException e) {
					throw new IllegalStateException(e);
				}
			}
		});
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.IMDStoreService#deliverMDRecordsDirect_Range(java.lang.String, int, int)
	 */
	public List<String> deliverMDRecordsDirect_Range(final String mdId, final int mdstoreRecordNumberFrom, final int mdstoreRecordNumberTo)
			throws MDStoreServiceException {

		final List<String> results = new ArrayList<String>();

		// this works only because the mocks numbers the records according to their index.
		for (int i = mdstoreRecordNumberFrom; i <= mdstoreRecordNumberTo; i++)
			results.add(deliverRecord(mdId, Integer.toString(i)));

		return results;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.IMDStoreService#deliverRecord(java.lang.String, java.lang.String)
	 */
	public String deliverRecord(final String mdId, final String recordId) throws MDStoreServiceException {
		return mdstoreDao.getMDStore(mdId).getRecord(Integer.parseInt(recordId));
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.IMDStoreService#getListOfMDStores()
	 */
	public List<String> getListOfMDStores() {
		return mdstoreDao.listMDStores();
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.ws.store.IMDRecordsStorage#storeMDRecordsFromRS(java.lang.String, java.lang.String,
	 *      java.lang.String)
	 */
	public boolean storeMDRecordsFromRS(final String mdId, final String rsId, final String storingType) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.data.mdstore.IMDStoreService#identify()
	 */
	public String identify() {
		return getClass().getName();
	}

	/**
	 * service started.
	 */
	public void start() {
		log.info("Starting service " + identify());
	}

	/**
	 * service has been stopped.
	 */
	public void stop() {
		log.info("Stopping service " + this);
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.common.interfaces.nh.INotificationHandler#notify(java.lang.String, java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	public boolean notify(final String subscrId, final String topic, final String isId, final String message) {
		log.debug("got notification: " + topic + ", profile: " + isId + ", body: " + message);

		getNotificationHandler().notified(subscrId, topic, isId, message);

		return true;
	}

	/**
	 * Low-level api: create a mdstore resource.
	 *
	 * @param format
	 *            mdformat
	 * @param interpretation
	 *            interpretation
	 * @param layout
	 *            layout
	 * @return new mdstore id
	 * @throws ISRegistryException
	 *             could happen
	 */
	public String createMDStore(final String format, final String interpretation, final String layout) throws ISRegistryException {
		log.info("creating a mdstore: " + format);

		// XXX: mini hack
		StringTemplate template = new StringTemplate(mdstoreDsTemplate.getTemplate());
		template.setAttribute("serviceUri", eprBuilder.getAddress(endpoint));
		template.setAttribute("format", format);
		template.setAttribute("interpretation", interpretation);
		template.setAttribute("layout", layout);

		final String mdId = registryLocator.getService().registerProfile(template.toString());
		mdstoreDao.createMDStore(mdId);

		try {
			final int size = mdstoreDao.getMDStore(mdId).getSize();
			lookupLocator.getService().quickSearchProfile(
					"for $x in collection('')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value/string() = '" + mdId + "']//NUMBER_OF_RECORDS"
							+ " return update value $x with '" + size + "'");
		} catch (final ISLookUpException e) {
			throw new ISRegistryException(e);
		} catch (final MDStoreServiceException e) {
			throw new ISRegistryException(e);
		}

		return mdId;
	}

	/**
	 * Low-level api: delete a mdstore resource.
	 *
	 * @param rsId
	 *            resource id
	 * @throws ISRegistryException
	 *             could happen
	 */
	protected void deleteMDStore(final String rsId) throws ISRegistryException {
		log.info("deleting mock mdstore " + rsId);

		registryLocator.getService().deleteProfile(rsId);
	}

	public ResultSetFactory getResultSetFactory() {
		return resultSetFactory;
	}

	public void setResultSetFactory(final ResultSetFactory resultSetFactory) {
		this.resultSetFactory = resultSetFactory;
	}

	public NotificationHandler getNotificationHandler() {
		return notificationHandler;
	}

	public void setNotificationHandler(final NotificationHandler notificationHandler) {
		this.notificationHandler = notificationHandler;
	}

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

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

	public StringTemplate getMdstoreDsTemplate() {
		return mdstoreDsTemplate;
	}

	public void setMdstoreDsTemplate(final StringTemplate mdstoreDsTemplate) {
		this.mdstoreDsTemplate = mdstoreDsTemplate;
	}

	public MDStoreDao getMdstoreDao() {
		return mdstoreDao;
	}

	@Required
	public void setMdstoreDao(final MDStoreDao mdstoreDao) {
		this.mdstoreDao = mdstoreDao;
	}

	public ServiceLocator<ISLookUpService> getLookupLocator() {
		return lookupLocator;
	}

	public void setLookupLocator(final ServiceLocator<ISLookUpService> lookupLocator) {
		this.lookupLocator = lookupLocator;
	}

	public Endpoint getEndpoint() {
		return endpoint;
	}

	public void setEndpoint(final Endpoint endpoint) {
		this.endpoint = endpoint;
	}

	public EndpointReferenceBuilder<Endpoint> getEprBuilder() {
		return eprBuilder;
	}

	public void setEprBuilder(final EndpointReferenceBuilder<Endpoint> eprBuilder) {
		this.eprBuilder = eprBuilder;
	}

}
