package eu.dnetlib.enabling.resultset;

import java.util.List;

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

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

import eu.dnetlib.common.rmi.UnimplementedException;
import eu.dnetlib.enabling.resultset.push.PushResultSet;
import eu.dnetlib.enabling.resultset.push.PushResultSetFactory;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.enabling.tools.AbstractBaseService;
import eu.dnetlib.soap.EndpointReferenceBuilder;

/**
 * This component dispatches the service method calls to stateful datastructure instances and to the singleton core.
 * 
 * @author marko
 * 
 */
@WebService(targetNamespace = "http://services.dnetlib.eu/")
public class ResultSetServiceImpl extends AbstractBaseService implements ResultSetService {

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

	/**
	 * Maps resultset identifiers to resultsets and manages expiration etc.
	 */
	private ResultSetRegistry resultsetRegistry;

	/**
	 * push resultset factory.
	 */
	private PushResultSetFactory pushFactory;

	/**
	 * Service endpoint.
	 */
	private Endpoint endpoint;

	/**
	 * injected epr builder.
	 */
	private EndpointReferenceBuilder<Endpoint> eprBuilder;

	/**
	 * custom resultset properties.
	 */
	private ResultSetPropertyDao customPropertyDao;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#closeRS(java.lang.String)
	 */
	@Override
	public void closeRS(final String rsId) {
		try {
			final ResultSet resultSet = getResultSetById(rsId);
			resultSet.close();
		} catch (ResultSetException e) {
			log.warn("should throw checked exception but wasn't declared in cnr-rmi-api", e);
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPullRS(java.lang.String, java.lang.String, int, int, java.lang.String,
	 *      java.lang.Integer, java.lang.Integer)
	 */
	@Override
	public W3CEndpointReference createPullRS(final String providerAddress,
			final String bdId,
			final int initialPageSize,
			final int expiryTime,
			final String styleSheet,
			final Integer keepAliveTime,
			final Integer total) {
		throw new UnimplementedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPullRSEPR(javax.xml.ws.wsaddressing.W3CEndpointReference,
	 *      java.lang.String, int, int, java.lang.String, java.lang.Integer, java.lang.Integer)
	 */
	@Override
	public W3CEndpointReference createPullRSEPR(final W3CEndpointReference dataProviderEPR,
			final String bdId,
			final int initialPageSize,
			final int expiryTime,
			final String styleSheet,
			final Integer keepAliveTime,
			final Integer total) {
		throw new UnimplementedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getNumberOfElements(java.lang.String)
	 */
	@Override
	public int getNumberOfElements(final String rsId) throws ResultSetException {
		final ResultSet resultSet = getResultSetById(rsId); // NOPMD
		return resultSet.getNumberOfResults();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getResult(java.lang.String, int, int, java.lang.String)
	 */
	@Override
	public List<String> getResult(final String rsId, final int fromPosition, final int toPosition, final String requestMode) throws ResultSetException {
		final ResultSet resultSet = getResultSetById(rsId); // NOPMD
		return resultSet.getResults(fromPosition, toPosition);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPushRS(int, int)
	 */
	@Override
	public W3CEndpointReference createPushRS(final int expiryTime, final int keepAliveTime) throws ResultSetException {
		return pushFactory.createPushResultSet(expiryTime);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getProperty(java.lang.String, java.lang.String)
	 */
	@Override
	public String getProperty(final String rsId, final String name) throws ResultSetException {
		final ResultSet resultSet = getResultSetById(rsId); // NOPMD

		if ("rsId".equals(name)) return resultSet.getIdentifier();
		else if ("total".equals(name)) return Integer.toString(resultSet.getNumberOfResults());
		else if ("maxExpiryTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId));
		else if ("expiryTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId));
		else if ("keepAliveTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId));
		else if ("classSimpleName".equals(name)) return resultSet.getClass().getSimpleName();
		// ...

		return customPropertyDao.getProperties(resultSet).get(name);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getRSStatus(java.lang.String)
	 */
	@Override
	public String getRSStatus(final String rsId) throws ResultSetException {
		final ResultSet resultSet = getResultSetById(rsId); // NOPMD
		if (resultSet.isOpen()) return "open";
		return "closed";
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#populateRS(java.lang.String, java.util.List)
	 */
	@Override
	public String populateRS(final String rsId, final List<String> elements) throws ResultSetException {
		final ResultSet resultSet = getResultSetById(rsId); // NOPMD
		if (resultSet instanceof PushResultSet) {
			((PushResultSet) resultSet).addElements(elements);
		} else throw new ResultSetException("ResultSet '" + rsId + "' is not a push resultset");

		return "1";
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.common.rmi.BaseService#start()
	 */
	@Override
	public void start() {
		// TODO Auto-generated method stub

	}

	/**
	 * obtain the resultset object for a given id.
	 * 
	 * @param rsId
	 *            resultset id
	 * @return resultset object, never null
	 * @throws ResultSetException
	 *             thrown when the id doesn't exist;
	 */
	private ResultSet getResultSetById(final String rsId) throws ResultSetException {
		final ResultSet resultSet = resultsetRegistry.getResultSetById(rsId); // NOPMD
		if (resultSet == null) throw new ResultSetException("resultset with id '" + rsId + "' doesn't exist");
		return resultSet;
	}

	public ResultSetRegistry getResultsetRegistry() {
		return resultsetRegistry;
	}

	@Required
	public void setResultsetRegistry(final ResultSetRegistry resultsetRegistry) {
		this.resultsetRegistry = resultsetRegistry;
	}

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

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

	public Endpoint getEndpoint() {
		return endpoint;
	}

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

	public PushResultSetFactory getPushFactory() {
		return pushFactory;
	}

	@Required
	public void setPushFactory(final PushResultSetFactory pushFactory) {
		this.pushFactory = pushFactory;
	}

	public ResultSetPropertyDao getCustomPropertyDao() {
		return customPropertyDao;
	}

	public void setCustomPropertyDao(final ResultSetPropertyDao customPropertyDao) {
		this.customPropertyDao = customPropertyDao;
	}

}
