package eu.dnetlib.enabling.tools.blackboard;

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.w3c.dom.Element;

import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.ServiceLocator;
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;

	/**
	 * registry locator.
	 */
	private ServiceLocator<ISRegistryService> registryLocator;

	/**
	 * 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)
	 */
	public void assign(final BlackboardJob job) {
		try {
			final BlackboardJobImpl impl = (BlackboardJobImpl) job;
			registryLocator.getService().addBlackBoardMessage(impl.getServiceId(), job.getId(), messageFactory.serialize(impl.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);
		}
	}


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

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.enabling.tools.blackboard.BlackboardClientHandler#newJob()
	 */
	public BlackboardJob newJob(final String serviceId) {
		final BlackboardJobImpl 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)
	 */
	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 ServiceLocator<ISRegistryService> getRegistryLocator() {
		return registryLocator;
	}


	public void setRegistryLocator(final ServiceLocator<ISRegistryService> registryLocator) {
		this.registryLocator = registryLocator;
	}


	public UniqueIdentifierGenerator getUuidGenerator() {
		return uuidGenerator;
	}


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

}
