package eu.dnetlib.enabling.tools.blackboard;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.xml.bind.JAXBException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.springframework.beans.factory.annotation.Required;
import org.w3c.dom.Element;

import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.miscutils.jaxb.JaxbFactory;

/**
 * Blackboard handler implementation.
 * 
 * @author marko
 * 
 */
public class BlackboardHandlerImpl implements BlackboardServerHandler {

	/**
	 * blackboard message factory.
	 */
	private JaxbFactory<BlackboardMessage> messageFactory;

	/**
	 * service locator.
	 */
	private UniqueServiceLocator serviceLocator;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardHandler#getJob(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	@Override
	public BlackboardJob getJob(final OpaqueResource profile) {

		final XPath xpa = XPathFactory.newInstance().newXPath();

		try {
			final Element source = (Element) xpa.evaluate("/RESOURCE_PROFILE/BODY/BLACKBOARD/MESSAGE[@id = /RESOURCE_PROFILE/BODY/BLACKBOARD/LAST_REQUEST]",
					profile.asDom(), XPathConstants.NODE);

			if (source == null) { throw new IllegalStateException("cannot find last blackboard message in the service profile"); }

			return new BlackboardJobImpl(profile.getResourceId(), messageFactory.parse(new DOMSource(source)));
		} catch (final JAXBException e) {
			throw new IllegalStateException("cannot parse blackboard message", e);
		} catch (final XPathExpressionException e) {
			throw new IllegalStateException("cannot find last blackboard message in the service profile", e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardHandler#done(eu.dnetlib.enabling.tools.blackboard.BlackboardJob)
	 */
	@Override
	public void done(final BlackboardJob job) {
		job.setActionStatus(ActionStatus.DONE);
		replyJob(job);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardHandler#failed(eu.dnetlib.enabling.tools.blackboard.BlackboardJob,
	 *      java.lang.Exception)
	 */
	@Override
	public void failed(final BlackboardJob job, final Throwable exception) {
		job.setActionStatus(ActionStatus.FAILED);
		final StringWriter stackTrace = new StringWriter();
		exception.printStackTrace(new PrintWriter(stackTrace));
		job.getParameters().put("error", exception.toString());
		job.getParameters().put("errorDetails", stackTrace.toString());
		replyJob(job);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardHandler#ongoing(eu.dnetlib.enabling.tools.blackboard.BlackboardJob)
	 */
	@Override
	public void ongoing(final BlackboardJob job) {
		job.setActionStatus(ActionStatus.ONGOING);
		replyJob(job);
	}

	/**
	 * Internal helper method which replies a blackboard job.
	 * 
	 * @param job
	 *            blackboard job
	 */
	protected void replyJob(final BlackboardJob job) {
		try {
			serviceLocator.getService(ISRegistryService.class).replyBlackBoardMessage(job.getServiceId(), messageFactory.serialize(job.getMessage()));
		} catch (final ISRegistryException e) {
			throw new IllegalStateException("cannot reply the blackboard message", e);
		} catch (final JAXBException e) {
			throw new IllegalArgumentException("cannot serialize blackboard message", e);
		}
	}

	public JaxbFactory<BlackboardMessage> getMessageFactory() {
		return messageFactory;
	}

	public void setMessageFactory(final JaxbFactory<BlackboardMessage> messageFactory) {
		this.messageFactory = messageFactory;
	}

	public UniqueServiceLocator getServiceLocator() {
		return serviceLocator;
	}

	@Required
	public void setServiceLocator(final UniqueServiceLocator serviceLocator) {
		this.serviceLocator = serviceLocator;
	}

}
