package at.oefai.aaa.agent.jam;

import java.util.Random;
import java.util.Vector;

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

/**
 * Represents the runtime state of DoAll constructs.
 * @author Marc Huber
 * @author Jaeho Lee
 */
class PlanRuntimeDoAllState implements PlanRuntimeState {

    private PlanConstruct thisConstruct = null;
    private PlanRuntimeState substate = null;

    private int activeBranchNum;
    private Vector<Integer> branchesLeft; // Vector holding indices of branches that
    // have not yet completed (succeeded OR failed) 0-based

    /**  */
    PlanRuntimeDoAllState(PlanDoAllConstruct be) {
        this.thisConstruct = be;
        this.branchesLeft = new Vector<Integer>(be.getNumBranches());
        for (int i = 0; i < be.getNumBranches(); i++) {
            this.branchesLeft.addElement(new Integer(i));
        }
        setActiveBranchNum(selectRandomBranch());
        this.substate = be.getBranch(getActiveBranchNum()).newRuntimeState();
    }

    //
    // Member functions
    //
    int getActiveBranchNum() { return this.activeBranchNum; }

    /** Select a branch randomly from those still not completed or failed. */
    private int selectRandomBranch() {
        Random ran = new Random();
        return (this.branchesLeft.elementAt(Math.abs(ran.nextInt() % this.branchesLeft.size())).intValue());
    }

    /** Execute something in one of the branches */
    public State execute(Binding b, Goal thisGoal) {
        PlanConstruct currentConstruct;

        currentConstruct = ((PlanDoAllConstruct)this.thisConstruct).getBranch(getActiveBranchNum());

        if (this.substate == null) {
            this.substate = currentConstruct.newRuntimeState();
        }

        State returnVal = this.substate.execute(b, thisGoal);

        //
        // FAILED!
        //
        // Something in the branch failed so this construct fails
        if (returnVal == State.CONSTRUCT_FAILED) {

            return State.CONSTRUCT_FAILED;
        }

        //
        // INCOMPLETE
        //
        // Nothing's been determined at this point
        else if (returnVal == State.CONSTRUCT_INCOMP) {

            return State.CONSTRUCT_INCOMP;
        }

        //
        // COMPLETE!
        //
        else {

            // If the agent's just completed the last branch then return success.
            if (this.branchesLeft.size() == 1) {
                return State.CONSTRUCT_COMPLETE;
            }

            // If not, then the agent goes on to the next, random, branch
            // First, find and remove the current branch from further consideration
            for (int i = 0; i < this.branchesLeft.size(); i++) {
                if (this.branchesLeft.elementAt(i).intValue() == getActiveBranchNum()) {
                    this.branchesLeft.removeElementAt(i);
                }
            }

            // And then select the new branch.
            setActiveBranchNum(selectRandomBranch());
            this.substate = ((PlanDoAllConstruct) this.thisConstruct).getBranch(getActiveBranchNum()).newRuntimeState();
            return State.CONSTRUCT_INCOMP;
        }
    }

    /** Set which branch is being executed and keep within bounds. */
    void setActiveBranchNum(int n) {
        if (n >= 0 && n < ((PlanDoAllConstruct) this.thisConstruct).getNumBranches()) {
            this.activeBranchNum = n;
        } else {
            this.activeBranchNum = -1;
        }
    }

}

