package eu.dnetlib.enabling.tools.blackboard;

import java.util.Map;

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.enabling.tools.UniqueIdentifierGenerator;
import eu.dnetlib.enabling.tools.UniqueIdentifierGeneratorImpl;
import eu.dnetlib.miscutils.jaxb.JaxbFactory;

/**
 * Blackboard client.
 * 
 * @author marko
 * 
 */
public class BlackboardClientHandlerImpl implements BlackboardClientHandler {

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

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

	/**
	 * generates blackboard message identifiers for new jobs.
	 */
	private UniqueIdentifierGenerator uuidGenerator = new UniqueIdentifierGeneratorImpl("bb-");

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardClientHandler#assign(eu.dnetlib.enabling.tools.blackboard.BlackboardJob)
	 */
	@Override
	public void assign(final BlackboardJob job) {
		checkJob(job);

		try {
			serviceLocator.getService(ISRegistryService.class).addBlackBoardMessage(job.getServiceId(), job.getId(),
					messageFactory.serialize(job.getMessage()));
		} catch (final ISRegistryException e) {
			throw new IllegalStateException("cannot register blackboard message", e);
		} catch (final JAXBException e) {
			throw new IllegalArgumentException("cannot serialize blackboard message", e);
		}
	}

	/**
	 * Check that the job has sane values.
	 * 
	 * @param job
	 */
	protected void checkJob(final BlackboardJob job) {
		for (Map.Entry<String, String> param : job.getParameters().entrySet()) {
			if (param.getValue() == null) { throw new IllegalStateException("job parameter value cannot be null: " + param.getKey()); }
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardClientHandler#delete(eu.dnetlib.enabling.tools.blackboard.BlackboardJob)
	 */
	@Override
	public void delete(final BlackboardJob job) {
		try {
			serviceLocator.getService(ISRegistryService.class).deleteBlackBoardMessage(job.getServiceId(), job.getId());
		} catch (final ISRegistryException e) {
			throw new IllegalStateException("cannot delete blackboard message", e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardClientHandler#newJob()
	 */
	@Override
	public BlackboardJob newJob(final String serviceId) {
		final BlackboardJob job = new BlackboardJobImpl(serviceId, messageFactory.newInstance());
		job.setActionStatus(ActionStatus.ASSIGNED);
		job.getParameters().put("id", "");
		job.getParameters().put("error", "");

		job.setId(uuidGenerator.generateIdentifier());
		return job;
	}

	/**
	 * {@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_RESPONSE]",
					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);
		}
	}

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

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

	public UniqueIdentifierGenerator getUuidGenerator() {
		return uuidGenerator;
	}

	public void setUuidGenerator(final UniqueIdentifierGenerator uuidGenerator) {
		this.uuidGenerator = uuidGenerator;
	}

	public UniqueServiceLocator getServiceLocator() {
		return serviceLocator;
	}

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

}
