package eu.dnetlib.msro.dispatcher;

import java.util.Map;

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

import com.google.common.collect.Maps;

import eu.dnetlib.common.services.locators.comparators.HandledDatastructuresComparator;
import eu.dnetlib.enabling.blackboard.BlackboardDispatcher;
import eu.dnetlib.enabling.datastructures.MetaWorkflow;
import eu.dnetlib.enabling.datastructures.Workflow;
import eu.dnetlib.enabling.datastructures.WorkflowInstance;
import eu.dnetlib.enabling.datastructures.WorkflowInstance.LastExecutionStatus;
import eu.dnetlib.enabling.is.client.InformationServiceClient;
import eu.dnetlib.miscutils.DateUtils;
import eu.dnetlib.rmi.blackboard.LaunchWorkflowMessage;
import eu.dnetlib.rmi.soap.exceptions.InformationServiceException;
import eu.dnetlib.rmi.soap.exceptions.ManagerServiceException;

public class WorkflowDispatcher {

	@Autowired
	private BlackboardDispatcher blackboardDispatcher;

	@Autowired
	private WorkflowRegistry registry;

	@Autowired
	private InformationServiceClient isClient;

	private static final Log log = LogFactory.getLog(WorkflowDispatcher.class);

	public ProcessInfo dispatchMetaWorkFlow(final String metaWfId) throws ManagerServiceException {
		try {
			final MetaWorkflow metaWf = isClient.getResourceByCode(metaWfId, MetaWorkflow.class);
			final WorkflowInstance wfi = metaWf.getRootWorkflowInstance();
			return dispatchWorkFlow(metaWf, wfi.getWfName());
		} catch (Throwable e) {
			log.error("Error dispatching wf", e);
			throw new ManagerServiceException("Error dispatching wf", e);
		}
	}

	public ProcessInfo dispatchWorkFlow(final String metaWfId, final String wfName) throws ManagerServiceException {
		try {
			return dispatchWorkFlow(isClient.getResourceByCode(metaWfId, MetaWorkflow.class), wfName);
		} catch (Throwable e) {
			log.error("Error dispatching wf", e);
			throw new ManagerServiceException("Error dispatching wf", e);
		}
	}

	public ProcessInfo dispatchWorkFlow(final MetaWorkflow metaWf, final String wfName) throws ManagerServiceException {
		return dispatchWorkFlow(metaWf, metaWf.findWorkflowInstanceByName(wfName));
	}

	public ProcessInfo dispatchWorkFlow(final WorkflowInstance wfi) throws ManagerServiceException {
		final ProcessInfo info = registry.registerProcess(wfi);
		dispatchWorkFlow(wfi, wfi.getParams(), new SimpleLaunchWorkflowCallback(info));
		return info;
	}

	public ProcessInfo dispatchWorkFlow(final MetaWorkflow metaWf, final WorkflowInstance wfi) throws ManagerServiceException {
		final ProcessInfo info = registry.registerProcess(metaWf, wfi);

		final Map<String, String> params = Maps.newHashMap();
		params.putAll(metaWf.getGlobalParams());
		params.putAll(wfi.getParams());

		dispatchWorkFlow(wfi, params, new SimpleLaunchWorkflowCallback(info) {

			@Override
			public void onSuccess(final LaunchWorkflowMessage m) {
				super.onSuccess(m);
				updateMetaWf(metaWf.getCode(), getInfo().getWfName(), LastExecutionStatus.SUCCESS);
				for (WorkflowInstance nextWfi : wfi.getChildren()) {
					try {
						dispatchWorkFlow(metaWf, nextWfi);
					} catch (ManagerServiceException e) {
						log.error("Child Workflow not launched", e);
					}
				}
			}

			@Override
			public void onFail(final LaunchWorkflowMessage m) {
				super.onFail(m);
				updateMetaWf(metaWf.getCode(), getInfo().getWfName(), LastExecutionStatus.FAILED);
			}
		});

		return info;
	}

	private void dispatchWorkFlow(final WorkflowInstance wfi, final Map<String, String> params, final SimpleLaunchWorkflowCallback callback)
			throws ManagerServiceException {
		try {
			final Workflow wf = isClient.getResourceByCode(wfi.getWfCode(), Workflow.class);

			final String sarasvatiXml = SarasvatiUtils.convertToSarasvatiProfile(wf, params);
			if (log.isDebugEnabled()) {
				log.debug("***********************************");
				log.debug(sarasvatiXml);
				log.debug("***********************************");
			}
			final LaunchWorkflowMessage message = callback.getInitialMessage(sarasvatiXml);
			final String workerId = blackboardDispatcher.createDispatcher(message, new HandledDatastructuresComparator(), callback);
			callback.getInfo().setWorkerId(workerId);
		} catch (Throwable e) {
			log.error("Error dispatching wf", e);
			throw new ManagerServiceException("Error dispatching wf", e);
		}
	}

	synchronized private void updateMetaWf(final String metaWfId, final String wfName, final LastExecutionStatus status) {
		try {
			final MetaWorkflow metaWf = isClient.getResourceByCode(metaWfId, MetaWorkflow.class);
			final WorkflowInstance wfi = metaWf.findWorkflowInstanceByName(wfName);
			wfi.setLastExecutionStatus(status);;
			wfi.setLastExecutionDate(DateUtils.now());
			isClient.updateResource(metaWf);
		} catch (InformationServiceException e) {
			log.error("Error updateMetaWf: " + metaWfId);
			throw new RuntimeException("Error updateMetaWf: " + metaWfId, e);
		}
	}
}
