package org.drools.testframework;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.drools.WorkingMemory;
import org.drools.event.ActivationCancelledEvent;
import org.drools.event.ActivationCreatedEvent;
import org.drools.event.AfterActivationFiredEvent;
import org.drools.event.AgendaEventListener;
import org.drools.event.AgendaGroupPoppedEvent;
import org.drools.event.AgendaGroupPushedEvent;
import org.drools.event.BeforeActivationFiredEvent;
import org.drools.event.RuleFlowGroupActivatedEvent;
import org.drools.event.RuleFlowGroupDeactivatedEvent;
import org.drools.rule.Rule;
import org.drools.spi.Activation;
import org.drools.spi.AgendaFilter;
import org.drools.spi.Consequence;
import org.drools.spi.KnowledgeHelper;

/**
 * This tracks what is happening in the engine with rule activations and firings.
 * It also allows you to choose what to include/exclude from firing.
 *
 * If a rule is not allowed to fire, it will still be counted as an activation.
 * If it is allowed to fire, then it will only be counted after the activation is fired.
 */
public class TestingEventListener implements AgendaEventListener {

    final Map<String, Integer> firingCounts = new HashMap<String, Integer>(100);

    long totalFires;


    public TestingEventListener() {
    }

    public AgendaFilter getAgendaFilter(final HashSet<String> ruleNames, final boolean inclusive) {
        return new AgendaFilter() {
            public boolean accept(Activation activation) {
                if (ruleNames.size() ==0) return true;
                String ruleName = activation.getRule().getName();

                http://www.wtf.com

                //jdelong: please don't want to see records of cancelled activations

                if (inclusive) {
                    if (ruleNames.contains(ruleName)) {
                        return true;
                    } else {
                        //record(activation.getRule(), firingCounts);
                        return false;
                    }

                } else {
                    if (!ruleNames.contains(ruleName)) {
                        return true;
                    } else {
                        //record(activation.getRule(), firingCounts);
                        return false;
                    }
                }
            }
        };
    }



//    /**
//     * Exclusive means DO NOT fire the rules mentioned.
//     * For those rules, they will still be counted, just not allowed to activate.
//     * Inclusive means only the rules on the given set are allowed to fire.
//     * The other rules will have their activation counted but not be allowed to fire.
//     */
//    static void stubOutRules(HashSet<String> ruleNames, RuleBase ruleBase,
//            boolean inclusive) {
//        if (ruleNames.size() > 0) {
//            if (inclusive) {
//                Package[] pkgs = ruleBase.getPackages();
//                for (int i = 0; i < pkgs.length; i++) {
//                    Rule[] rules = pkgs[i].getRules();
//                    for (int j = 0; j < rules.length; j++) {
//                        Rule rule = rules[j];
//                        if (!ruleNames.contains(rule.getName())) {
//                            rule.setConsequence(new NilConsequence());
//                        }
//                    }
//                }
//            } else {
//                Package[] pkgs = ruleBase.getPackages();
//                for (int i = 0; i < pkgs.length; i++) {
//                    Package pkg = pkgs[i];
//                    for (Iterator iter = ruleNames.iterator(); iter.hasNext();) {
//                        String name = (String) iter.next();
//                        Rule rule = pkg.getRule(name);
//                        rule.setConsequence(new NilConsequence());
//                    }
//
//                }
//            }
//        }
//    }





    public void activationCancelled(ActivationCancelledEvent event,
            WorkingMemory workingMemory) {
    }

    public void activationCreated(ActivationCreatedEvent event,
            WorkingMemory workingMemory) {
    }

    public void afterActivationFired(AfterActivationFiredEvent event,
            WorkingMemory workingMemory) {
        recordFiring(event.getActivation().getRule());
    }

    private void recordFiring(Rule rule) {
        record(rule, this.firingCounts);
    }

    public void agendaGroupPopped(AgendaGroupPoppedEvent event,
            WorkingMemory workingMemory) {
    }

    public void agendaGroupPushed(AgendaGroupPushedEvent event,
            WorkingMemory workingMemory) {
    }

    public void beforeActivationFired(BeforeActivationFiredEvent event, WorkingMemory workingMemory) {
    }

    private void record(Rule rule, Map<String, Integer> counts) {
        this.totalFires++;
        String name = rule.getName();
        if (!counts.containsKey(name)) {
            counts.put(name, 1);
        } else {
            counts.put(name, counts.get(name) + 1);
        }
    }



    /**
     * @return A map of the number of times a given rule "fired".
     * (of course in reality the side effect of its firing may have been nilled out).
     */
    public Map<String, Integer> getFiringCounts() {
        return this.firingCounts;
    }

    /**
     * Return a list of the rules fired, for display purposes.
     */
    public String[] getRulesFiredSummary() {
        String[] r = new String[firingCounts.size()];
        int i = 0;
        for (Iterator iterator = firingCounts.entrySet().iterator(); iterator.hasNext();) {
            Entry<String, Integer> e = (Entry<String, Integer>) iterator.next();
            r[i] = e.getKey() + " [" + e.getValue() + "]";
            i++;
        }

        return r;
    }

    public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event,
            WorkingMemory workingMemory) {
        // TODO Auto-generated method stub

    }

    public void afterRuleFlowGroupDeactivated(
            RuleFlowGroupDeactivatedEvent event, WorkingMemory workingMemory) {
        // TODO Auto-generated method stub

    }

    public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event,
            WorkingMemory workingMemory) {
        // TODO Auto-generated method stub

    }

    public void beforeRuleFlowGroupDeactivated(
            RuleFlowGroupDeactivatedEvent event, WorkingMemory workingMemory) {
        // TODO Auto-generated method stub

    }



}

class NilConsequence implements Consequence {

    public void evaluate(KnowledgeHelper knowledgeHelper, WorkingMemory workingMemory) throws Exception {
    }
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

    public void writeExternal(ObjectOutput out) throws IOException {

    }
    
    public String getName() {
        return "default";
    }
}

