
#include "types.h"
#include "trace.h"
#include "platform_hooks.h"
#include "constants.h"
#include "specialsignatures.h"
#include "specialclasses.h"
#include "threads.h"
#include "classes.h"
#include "language.h"
#include "configure.h"
#include "interpreter.h"
#include "memory.h"
#include "exceptions.h"
#include "stack.h"

#define NO_OWNER 0x00

#define get_stack_frame() ((StackFrame *) (currentThread->currentStackFrame))

/**
 * Thread currently being executed by engine(). Threads exist in an
 * intrinsic circular list.
 */
Thread* currentThread;

/**
 * Priority queue of threads. Entry points at the last thread in the queue.
 */
REFERENCE threads;
Thread **threadQ;

/**
 * Thread id generator, always increasing.
 */
byte gThreadCounter;

/**
 * Current program number, i.e. number of 'main()'s hanging around
 */
byte gProgramNumber;

StackFrame *current_stackframe()
{
  byte arraySize;

  arraySize = currentThread->stackFrameArraySize;
  if (arraySize == 0)
    return null;
  return stackframe_array() + (arraySize - 1);
}

void update_stack_frame (StackFrame *stackFrame)
{
  stackFrame->stackTop = curStackTop;
  stackFrame->pc = curPc;
}  

void update_registers (StackFrame *stackFrame)
{
  curPc = stackFrame->pc;
  curStackTop = stackFrame->stackTop;
  curLocalsBase = stackFrame->localsBase;
}

/* Turns out inlines aren't really inlined.
inline byte get_thread_id (Object *obj)
{
  return obj->threadId;
}

inline void set_thread_id (Object *obj, byte threadId)
{
  obj->threadId = threadId;
}

inline void inc_monitor_count (Object *obj)
{
  obj->monitorCount++;
}

inline void set_monitor_count (Object *obj, byte count)
{
  obj->monitorCount = count;
}
*/

#define get_thread_id(obj) ((obj)->threadId)
#define set_thread_id(obj,_threadId) ((obj)->threadId = (_threadId))
#define inc_monitor_count(obj) ((obj)->monitorCount++)
#define set_monitor_count(obj,count) ((obj)->monitorCount = (count))
/**
 * Initialise the thread pool. Note we use a Java object so that we can make
 * the pool available to Java.
 **/
void init_threads()
{
  int i;
  threads = ptr2ref(new_primitive_array(T_REFERENCE, 10));
  threadQ = (Thread **)ref_array(threads);
  Thread **pQ = threadQ;
  gThreadCounter = 0;
  currentThread = JNULL;
  for (i = 0; i<10; i++)
  {
    *pQ++ = null;
  }
}

/**
 * Allocate stack frames
 * Allocate ID
 * Insert into run list
 * Mark thread as STARTED
 */
boolean init_thread (Thread *thread)
{
  thread->threadId = gThreadCounter + 1;
  
  // Catch the primordial thread
  if (currentThread == null)
    thread->priority = NORM_PRIORITY;
  
  #if DEBUG_THREADS
  printf("Setting intial priority to %d\n", thread->priority);
  #endif

  if (thread->state != NEW)
  {
    throw_exception(illegalStateException);
    return false;
  }
  
  // Allocate space for stack frames.
  thread->stackFrameArray = ptr2word (new_primitive_array (T_STACKFRAME, INITIAL_STACK_FRAMES));
  if (thread->stackFrameArray == JNULL)
    return false;
    
  // Allocate actual stack storage (INITIAL_STACK_SIZE * 4 bytes)
  thread->stackArray = ptr2word (new_primitive_array (T_INT, INITIAL_STACK_SIZE));
  if (thread->stackArray == JNULL)
  {
    free_array (ref2obj(thread->stackFrameArray));
    thread->stackFrameArray = JNULL;
    return false;    
  }
  
  gThreadCounter++;
  
  #ifdef VERIFY
  assert (is_array (word2obj (thread->stackFrameArray)), THREADS0);
  assert (is_array (word2obj (thread->stackArray)), THREADS1);
  #endif

  thread->stackFrameArraySize = 0;
  thread->state = STARTED;
  if (currentThread == null)
    currentThread = thread;
    
  enqueue_thread(thread);

  return true;
}

/**
 * Switches to next thread:
 *
 * do
 *   get next thread
 *   if waiting, grab monitor and run
 *   if sleeping and timer expired, run
 *   if DEAD, clean up and use current thread
 *   if started, initialize and run
 * until selected thread can run
 *  
 * @return false iff there are no live threads
 *         to switch to.
 */
 
boolean switch_thread()
{
  Thread *anchorThread, *previousThread, *candidate;
  Thread **pThreadQ;
  boolean nonDaemonRunnable = false;
  StackFrame *stackFrame = null;
  short i;

  #if DEBUG_THREADS || DEBUG_BYTECODE
  printf ("------ switch_thread: currentThread at %d\n", (int) currentThread);
  #endif

  if (currentThread != null)
  {
    // Only current threads can die. Tidy up dead threads
    if (currentThread->state == DEAD)
    {
      #if DEBUG_THREADS
      printf ("Tidying up DEAD thread %d: %d\n", (int) currentThread, (int)currentThread->threadId);
      #endif
  
      #if REMOVE_DEAD_THREADS
      // This order of deallocation is actually crucial to avoid leaks
      free_array ((Object *) word2ptr (currentThread->stackArray));
      free_array ((Object *) word2ptr (currentThread->stackFrameArray));

      #ifdef SAFE
      currentThread->stackFrameArray = JNULL;
      currentThread->stackArray = JNULL;
      #endif // SAFE
      #endif // REMOVE_DEAD_THREADS
    
      // Remove thread from queue.
      dequeue_thread(currentThread);
    }
    else { // Save context information
      stackFrame = current_stackframe();

#if DEBUG_THREADS
      printf ("switchThread: current stack frame: %d\n", (int) stackFrame);
#endif
  
      if (stackFrame != null) {
        update_stack_frame (stackFrame);
      }
    }
  }

  currentThread = null;
  
  // Loop until a frame is found that can be made to run.
  for (i=MAX_PRIORITY-1; i >= 0; i--) {
    pThreadQ = &threadQ[i];

    previousThread = anchorThread = *pThreadQ;
    if (!previousThread)
      continue;

    do
    {
      candidate = word2ptr(previousThread->nextThread);

      #if DEBUG_THREADS
      printf ("Checking state of thread %d(%d)(s=%d,p=%d,i=%d,d=%d)\n",
      	(int)candidate,
      	(int)candidate->threadId,
      	(int)candidate->state,
      	(int)candidate->priority,
      	(int)candidate->interruptState,
      	(int)candidate->daemon
             );
      #endif
      
      // See if we can move a thread to the running state. Used to not do this if we
      // had already found one, but turns out to be easiest if we are avoiding
      // priority inversion.
      switch (candidate->state)
      {
        case CONDVAR_WAITING:
          // We are waiting to be notified
          if ((candidate->sleepUntil > 0) && (get_sys_time() >= (FOURBYTES) candidate->sleepUntil))
          {
#if DEBUG_MONITOR
      printf ("Waking up waiting thread %d: %d\n", (int) candidate, candidate->threadId);
#endif
            // We will drop through to mon waiting.
          }
          else if (candidate->interruptState == INTERRUPT_CLEARED)
            break;
          else
            candidate->interruptState = INTERRUPT_GRANTED;

          // candidate->state = MON_WAITING;
          // drop through
        case MON_WAITING:
          {
            Object *pObj = word2obj(candidate->waitingOn);
            byte threadId = get_thread_id(pObj);

            // We are waiting to enter a synchronized block
            #ifdef VERIFY
            assert (pObj != JNULL, THREADS6);
            #endif
             
            if (threadId == NO_OWNER)
            {
              // NOW enter the monitor (guaranteed to succeed)
              enter_monitor(candidate, pObj);
              
              // Set the monitor depth to whatever was saved.
              set_monitor_count(pObj, candidate->monitorCount);
              
              // Let the thread run.
              candidate->state = RUNNING;
  
              #ifdef SAFE
              candidate->waitingOn = JNULL;
              #endif
            }
#if PI_AVOIDANCE
            // Only avoid priority inversion if we don't already have a thread to run.
            else if (currentThread == null)
            {
            	Thread *pOwner;
            	int j;
            	
            	// Track down who owns this monitor and run them instead.            	
            	// Could be 'waiting' in a native method, or we could be deadlocked!
find_next:
            	if (candidate->threadId != threadId)
            	{
                    for (j=MAX_PRIORITY-1; j >= 0; j--)
                    {
                      pOwner = threadQ[j];
                      if (!pOwner)
                        continue;
                        
                      do {
                        // Remember threadQ[j] is the last thread on the queue
                        pOwner = word2ptr(pOwner->nextThread);
                        if (pOwner->threadId == threadId)
                        {
            		      if (pOwner->state == RUNNING)
            		      {
            			    currentThread = pOwner;
            			    goto done_pi;
            			  }
            			  
            		      // if owner is waiting too, iterate down.
            		      if (pOwner->state == MON_WAITING)
            		      {
            			    threadId = get_thread_id(word2obj(pOwner->waitingOn));
            			    if (threadId != NO_OWNER)
            				  goto find_next;
            		      }
                        }
                      } while (pOwner != threadQ[j]);
                    }                   
                    // If we got here, we're in trouble, just drop through.
            	}
            }
done_pi:
		break;
	    ;
#endif // PI_AVOIDANCE
          
          }
          break;
        case SLEEPING:
          if (candidate->interruptState != INTERRUPT_CLEARED
              || (get_sys_time() >= (FOURBYTES) candidate->sleepUntil))
          {
      #if DEBUG_THREADS
      printf ("Waking up sleeping thread %d: %d\n", (int) candidate, candidate->threadId);
      #endif
            candidate->state = RUNNING;
            if (candidate->interruptState != INTERRUPT_CLEARED)
          	candidate->interruptState = INTERRUPT_GRANTED;
            #ifdef SAFE
  	    candidate->sleepUntil = JNULL;
            #endif // SAFE
          }
          break;
        case STARTED:
          if (currentThread == null)
          {      
            // Put stack ptr at the beginning of the stack so we can push arguments
            // to entry methods. This assumes set_top_word or set_top_ref will
            // be called immediately below.
        #if DEBUG_THREADS
        printf ("Starting thread %d: %d\n", (int) candidate, candidate->threadId);
        #endif
            currentThread = candidate;	// Its just easier this way.
            init_sp_pv();
            candidate->state = RUNNING;
            if (candidate == bootThread)
            {
              MethodRecord *mRec;
              ClassRecord *classRecord;
              classRecord = get_class_record (get_entry_class (gProgramNumber));
              // Initialize top word with fake parameter for main():
              set_top_ref_cur (JNULL);
              // Push stack frame for main method:
              mRec= find_method (classRecord, main_4_1Ljava_3lang_3String_2_5V);
              dispatch_special (mRec, null);
              // Push another if necessary for the static initializer:
              dispatch_static_initializer (classRecord, curPc);
            }
            else
            {
              set_top_ref_cur (ptr2word (candidate));
              dispatch_virtual ((Object *) candidate, run_4_5V, null);
            }
            // The following is needed because the current stack frame
            // was just created
            stackFrame = current_stackframe();
            update_stack_frame (stackFrame);
          }
          break;
        case RUNNING:
          // Its running already
        case DEAD:
          // Dead threads should be handled earlier
        default:
          // ???
          break;
      }

      // Do we now have a thread we want to run?
      // Note we may later decide not to if all non-daemon threads have died        
      if (currentThread == null && candidate->state == RUNNING)
      {
        currentThread = candidate;
        // Move thread to end of queue
        *pThreadQ = candidate;
      }
      
      if (!candidate->daemon)
      {
      	// May or may not be running but it could do at some point
#if DEBUG_THREADS
printf ("Found a non-daemon thread %d: %d(%d)\n", (int) candidate, (int)candidate->threadId, (int) candidate->state);
#endif
           nonDaemonRunnable = true;
      }
      
      #if DEBUG_THREADS
      printf ("switch_thread: done processing thread %d: %d\n", (int) candidate,
              (int) (candidate->state == RUNNING));
      #endif

      // Always use the first running thread as the thread
      // Keep looping: cull dead threads, check there's at least one non-daemon thread
      previousThread = candidate;
    } while (candidate != anchorThread);
  } // end for
  
#if DEBUG_THREADS
printf ("currentThread=%d, ndr=%d\n", (int) currentThread, (int)nonDaemonRunnable);
#endif

#if DEBUG_THREADS
  printf ("Leaving switch_thread()\n");
#endif
  if (nonDaemonRunnable)
  {
    // There is at least one non-daemon thread left alive
    if (currentThread != null)
    {
      // If we found a running thread and there is at least one
      // non-daemon thread left somewhere in the queue...
      #if DEBUG_THREADS
      printf ("Current thread is %d: %d(%d)\n", (int) currentThread, (int)currentThread->threadId, (int) currentThread->state);
      printf ("getting current stack frame...\n");
      #endif
    
      stackFrame = current_stackframe();
    
      #if DEBUG_THREADS
      printf ("updating registers...\n");
      #endif
    
      update_registers (stackFrame);
    
      #if DEBUG_THREADS
      printf ("done updating registers\n");
      #endif
    
      if (currentThread->interruptState == INTERRUPT_GRANTED)
        throw_exception(interruptedException);
    }
      
    return true;
  }

  schedule_request(REQUEST_EXIT);
  currentThread = null;
  
  return false;
}

/*
 * Current thread owns object's monitor (we hope) and wishes to relinquish
 * it temporarily (by calling Object.wait()).
 */
void monitor_wait(Object *obj, const FOURBYTES time)
{
#if DEBUG_MONITOR
  printf("monitor_wait of %d, thread %d(%d)\n",(int)obj, (int)currentThread, currentThread->threadId);
#endif
  if (currentThread->threadId != get_thread_id (obj))
  {
    throw_exception(illegalMonitorStateException);
    return;
  }
  
  // Great. We own the monitor which means we can give it up, but
  // indicate that we are listening for notify's.
  currentThread->state = CONDVAR_WAITING;
  
  // Save monitor depth
  currentThread->monitorCount = get_monitor_count(obj);
  
  // Save the object who's monitor we will want back
  currentThread->waitingOn = ptr2word (obj);
  
  // Might be an alarm set too.
  if (time > 0)
    currentThread->sleepUntil = get_sys_time() + time; 	
  else
    currentThread->sleepUntil = 0;
  
#if DEBUG_MONITOR
  printf("monitor_wait of %d, thread %d(%d) until %ld\n",(int)obj, (int)currentThread, currentThread->threadId, time);
#endif

  // Indicate that the object's monitor is now free.
  set_thread_id (obj, NO_OWNER);
  set_monitor_count(obj, 0);
  
  // Gotta yield
  schedule_request( REQUEST_SWITCH_THREAD);
}

/*
 * Current thread owns object's monitor (we hope) and wishes to wake up
 * any other threads waiting on it. (by calling Object.notify()).
 */
void monitor_notify(Object *obj, const boolean all)
{
#if DEBUG_MONITOR
  printf("monitor_notify of %d, thread %d(%d)\n",(int)obj, (int)currentThread, currentThread->threadId);
#endif
  if (currentThread->threadId != get_thread_id (obj))
  {
    throw_exception(illegalMonitorStateException);
    return;
  }
  
  monitor_notify_unchecked(obj, all);
}

/*
 * wake up any objects waiting on the passed object.
 */
void monitor_notify_unchecked(Object *obj, const boolean all)
{
  short i;
  Thread *pThread;
  
#if DEBUG_MONITOR
  printf("monitor_notify_unchecked of %d, thread %d(%d)\n",(int)obj, (int)currentThread, currentThread->threadId);
#endif
  // Find a thread waiting on us and move to WAIT state.
  for (i=MAX_PRIORITY-1; i >= 0; i--)
  {
    pThread = threadQ[i];
    if (!pThread)
      continue;
      
    do {
      // Remember threadQ[i] is the last thread on the queue
      pThread = word2ptr(pThread->nextThread);
      if (pThread->state == CONDVAR_WAITING && pThread->waitingOn == ptr2word (obj))
      {
        // might have been interrupted while waiting
        if (pThread->interruptState != INTERRUPT_CLEARED)
          pThread->interruptState = INTERRUPT_GRANTED;

        pThread->state = MON_WAITING;
        if (!all)
          return;
      }
    } while (pThread != threadQ[i]);
  }
}

/**
 * currentThread enters obj's monitor:
 *
 * if monitor is in use, save object in thread and re-schedule
 * else grab monitor and increment its count.
 * 
 * Note that this operation is atomic as far as the program is concerned.
 */
void enter_monitor (Thread *pThread, Object* obj)
{
#if DEBUG_MONITOR
  printf("enter_monitor of %d\n",(int)obj);
#endif

  if (obj == JNULL)
  {
    throw_exception (nullPointerException);
    return;
  }

  if (get_monitor_count (obj) != NO_OWNER && pThread->threadId != get_thread_id (obj))
  {
    // There is an owner, but its not us.
    // Make thread wait until the monitor is relinquished.
    pThread->state = MON_WAITING;
    pThread->waitingOn = ptr2word (obj);
    pThread->monitorCount = 1;
    // Gotta yield
    schedule_request (REQUEST_SWITCH_THREAD);    
    return;
  }
  set_thread_id (obj, pThread->threadId);
  inc_monitor_count (obj);
}

/**
 * Decrement monitor count
 * Release monitor if count reaches zero
 */
void exit_monitor (Thread *pThread, Object* obj)
{
  byte newMonitorCount;

#if DEBUG_MONITOR
  printf("exit_monitor of %d\n",(int)obj);
#endif

  if (obj == JNULL)
  {
    // Exiting due to a NPE on monitor_enter [FIX THIS]
    return;
  }

  #ifdef VERIFY
  assert (get_thread_id(obj) == pThread->threadId, THREADS7);
  assert (get_monitor_count(obj) > 0, THREADS8);
  #endif

  newMonitorCount = get_monitor_count(obj)-1;
  if (newMonitorCount == 0)
    set_thread_id (obj, NO_OWNER);
  set_monitor_count (obj, newMonitorCount);
}

/**
 * Current thread waits for thread to die.
 *
 * throws InterruptedException
 */
void join_thread(Thread *thread)
{
}

void dequeue_thread(Thread *thread)
{
  // First take it out of its current queue
  byte cIndex = thread->priority-1;
  Thread **pThreadQ = &threadQ[cIndex];
  
  // Find the previous thread at the old priority
  Thread *previous = *pThreadQ;
  #if DEBUG_THREADS
  printf("Previous thread %ld\n", ptr2word(previous));
  #endif
  while (word2ptr(previous->nextThread) != thread)
    previous = word2ptr(previous->nextThread);

  #if DEBUG_THREADS
  printf("Previous thread %ld\n", ptr2word(previous));
  #endif
  if (previous == thread)
  {
  #if DEBUG_THREADS
  printf("No more threads of priority %d\n", thread->priority);
  #endif
    *pThreadQ = null;
  }
  else
  {
    previous->nextThread = thread->nextThread;
    *pThreadQ = previous;
  }  
}

void enqueue_thread(Thread *thread)
{
  // Could insert it anywhere. Just insert it at the end.
  byte cIndex = thread->priority-1;
  Thread *previous = threadQ[cIndex];
  threadQ[cIndex] = thread;
  if (previous == null)
    thread->nextThread = ptr2word(thread);
  else {
    Thread *pNext = word2ptr(previous->nextThread);
    thread->nextThread = ptr2word(pNext);
    previous->nextThread = ptr2word(thread);
  }
}

/**
 * Set the priority of the passed thread. Insert into new queue, remove
 * from old queue. Overload to remove from all queues if passed priority
 * is zero.
 *
 * Returns the 'previous' thread.
 */
void set_thread_priority(Thread *thread, const FOURBYTES priority)
{
  #if DEBUG_THREADS
  printf("Thread priority set to %ld was %d\n", priority, thread->priority);
  #endif
  if (thread->priority == priority)
    return;

  if (thread->state == NEW)
  {
  	// Not fully initialized
  	thread->priority = priority;
  	return;
  }

  dequeue_thread(thread);
  thread->priority = priority;
  enqueue_thread(thread);      
}

/**
 * Suspend the specified thread. If thread is null suspend all threads
 * except currentThread.
 */
void suspend_thread(Thread *thread)
{
  int i;
  Thread *pThread;
  if (thread)
    thread->state |= SUSPENDED;
  else
  {
    // Suspend all threads
    for (i=MAX_PRIORITY-1; i >= 0; i--)
    {
      pThread = threadQ[i];
      if (!pThread)
        continue;
      
      do {
        // Remember threadQ[i] is the last thread on the queue
        pThread = word2ptr(pThread->nextThread);
        if (pThread != currentThread) pThread->state |= SUSPENDED;
      } while (pThread != threadQ[i]);
    }
  }
  schedule_request( REQUEST_SWITCH_THREAD);
}

void resume_thread(Thread *thread)
{
  int i;
  Thread *pThread;
  if (thread)
  {
    thread->state &= ~SUSPENDED;
    return;
  }
  // Suspend all threads
  for (i=MAX_PRIORITY-1; i >= 0; i--)
  {
    pThread = threadQ[i];
    if (!pThread)
      continue;
      
    do {
      // Remember threadQ[i] is the last thread on the queue
      pThread = word2ptr(pThread->nextThread);
      pThread->state &= ~SUSPENDED;
    } while (pThread != threadQ[i]);
  }
}
