/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

package netscape.jsdebug;

/*  
*  jband - 03/19/97
*
*  This is an 'abstracted version of netscape.debug.ThreadState 
*
*  The methods that were 'native' there are 'abstract' here.
*  Changed 'private' data to 'protected' (though native access is immune)
*  Changed 'private' resume0() to 'protected'
*  Removed ThreadHook referneces
*
*/

/**
* When a hook is hit, the debugger records the state of the
* thread before the hook in a ThreadState object.  This object
* is then passed to any hook methods that are called, and can
* be used to change the state of the thread when it resumes from the
* hook.
*
* @author  John Bandhauer
* @author  Nick Thompson
* @version 1.0
* @since   1.0
*/
public abstract class ThreadStateBase {
    protected Thread thread;
    protected boolean valid;
    protected boolean runningHook;
    protected boolean resumeWhenDone;
    protected int status;
    protected int continueState;
    protected StackFrameInfo[] stack; /* jband - 03/19/97 - had no access modifier */
    protected Object returnValue;
    protected Throwable currentException;
    protected int currentFramePtr;  /* used internally */
    protected ThreadStateBase previous;

    /**
     * <B><font color="red">Not Implemented.</font></B> 
     * Always throws <code>InternalError("unimplemented")</code>
     */
    public static ThreadStateBase getThreadState(Thread t)
        throws InvalidInfoException
    {
        throw new InternalError("unimplemented");
    }

    /**
     * Get the Thread that this ThreadState came from.
     */
    public Thread getThread() {
        return thread;
    }

    /**
     * Return true if the Thread hasn't been resumed since this ThreadState
     * was made.
     */
    public boolean isValid() {
        return valid;
    }

    /**
     * Return true if the thread is currently running a hook
     * for this ThreadState
     */
    public boolean isRunningHook() {
        return runningHook;
    }

    /**
     * partial list of thread states from sun.debug.ThreadInfo.
     * XXX some of these don't apply.
     */
    public final static int THR_STATUS_UNKNOWN		= 0x01;
    public final static int THR_STATUS_ZOMBIE		= 0x02;
    public final static int THR_STATUS_RUNNING		= 0x03;
    public final static int THR_STATUS_SLEEPING		= 0x04;
    public final static int THR_STATUS_MONWAIT		= 0x05;
    public final static int THR_STATUS_CONDWAIT		= 0x06;
    public final static int THR_STATUS_SUSPENDED	= 0x07;
    public final static int THR_STATUS_BREAK		= 0x08;

    /** 
     * Get the state of the thread at the time it entered debug mode.
     * This can't be modified directly.
     */
    public int getStatus() {
        return status;
    }

    /** 
     * Get the count of the stackframes
     */
    public abstract int countStackFrames()
        throws InvalidInfoException;
    /** 
     * Get the 'top' stackframe; i.e. the one with the current instruction
     */
    public abstract StackFrameInfo getCurrentFrame()
        throws InvalidInfoException;

    /**
     * Get the thread's stack as an array.  stack[stack.length-1] is the
     * current frame, and stack[0] is the beginning of the stack.
     */
    public synchronized StackFrameInfo[] getStack()
        throws InvalidInfoException {
        // XXX check valid?
        if (stack == null) {
            stack = new StackFrameInfo[countStackFrames()];
        }

        if (stack.length == 0)
            return stack;

        StackFrameInfo frame = getCurrentFrame();
        stack[stack.length - 1] = frame;
        for (int i = stack.length - 2; i >= 0; i--) {
            frame = frame.getCaller();
            stack[i] = frame;
        }

        // should really be read-only for safety
        return stack;
    }

    /**
     * Leave the thread in a suspended state when the hook method(s)
     * finish.  This can be undone by calling resume().  This is the
     * default only if the break was the result of
     * DebugController.sendInterrupt().
     */
    public void leaveSuspended() {
        resumeWhenDone = false;
    }

    /**
     * Resume the thread.  This is the default unless the break was the result
     * of DebugController.sendInterrupt(). This method may be called from a
     * hook method, in which case the thread will resume when the
     * method returns.
     * Alternatively, if there is no active hook method and
     * the thread is suspended around in the DebugFrame, this resumes it
     * immediately.
     */
    public synchronized void resume() {
        if (runningHook)
            resumeWhenDone = true;
        else
            resume0();
    }

    protected abstract void resume0();

    /**
     * if the continueState is DEAD, the thread cannot
     * be restarted.
     */
    public final static int DEBUG_STATE_DEAD		= 0x01;

    /**
     * if the continueState is RUN, the thread will
     * proceed to the next program counter value when it resumes.
     */
    public final static int DEBUG_STATE_RUN		= 0x02;

    /**
     * if the continueState is RETURN, the thread will
     * return from the current method with the value in getReturnValue()
     * when it resumes.
     */
    public final static int DEBUG_STATE_RETURN		= 0x03;

    /**
     * if the continueState is THROW, the thread will
     * throw an exception (accessible with getException()) when it
     * resumes.
     */
    public final static int DEBUG_STATE_THROW		= 0x04;

    /**
     * This gets the current continue state of the debug frame, which
     * will be one of the DEBUG_STATE_* values above.
     */
    public int getContinueState() {
        return continueState;
    }

    public int setContinueState(int state) {
        int old = continueState;
        continueState = state;
        return old;
    }

    /**
     * If the thread was stopped while in the process of returning
     * a value, this call returns an object representing that value.
     * non-Object values are wrapped as in the java.lang.reflect api.
     * This is only relevant if the continue state is RETURN, and the
     * method throws an IllegalStateException otherwise.
     */
    public Object getReturnValue() throws IllegalStateException {
        if (continueState != DEBUG_STATE_RETURN)
            throw new IllegalStateException("no value being returned");
        return returnValue;
    }

    /** 
     * If the thread was stopped while in the process of throwing an
     * exception, this call returns that exception.  
     * This is only relevant if the continue state is THROW, and it
     * throws an IllegalStateException otherwise.
     */
    public Throwable getException() throws IllegalStateException {
        if (continueState != DEBUG_STATE_THROW)
            throw new IllegalStateException("no exception throw in progress");
        return currentException;
    }
}

