package at.oefai.aaa.agent.jam;

import at.oefai.aaa.agent.jam.types.Binding;

/**
 * Represents a subgoaling construct.
 * @author Marc Huber
 * @author Jaeho Lee
 */
class PlanRuntimeGoalState implements PlanRuntimeState {

    private final GoalAction constructAction;

    /** Constructor w/ goal specifier as argument. */
    PlanRuntimeGoalState(final PlanSimpleConstruct se) {
        this.constructAction = (GoalAction) se.getAction();
    }

    // !!!!!NEED TO SIMPLIFY THIS!!!!
    /**
     * Find an applicable plan if necessary and execute the plan if it exists.
     * Also, deal with plan/subgoal failure and success appropriately.
     */
    public State execute(final Binding b, final Goal thisGoal) {
        if (thisGoal == null) {
            AgentLogger.getStaticInstance().warning("JAM: Warning!  "
                    + "Detected goal action within execution of Observer body.  Ignoring.");
            return State.CONSTRUCT_COMPLETE;
        }
        // use a reference to the interpreter of the goal to avoid method calls
        WorldModelTable goalWorldModel = thisGoal.getIntentionStructure().getWorldModel();
        AgentLogger log = thisGoal.getIntentionStructure().getLog();
        // get the subgoal of thisGoal (instead of the private member as before)
        Goal subgoal = thisGoal.getSubgoal();
        // If this is an ACHIEVEment goal then check to see if the goal's already been achieved.
        if (this.constructAction instanceof AchieveGoalAction) {
            Relation rel = this.constructAction.getGoal();
            if (goalWorldModel.match(rel, b)) {
                if (log.getShowISorGoalList()) log.fine("Already completed ACHIEVE goal.");
                if (subgoal != null) {
                    subgoal.setStatus(Goal.Status.SUCCESS);
                }
                return State.CONSTRUCT_COMPLETE;
            }
        }
        // Check to see if we've already posted a subgoal.  If there is,
        // we then need to see if an intention has been found for it.  If
        // so, we can execute something in the intention.
        if (subgoal != null) {
            if (subgoal.getIntention() != null) {
                PlanRuntimeState activeState = subgoal.getRuntimeState();
                if (activeState != null) {
                    // At this point we'll be executing an action from a PLAN so we have
                    // to check the subgoal to see if it's still valid.
                    if (!subgoal.getIntention().getPlan().confirmContext(subgoal.getIntentionBinding())
                        || (subgoal.getStatus() == Goal.Status.ABANDONED)) {
                            // OOPS!  subgoal invalid!
                            // Clean up the stack. Replace some of these with something like
                            // IS.force_goal_failure()???
                            subgoal.removeIntention(true);
                            thisGoal.getIntentionStructure().removeGoal(subgoal);
                            subgoal.setStatus(Goal.Status.FAILURE);
                            return State.CONSTRUCT_FAILED;
                    }
                    // NOW we can run something!
                    State returnValue = activeState.execute(subgoal.getIntentionBinding(), subgoal);
                    if (returnValue == State.CONSTRUCT_FAILED) {
                        if (log.getShowISorGoalList()) log.info("Subgoal \"" + subgoal.getName() + "\" failed!\n");
                        // Clean up the stack.  This might be the exact same as the context
                        // failure case above so I might want to create something like
                        // IS.force_goal_failure() and call it here and elsewhere.
                        subgoal.removeIntention(true);
                        subgoal.setStatus(Goal.Status.FAILURE);
                        thisGoal.getIntentionStructure().removeGoal(subgoal);
                        thisGoal.setSubgoal(null);
                        subgoal = null;
                        return State.CONSTRUCT_FAILED;
                    } else if (returnValue == State.CONSTRUCT_INCOMP) {
                        return State.CONSTRUCT_INCOMP;
                    } else { // if (returnValue == Status.CONSTRUCT_COMPLETE)
                        if (subgoal.getIntention().getPlan().getEffects() != null) {
                            subgoal.executeEffects();
                        }
                        subgoal.setStatus(Goal.Status.SUCCESS);
                        // Switch according to the _PLAN'S_ goal specification rather than
                        // the goal's specification
                        Action a = subgoal.getIntention().getPlan().getGoalSpecification();
                        if (a instanceof AchieveGoalAction) {
                            if (log.getShowISorGoalList())
                                log.fine("Just completed ACHIEVE goal.");
                                // Assert achieved goal state onto World Model
                            Relation rel = this.constructAction.getGoal();
                            goalWorldModel.assertRelation(rel, b);
                            if (log.getShowWorldModel())
                                log.info(goalWorldModel.verboseString());
                        } else if (a instanceof PerformGoalAction) {
                            if (log.getShowISorGoalList())
                                log.fine("Just completed PERFORM goal.");
                        } else if (a instanceof MaintainGoalAction) {
                            if (log.getShowISorGoalList())
                                log.fine("Just completed MAINTAIN goal.");
                        } else if (a instanceof QueryGoalAction) {
                            if (log.getShowISorGoalList())
                                log.fine("Just completed QUERY goal.");
                        } else {
                            log.info("Just completed UNKNOWN type of goal:"
                                     + this.constructAction.getClass().getName());
                        }
                        subgoal = null;
                        thisGoal.setSubgoal(null);
                        return State.CONSTRUCT_COMPLETE;
                    }
                } else { // activeState == null
                    log.warning(":PlanRuntimeGoalState: ERROR! activeState == null!");
                }
            } else { // no intention for subgoal yet
                thisGoal.setStatus(Goal.Status.BLOCKED);
                if (log.getShowISorGoalList()) log.info("PlanRuntimeGoalState: Setting goal status to BLOCKED for goal: "
                        + thisGoal.formattedString());
                thisGoal.getIntentionStructure().renewLeafGoals();
                return State.CONSTRUCT_INCOMP;
            }
        } else { // Otherwise we need to post the subgoal to the system.
            if (log.getShowISorGoalList()) log.info("PlanRuntimeGoalState: Adding subgoal:\n  "
                    + this.constructAction.formattedString(b));
            Goal newGoal = thisGoal.getIntentionStructure().addUnique(this.constructAction, thisGoal, b);
            newGoal.setStatus(Goal.Status.ACTIVE);
            if (log.getShowISorGoalList()) log.info("Just posted subgoal " + newGoal.getName() + " to system.\n"
                    + thisGoal.getIntentionStructure().verboseString());
            return State.CONSTRUCT_INCOMP;
        }
        log.warning("PlanRuntimeGoalState: Reached end of logic without return!\n");
        return State.CONSTRUCT_INCOMP;
    }
}

