/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.event;

import java.util.Iterator;

import org.apache.logging.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.core.Utils;

/**
 * BasicDispatcher implements the primary task of a Dispatcher: it delivers a
 * filtered list of events, synchronously, to a configured list of consumers. It
 * may be extended for more elaborate behavior.
 *
 * @version $Revision$
 */
public class BasicDispatcher extends Dispatcher {

    public BasicDispatcher(String name) {
        super(name);
    }

    /**
     * log4j category
     */
    private static Logger log = org.apache.logging.log4j.LogManager.getLogger(BasicDispatcher.class);

    @Override
    public void addConsumerProfile(ConsumerProfile cp)
        throws IllegalArgumentException {
        if (consumers.containsKey(cp.getName())) {
            throw new IllegalArgumentException(
                "This dispatcher already has a consumer named \""
                    + cp.getName() + "\"");
        }

        consumers.put(cp.getName(), cp);

        if (log.isDebugEnabled()) {
            int n = 0;
            for (Iterator i = cp.getFilters().iterator(); i.hasNext(); ++n) {
                int f[] = (int[]) i.next();
                log.debug("Adding Consumer=\"" + cp.getName() + "\", instance="
                              + cp.getConsumer().toString() + ", filter["
                              + String.valueOf(n) + "]=(ObjMask="
                              + String.valueOf(f[Event.SUBJECT_MASK])
                              + ", EventMask=" + String.valueOf(f[Event.EVENT_MASK])
                              + ")");
            }
        }
    }

    /**
     * Dispatch all events added to this Context according to configured
     * consumers.
     *
     * @param ctx the execution context
     */
    @Override
    public void dispatch(Context ctx) {
        if (!consumers.isEmpty()) {

            if (!ctx.hasEvents()) {
                return;
            }

            if (log.isDebugEnabled()) {
                log.debug("Processing queue of "
                              + String.valueOf(ctx.getEvents().size()) + " events.");
            }

            // transaction identifier applies to all events created in
            // this context for the current transaction. Prefix it with
            // some letters so RDF readers don't mistake it for an integer.
            String tid = "TX" + Utils.generateKey();

            while (ctx.hasEvents()) {
                Event event = ctx.pollEvent();
                event.setDispatcher(getIdentifier());
                event.setTransactionID(tid);

                if (log.isDebugEnabled()) {
                    log.debug("Iterating over "
                                  + String.valueOf(consumers.values().size())
                                  + " consumers...");
                }

                for (Iterator ci = consumers.values().iterator(); ci.hasNext(); ) {
                    ConsumerProfile cp = (ConsumerProfile) ci.next();

                    if (event.pass(cp.getFilters())) {
                        if (log.isDebugEnabled()) {
                            log.debug("Sending event to \"" + cp.getName()
                                          + "\": " + event.toString());
                        }

                        try {
                            cp.getConsumer().consume(ctx, event);

                            // Record that the event has been consumed by this
                            // consumer
                            event.setBitSet(cp.getName());
                        } catch (Exception e) {
                            log.error("Consumer(\"" + cp.getName()
                                          + "\").consume threw: " + e.toString(), e);
                        }
                    }

                }
            }

            // Call end on the consumers that got synchronous events.
            for (Iterator ci = consumers.values().iterator(); ci.hasNext(); ) {
                ConsumerProfile cp = (ConsumerProfile) ci.next();
                if (cp != null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Calling end for consumer \"" + cp.getName()
                                      + "\"");
                    }

                    try {
                        cp.getConsumer().end(ctx);
                    } catch (Exception e) {
                        log.error("Error in Consumer(\"" + cp.getName()
                                      + "\").end: " + e.toString(), e);
                    }
                }
            }
        }
    }

}
