package eu.dnetlib.xml.database.exist;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import eu.dnetlib.miscutils.functional.xml.IndentXmlString;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.exist.collections.Collection;
import org.exist.collections.triggers.SAXTrigger;
import org.exist.collections.triggers.TriggerException;
import org.exist.dom.persistent.DocumentImpl;
import org.exist.storage.DBBroker;
import org.exist.storage.txn.Txn;
import org.exist.xmldb.XmldbURI;

import eu.dnetlib.xml.database.Trigger;

/**
 * this trigger delegates diffs for CRUDE events to an non-exist dependent eu.dnetlib.xml.database.Trigger instance.
 *
 * Since eXist triggers are instantiated by eXist, we need to locate the Trigger instances which are registered to the database instance.
 *
 * @author marko
 *
 */
public class DelegatingDiffTrigger extends SAXTrigger {

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

	/**
	 * trigger identifier, from configuration.
	 */
	private String triggerName;

	private Map<String, ExistsTriggerEvent> existsTriggerEventMap = new HashMap<>();

	/**
	 * {@inheritDoc}
	 *
	 * @see org.exist.collections.triggers.FilteringTrigger#configure(org.exist.storage.DBBroker, org.exist.collections.Collection,
	 *      java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void configure(final DBBroker dbBroker, final Collection parent, final Map<String, List<?>> parameters) throws TriggerException {
		super.configure(dbBroker, parent, parameters);

		if (parameters == null) { return; }

		final Map<String, List<?>> params = parameters;
		if (parameters.containsKey("triggerName")) {
			final List<?> myTriggerName = parameters.get("triggerName");
			if ((myTriggerName != null) && (myTriggerName.size() == 1)) {
				setTriggerName(myTriggerName.get(0).toString());
			}
		}
		if (getTriggerName() == null) {
			log.fatal("trigger id not configured");
			throw new TriggerException("trigger name not configured");
		}
	}

	/**
	 * find the trigger instance associated with this delegation.
	 *
	 * @return trigger instance
	 */
	protected Trigger getTrigger() {
		final Trigger trigger = ExistTriggerRegistry.defaultInstance().getTrigger(getTriggerName());
		if (trigger == null) {
			log.fatal("no trigger " + triggerName + " but there is a registered callback for it");
			return null;
		}

		return trigger;
	}

	public String getTriggerName() {
		return triggerName;
	}

	public void setTriggerName(final String triggerId) {
		triggerName = triggerId;
	}

	@Override
	public void beforeCreateDocument(final DBBroker dbBroker, final Txn txn, final XmldbURI xmldbURI) throws TriggerException {

	}

	@Override
	public void afterCreateDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

		try {
			final Trigger trigger = getTrigger();
			final String collection = document.getCollection().getURI().toString();
			final String fileName = document.getFileURI().toString();
			if (trigger != null) {
				trigger.created(fileName, collection, document);
			}
		} catch (final Exception e) {
			throw new TriggerException(e);
		}
	}

	@Override
	public void beforeUpdateDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

		try {
			final String collection = document.getCollection().getURI().toString();
			final String fileName = document.getFileURI().toString();
			final ExistsTriggerEvent event = new ExistsTriggerEvent();
			event.setOldDocument(IndentXmlString.apply(document));
			event.setEventType(EventType.UPDATE);
			event.setCollection(collection);
			event.setName(fileName);
			existsTriggerEventMap.put(String.valueOf(txn.getId()), event);
		} catch (final Exception e) {
			throw new TriggerException(e);
		}
	}

	@Override
	public void afterUpdateDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

		try {
			if (!existsTriggerEventMap.containsKey(
					String.valueOf(txn.getId()))) { throw new TriggerException("Error on trigger missing previous operation beforeUpdateTrigger"); }
			final ExistsTriggerEvent existsTriggerEvent = existsTriggerEventMap.remove(String.valueOf(txn.getId()));

			existsTriggerEvent.setDocument(IndentXmlString.apply(document));
			final Trigger trigger = getTrigger();
			if (trigger != null) {
				trigger.updated(existsTriggerEvent.getName(), existsTriggerEvent.getCollection(), existsTriggerEvent.getOldDocument(),
						existsTriggerEvent.getDocument());
			}
		} catch (final Exception e) {
			throw new TriggerException(e);
		}
	}

	@Override
	public void beforeUpdateDocumentMetadata(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

	}

	@Override
	public void afterUpdateDocumentMetadata(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

	}

	@Override
	public void beforeCopyDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document, final XmldbURI xmldbURI) throws TriggerException {

	}

	@Override
	public void afterCopyDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document, final XmldbURI xmldbURI) throws TriggerException {

	}

	@Override
	public void beforeMoveDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document, final XmldbURI xmldbURI) throws TriggerException {

	}

	@Override
	public void afterMoveDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document, final XmldbURI xmldbURI) throws TriggerException {

	}

	@Override
	public void beforeDeleteDocument(final DBBroker dbBroker, final Txn txn, final DocumentImpl document) throws TriggerException {

		try {
			final Trigger trigger = getTrigger();
			final String collection = document.getCollection().getURI().toString();
			final String fileName = document.getFileURI().toString();
			if (trigger != null) {
				trigger.deleted(fileName, collection, IndentXmlString.apply(document));
			}
		} catch (final Exception e) {
			throw new TriggerException(e);
		}
	}

	@Override
	public void afterDeleteDocument(final DBBroker dbBroker, final Txn txn, final XmldbURI xmldbURI) throws TriggerException {

	}

}
