package at.oefai.aaa.agent.jam;

import java.io.Serializable;

import at.oefai.aaa.agent.jam.types.Binding;
import at.oefai.aaa.agent.jam.types.DList;
import at.oefai.aaa.agent.jam.types.Expression;
import at.oefai.aaa.agent.jam.types.SymbolMap;

/**
 * Represents the basic plan within JAM.
 * @author Marc Huber
 * @author Jaeho Lee
 */
public class Plan implements Serializable {

    private String name = null;
    // Required Plan fields
    private GoalAction goalSpecification = null;
    private Relation concludeSpecification = null;
    private Relation perceiveAssertSpecification = null;
    private Relation perceiveRetractSpecification = null;
    private Expression utility = null;
    private PlanSequenceConstruct body = null;
    // Common optional Plan fields
    private PlanContext context = new PlanContext();
    private PlanContext precondition = new PlanContext();
    private String documentation = null;
    private PlanAtomicConstruct failure = null;
    // Optional Plan fields
    private PlanAtomicConstruct effects = null;
    private String attributes = null;
    // Other member fields
    private SymbolMap symbolMap = new SymbolMap();
    private boolean valid = true;

    /** only package level constructor */
    Plan() {
        // package only
    }

    // Member functions
    public String getName() { return this.name; }

    void setName(String planName) { this.name = planName; }

    GoalAction getGoalSpecification() { return this.goalSpecification; }

    void setGoalSpecification(GoalAction p) { this.goalSpecification = p; }

    Relation getConcludeSpecification() { return this.concludeSpecification; }

    void setConcludeSpecification(Relation r) { this.concludeSpecification = r; }

    Relation getPerceiveAssertSpecification() { return this.perceiveAssertSpecification; }

    void setPerceiveAssertSpecification(Relation r) { this.perceiveAssertSpecification = r; }

    Relation getPerceiveRetractSpecification() { return this.perceiveRetractSpecification; }

    void setPerceiveRetractSpecification(Relation r) { this.perceiveRetractSpecification = r; }

    Relation getPerceiveSpecification() {
        return (this.perceiveAssertSpecification != null) ?
                this.perceiveAssertSpecification :
                this.perceiveRetractSpecification;
    }

    void setUtility(Expression p) { this.utility = p; }

    Expression getUtility() { return this.utility; }

    PlanSequenceConstruct getBody() { return this.body; }

    PlanContext getContext() { return this.context; }

    void setContext(PlanContext c) { this.context = c; }

    PlanContext getPrecondition() { return this.precondition; }

    void setPrecondition(PlanContext c) { this.precondition = c; }

    PlanAtomicConstruct getEffects() { return this.effects; }

    void setEffects(PlanAtomicConstruct c) { this.effects = c; }

    PlanAtomicConstruct getFailure() { return this.failure; }

    void setFailure(PlanAtomicConstruct f) { this.failure = f; }

    String getDocumentation() { return this.documentation; }

    SymbolMap getSymbolMap() { return this.symbolMap; }

    void setDocumentation(String planDoc) { this.documentation = planDoc; }

    String getAttributes() { return this.attributes; }

    void setAttributes(String planAtt) { this.attributes = planAtt; }

    void setBody(PlanSequenceConstruct c) { this.body = c; }

    /** Calculate the utility value of the plan instance
     * @param binding
     * @return a float, higher precision would be risking the wrath of the gods
     */
    float evalUtility(Binding binding) {
        return (this.utility != null) ? (float) this.utility.eval(binding).getReal() : 0f;
    }

    /**  */
    PlanContext addContext(DList<Condition> cl) {
        this.context.addConditions(cl);
        return this.context;
    }

    /**  */
    PlanContext addPrecondition(DList<Condition> cl) {
        this.precondition.addConditions(cl);
        return this.precondition;
    }

    /** Evaluate truth value of context expression */
    boolean checkContext(DList<Binding> bindingList) {
        return (this.context != null) ? this.context.check(bindingList) : true;
    }

    /** Evaluate truth value of context expression */
    boolean confirmContext(Binding b) {
        return (this.context != null) ? this.context.confirm(b) : true;
    }

    /** Evaluate truth value of precondition expression */
    boolean checkPrecondition(DList<Binding> bindingList) {
        return (this.precondition != null) ? this.precondition.check(bindingList) : true;
    }

    /** Print out without worrying about being in-line with other output */
    String verboseString(Binding b) {
        String s = "";
        s += "Plan" + "\n";
        s += "  NAME: " + this.name + "\n";
        if (this.goalSpecification != null) {
            s += "  GOAL: ";
            s += this.goalSpecification.simpleString(b);
            s += "\n";
        } else if (this.concludeSpecification != null) {
            s += "  CONCLUDE: ";
            s += this.concludeSpecification.simpleString(b);
            s += "\n";
        } else if (this.perceiveAssertSpecification != null) {
            s += "  PERCEIVE ASSERT: ";
            s += this.perceiveAssertSpecification.simpleString(b);
            s += "\n";
        } else if (this.perceiveRetractSpecification != null) {
            s += "  PERCEIVE RETRACT: ";
            s += this.perceiveRetractSpecification.simpleString(b);
            s += "\n";
        }
        if (this.documentation != null) {
            s += "  DOCUMENTATION: " + this.documentation + "\n";
        }
        return s;
    }

    /** Print out so that it can be in-line with other output */
    String formattedString() {
        return "Plan: " + this.name + " ";
    }

}

