package eu.dnetlib.data.collective.harvest;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

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.interfaces.nh.IBlackboardMessage.Action;
import eu.dnetlib.data.collective.harvest.mapper.IngestionMapperFactory;
import eu.dnetlib.data.collective.harvest.provider.DataProvider;
import eu.dnetlib.data.collective.harvest.provider.DataProviderFactory;
import eu.dnetlib.enabling.resultset.MappedResultSetFactory;
import eu.dnetlib.enabling.tools.blackboard.AbstractBlackboardNotificationHandler;
import eu.dnetlib.enabling.tools.blackboard.BlackboardJob;
import eu.dnetlib.enabling.tools.blackboard.BlackboardServerHandler;
import eu.dnetlib.enabling.tools.blackboard.NotificationHandler;
import eu.dnetlib.miscutils.functional.UnaryFunction;

/**
 * 
 * @author claudio
 * 
 */
public class GenericHarvesterNotificationHandler extends AbstractBlackboardNotificationHandler<BlackboardServerHandler> implements NotificationHandler {

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

	/**
	 * factory bean used to generate the internal epr.
	 */
	private BlackboardIterableResultsetFactory resultsetFactory;

	/**
	 * factory bean used to generate the final epr.
	 */
	private MappedResultSetFactory mappedResultSetFactory;

	/**
	 * factory bean for ingestion transformations.
	 */
	private IngestionMapperFactory ingestionMapperFactory;

	/**
	 * Factory bean for data providers.
	 */
	private DataProviderFactory dataProviderFactory;

	@Override
	protected void processJob(BlackboardJob job) {

		try {
			Action action = Action.valueOf(job.getAction());
			switch (action) {

			case LISTRECORDS:
				performListrecords(job);
				break;

			case CANCEL:
				log.info("job interrubped by user action");
				getBlackboardHandler().failed(job, new InterruptedException("job interrubped by user action"));
				break;

			case EXTRACT:
				performExtractFulltext(job);
				break;

			default:
				job.setError("unsupported Action: " + job.getAction());
				log.warn("unsupported message action: " + action.name());
				throw new IllegalArgumentException("unsupported message action: " + action.name());
			}
		} catch (final Throwable e) {
			log.error("failed action " + job.getAction(), e);
			getBlackboardHandler().failed(job, e);
			return;
		}
	}

	private void performExtractFulltext(BlackboardJob job) {
		final String baseUrl = job.getParameters().get("base_url");
		final String repoId = job.getParameters().get("repository_id");
		final String username = job.getParameters().get("username");
		final String password = job.getParameters().get("password");
		//final String filter = job.getParameters().get("filter");

		final Map<String, String> params = new HashMap<String, String>();
		params.put("repoId", repoId);

		try {

			final DataProvider<String> dataProvider = getDataProviderFactory().newFulltextProviderInstance(baseUrl);

			if (username != null) {
				dataProvider.setUsername(username);
				dataProvider.setPassword(password);
			}

			final W3CEndpointReference epr = resultsetFactory.createBlackboardIterableResultset(dataProvider, job);

			job.getParameters().put("resultset_epr", epr.toString());

			getBlackboardHandler().ongoing(job);

			log.info("EXTRACT FULLTEXT job set to ONGOING");

		} catch (Throwable e) {
			getBlackboardHandler().failed(job, e);
			log.fatal("couldn't parse base_url: " + baseUrl, e);
		}
	}

	/**
	 * performs the LISTRECORS operation
	 * 
	 * @param job
	 *            the job
	 * @throws MalformedURLException
	 * @throws NullPointerException
	 * @throws IOException
	 * @throws URISyntaxException
	 */
	private void performListrecords(BlackboardJob job) throws MalformedURLException {
		final String baseUrl = job.getParameters().get("base_url");
		final String repoId = job.getParameters().get("repository_id");
		final String username = job.getParameters().get("username");
		final String password = job.getParameters().get("password");
		final String filters = job.getParameters().get("filter");
		final String namespacePrefix = job.getParameters().get("namespacePrefix");

		final String inputFormat = job.getParameters().get("inputFormat");
		log.info("inputFormat value before creating the mapped rs: " + inputFormat);

		final Map<String, String> params = new HashMap<String, String>();
		params.put("repoId", repoId);
		params.put("inputFormat", inputFormat);
		if (namespacePrefix != null) {
			params.put("namespacePrefix", namespacePrefix);
		}

		try {

			final DataProvider<String> dataProvider = getDataProviderFactory().newDataProviderInstance(baseUrl);

			if (username != null) {
				dataProvider.setUsername(username);
				dataProvider.setPassword(password);
			}

			if (filters != null) {
				dataProvider.setFtpFileFilter(filters);
			}

			final W3CEndpointReference bbEpr = resultsetFactory.createBlackboardIterableResultset(dataProvider, job);

			final UnaryFunction<String, String> mapper = getIngestionMapperFactory().newInstance(params);

			W3CEndpointReference epr = mappedResultSetFactory.createMappedResultSet(bbEpr, mapper);

			job.getParameters().put("rs_epr", epr.toString());

			getBlackboardHandler().ongoing(job);

			log.info("LISTRECORDS job set to ONGOING");

		} catch (Throwable e) {
			getBlackboardHandler().failed(job, e);
			log.fatal("couldn't parse base_url: " + baseUrl, e);
		}
	}

	///////////////////////// setters and getters

	@Required
	public void setResultsetFactory(BlackboardIterableResultsetFactory resultsetFactory) {
		this.resultsetFactory = resultsetFactory;
	}

	public BlackboardIterableResultsetFactory getResultsetFactory() {
		return resultsetFactory;
	}

	@Required
	public void setMappedResultSetFactory(MappedResultSetFactory mappedResultSetFactory) {
		this.mappedResultSetFactory = mappedResultSetFactory;
	}

	public MappedResultSetFactory getMappedResultSetFactory() {
		return mappedResultSetFactory;
	}

	@Required
	public void setIngestionMapperFactory(IngestionMapperFactory ingestionMapperFactory) {
		this.ingestionMapperFactory = ingestionMapperFactory;
	}

	public IngestionMapperFactory getIngestionMapperFactory() {
		return ingestionMapperFactory;
	}

	public DataProviderFactory getDataProviderFactory() {
		return dataProviderFactory;
	}

	@Required
	public void setDataProviderFactory(DataProviderFactory dataProviderFactory) {
		this.dataProviderFactory = dataProviderFactory;
	}

}
