package at.oefai.aaa.agent.jam;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * keeps all the recent appraisals read for regular updating and insertion of new ones.
 * @author Stefan Rank
 */
class AppraisalRegister {
    private final AgentLogger log;
    private final Interpreter interpreter;
    private Map<String,ActionAppraisal> actionMap = new HashMap<String,ActionAppraisal>();
    private Map<String,EventAppraisal> eventMap = new HashMap<String,EventAppraisal>();
    private Map<String,ObjectAppraisal> objectMap = new HashMap<String,ObjectAppraisal>();


    AppraisalRegister(final Interpreter pInterpreter) {
        this.log = pInterpreter.getLog();
        this.interpreter = pInterpreter;
    }

    // updates an already existing match or creates a new one
    void registerGoalsMatch(final float relevanceValue, final WorldModelRelation wmr) {
        // search if there is already an appraisal of this wmr-goal combination
        Appraisal oldApp = getEventAppraisal(wmr);
        if (oldApp != null) {
            // update the appraisal
            oldApp.update(relevanceValue);
            //log.info("updated  EVENTAPPRAISAL:\n" + oldApp.verboseString());
        } else {
            // create a new one, fully calculated attributes
            EventAppraisal newApp = EventAppraisal.newEventAppraisal(relevanceValue, wmr, this.interpreter);
            putEventAppraisal(newApp);
            this.interpreter.getMetaData().saveEmotion(newApp);
            if (this.log.getShowAppraisal()) { this.log.info("inserted EVENTAPPRAISAL:\n" + newApp.verboseString()); }
        }
    }


    // updates an already existing match or creates a new one
    void registerStandardsMatch(final float standardValue, final WorldModelRelation wmr) {
        // search if there is already an appraisal of this wmr-goal combination
        Appraisal oldApp = getActionAppraisal(wmr);
        if (oldApp != null) {
            // update the appraisal
            oldApp.update(standardValue);
            //log.info("updated  ACTIONAPPRAISAL:\n" + oldApp.verboseString());
        } else {
            // create a new one, fully calculated attributes
            ActionAppraisal newApp = ActionAppraisal.newActionAppraisal(standardValue, wmr, this.interpreter);
            putActionAppraisal(newApp);
            this.interpreter.getMetaData().saveEmotion(newApp);
            if (this.log.getShowAppraisal()) { this.log.info("inserted ACTIONAPPRAISAL:\n" + newApp.verboseString()); }
        }
    }


    // updates an already existing match or creates a new one
    void registerObjectMatch(final float prefValue, final WorldModelRelation wmr) {
        // search if there is already an appraisal of this wmr-goal combination
        Appraisal oldApp = getObjectAppraisal(wmr);
        if (oldApp != null) {
            // update the appraisal
            oldApp.update(prefValue);
            //log.info("updated  OBJECTAPPRAISAL:\n" + oldApp.verboseString());
        } else {
            // create a new one, fully calculated attributes
            ObjectAppraisal newApp = ObjectAppraisal.newObjectAppraisal(prefValue, wmr, this.interpreter);
            putObjectAppraisal(newApp);
            this.interpreter.getMetaData().saveEmotion(newApp);
            if (this.log.getShowAppraisal()) { this.log.info("inserted OBJECTAPPRAISAL:\n" + newApp.verboseString()); }
        }
    }


    private EventAppraisal getEventAppraisal(final Relation w) {
        return this.eventMap.get(w.simpleString(null));
    }

    private ActionAppraisal getActionAppraisal(final Relation w) {
        return this.actionMap.get(w.simpleString(null));
    }

    private ObjectAppraisal getObjectAppraisal(final Relation w) {
        return this.objectMap.get(w.getPreferenceName(null));
    }



    private void putEventAppraisal(final EventAppraisal app) {
        WorldModelRelation w = app.getRelation();
        this.supercedeAppraisals(w, app.getIntensity());
        this.eventMap.put(w.simpleString(null), app);
    }

    private void putActionAppraisal(final ActionAppraisal app) {
        WorldModelRelation w = app.getRelation();
        this.supercedeAppraisals(w, app.getIntensity());
        this.actionMap.put(w.simpleString(null), app);
    }

    private void putObjectAppraisal(final ObjectAppraisal app) {
        String objectname = app.getRelation().getPreferenceName(null);
        // object appraisals cannot supercede others
        this.objectMap.put(objectname, app);
    }

    /**
     * Removes any prospective appraisals that are superceded by the app of the relation.
     * @param intensitythreshold only remove lower intensity appraisals (a previous prospect-
     *                           appraisal that'S less intense is very unlikely, nevertheless...)
     */
    private void supercedeAppraisals(final Relation wmr, final float intensitythreshold) {
        Relation r = wmr.getCorrespondingProspect();
        if (r != null) {
            EventAppraisal eapp = this.getEventAppraisal(r);
            if (eapp != null) {
                if (eapp.getIntensity() < intensitythreshold) {
                    eapp.removeCoping();
                    this.log.info("New Appraisal of " + wmr.simpleString(null) + " supercedes Appraisal "
                                  + eapp.toString());
                }
            }
        }
    }

    public String verboseString() {
        StringBuffer sb = new StringBuffer("AppraisalRegister:\n");
        sb.append("    EventAppraisals:\n");
        for (Appraisal app : this.eventMap.values()) {
            sb.append(app.verboseString() + "\n");
        }
        sb.append("    ActionAppraisals:\n");
        for (Appraisal app : this.actionMap.values()) {
            sb.append(app.verboseString() + "\n");
        }
        return sb.toString();
    }


    // update all effects and remove too old ones...
    void updateEffects() {
        Appraisal app;
        Iterator<? extends Appraisal> mapIter = this.eventMap.values().iterator(); // needed for .remove()
        while (mapIter.hasNext()) {
            app = mapIter.next();
            if (! app.updateEffects()) {
                if (this.log.getShowAppraisal()) { this.log.info("removing old: " + app); }
                app.removeCoping();
                mapIter.remove();
            }
        }
        mapIter = this.actionMap.values().iterator(); // needed for .remove()
        while (mapIter.hasNext()) {
            app = mapIter.next();
            if (! app.updateEffects()) {
                if (this.log.getShowAppraisal()) { this.log.info("removing old: " + app); }
                app.removeCoping();
                mapIter.remove();
            }
        }
        mapIter = this.objectMap.values().iterator(); // needed for .remove()
        while (mapIter.hasNext()) {
            app = mapIter.next();
            if (! app.updateEffects()) {
                if (this.log.getShowAppraisal()) { this.log.info("removing old: " + app); }
                app.removeCoping();
                mapIter.remove();
            }
        }
    }
}
