package at.oefai.aaa.agent.jam;

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

/**
 * Represents the runtime state of a threaded sequence of constructs.
 * This class re-implements the PlanRuntimeState members simply because it's easier.
 * @author Marc Huber
 * @author Jaeho Lee
 */
class PlanRuntimeThreadState extends Thread implements PlanRuntimeState {

    private PlanSequenceConstruct thisConstruct;
    private PlanRuntimeSequenceState substate;
    private Binding binding;
    private Goal goal;
    private int threadNumber;
    private PlanRuntimeState.State[] threadState;

    //:modified by klesen June Thu 29 2000
    // used to produce a thread that may be safely "stopped" or "suspended"
    // see also jdk1.2/docs/guide/misc/threadPrimitiveDeprecation.html
    private volatile boolean threadStopped = true;
    private volatile boolean threadSuspended = false;

    //
    // Constructors
    //
    PlanRuntimeThreadState(final PlanSequenceConstruct be, final Binding b, final Goal thisGoal,
                           final int pThreadNumber, final PlanRuntimeState.State[] pThreadState) {
        this.thisConstruct = be;
        // Execute this construct directly (it's the only one)
        this.substate = (PlanRuntimeSequenceState) be.newRuntimeState();
        this.binding = b; // To hold for subconstructs
        this.goal = thisGoal; // To hold for subconstructs
        this.threadNumber = pThreadNumber; // Index into state array
        this.threadState = pThreadState; // reference to array held by PlanRuntimeParallelState
        setDaemon(true);
    }

    /** Suspend the currently executing thread safely. */
    protected void mySuspend() {
        this.threadSuspended = true;
    }

    /**
     * Returns <code>true</code> if this thread has been suspended.
     * @see #mySuspend()
     */
    protected boolean suspended() {
        return this.threadSuspended;
    }

    /** Resume the currently executing thread safely. */
    protected synchronized void myResume() {
        this.threadSuspended = false;
        notify();
    }

    /** Stop the currently executing thread safely. */
    protected synchronized void myStop() {
        this.threadStopped = true;
        notify();
    }

    /**
     * Returns <code>true</code> if this thread has been stopped.
     * @see #myStop()
     */
    protected boolean stopped() {
        return this.threadStopped;
    }

    /**
     * Returns <code>true</code> if this thread has executed the next action/construct
     * and suspended itself. If this thread is still in the middle of execution the main thread waits until it gets notified.
     * @see #suspended()
     */
    protected synchronized boolean stepComplete() {
        while (!this.threadSuspended) {
            try {
                wait();
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
        return true;
    }

    //
    // Member functions
    //
    public void run() {
        PlanRuntimeState.State returnVal;
        if (this.substate == null) {
            this.substate = (PlanRuntimeSequenceState) this.thisConstruct.newRuntimeState();
        }
        this.threadStopped = false;
        //log.info("PlanRuntimeThreadState:thread \"" + getName() + "\" running...");
        while (!this.threadStopped) { // Loop forever until sequence completes or fails
            returnVal = this.substate.execute(this.binding, this.goal);

            if (returnVal == PlanRuntimeState.State.CONSTRUCT_FAILED) {
                this.threadState[this.threadNumber] = PlanRuntimeState.State.CONSTRUCT_FAILED;
                //log.info("Subconstruct in \"" + getName() + "\" failed! (" + returnVal + ")");
                this.threadStopped = true;
            } else if (returnVal == PlanRuntimeState.State.CONSTRUCT_COMPLETE) {
                this.threadState[this.threadNumber] = PlanRuntimeState.State.CONSTRUCT_COMPLETE;
                //log.info("Subconstruct in \"" + getName() + "\" complete! (" + returnVal + ")");
                this.threadStopped = true;
            } else { // return_val == Status.CONSTRUCT_INCOMP
                //log.info("Subconstruct in \"" + getName() + "\" incomplete!");
                this.threadState[this.threadNumber] = PlanRuntimeState.State.CONSTRUCT_INCOMP;

                synchronized (this) {
                    //:modified by klesen August Wed 30 2000
                    this.threadSuspended = true;
                    notify();
                    try {
                        while (this.threadSuspended && !this.threadStopped) {
                            //log.info("PRTS: Thread \"" + getName() + "\" waiting");
                            wait();
                            //log.info("PRTS: Thread \"" + getName() + "\" done waiting");
                        }
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            }
        }
        //log.info("PlanRuntimeThreadState:thread \"" + getName() + "\" exiting...");
    }

    /** implementing the interface contract, but this.start is used in
     * PlanRuntimeParallelState... should be cleaned up consistently */
    public PlanRuntimeState.State execute(final Binding b, final Goal thisGoal) {
        //this.start();
        return PlanRuntimeState.State.CONSTRUCT_INCOMP;
    }
}

