/**
 * Runtime data structures for loaded program.
 */

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

#if 0
#define get_stack_object(MREC_)  ((Object *) get_ref_at ((MREC_)->numParameters - 1))
#endif

// Reliable globals:

void *installedBinary;

// Temporary globals:

// (Gotta be careful with these; a lot of stuff
// is not reentrant because of globals like these).

static ClassRecord *tempClassRecord;
static MethodRecord *tempMethodRecord;

// Methods:

byte get_class_index (Object *obj)
{
  byte f;

  f = obj->flags.all;
  if (f & IS_ARRAY_MASK)
    return JAVA_LANG_OBJECT;
  return (f & CLASS_MASK);
}

/**
 * @return Method record or null.
 */
MethodRecord *find_method (ClassRecord *classRecord, TWOBYTES methodSignature)
{
  tempByte = classRecord->numMethods;
  while (tempByte--)
  {
    tempMethodRecord = get_method_record (classRecord, tempByte);
    if (tempMethodRecord->signatureId == methodSignature)
      return tempMethodRecord;
  }
  return null;
}

boolean dispatch_static_initializer (ClassRecord *aRec, byte *retAddr)
{
  if (is_initialized (aRec))
    return false;
  set_initialized (aRec);
  if (!has_clinit (aRec))
    return false;
  #if DEBUG_METHODS
  printf ("dispatch_static_initializer: has clinit: %d, %d\n",
          (int) aRec, (int) retAddr);
  #endif
  dispatch_special (find_method (aRec, _6clinit_7_4_5V), retAddr);
  return true;
}

void dispatch_virtual (Object *ref, TWOBYTES signature, byte *retAddr)
{
  MethodRecord *auxMethodRecord;
  byte auxByte;

#if DEBUG_METHODS
  printf("dispatch_virtual %d\n", signature);
#endif
  if (ref == JNULL)
  {
    throw_exception (nullPointerException);
    return;
  }

  auxByte = get_class_index(ref);
 LABEL_METHODLOOKUP:
  tempClassRecord = get_class_record (auxByte);
  auxMethodRecord = find_method (tempClassRecord, signature);
  if (auxMethodRecord == null)
  {
    #if SAFE
    if (auxByte == JAVA_LANG_OBJECT)
    {
      throw_exception (noSuchMethodError);
      return;
    }
    #endif
    auxByte = tempClassRecord->parentClass;
    goto LABEL_METHODLOOKUP;
  }

  if (dispatch_special (auxMethodRecord, retAddr))
  {
    if (is_synchronized(auxMethodRecord))
    {
      current_stackframe()->monitor = ref;
      enter_monitor (currentThread, ref);
    }
  }
}

/**
 * Calls static initializer if necessary before
 * dispatching with dispatch_special().
 * @param retAddr Return bytecode address.
 * @param btAddr Backtrack bytecode address (in case
 *               static initializer is executed).
 */
void dispatch_special_checked (byte classIndex, byte methodIndex,
                               byte *retAddr, byte *btAddr)
{
  ClassRecord *classRecord;

  #if DEBUG_METHODS
  printf ("dispatch_special_checked: %d, %d, %d, %d\n",
          classIndex, methodIndex, (int) retAddr, (int) btAddr);
  #endif

  classRecord = get_class_record (classIndex);
  if (dispatch_static_initializer (classRecord, btAddr))
    return;
  dispatch_special (get_method_record (classRecord, methodIndex), retAddr);
}

/**
 * @param classRecord Record for method class.
 * @param methodRecord Calle's method record.
 * @param retAddr What the PC should be upon return.
 * @return true iff the stack frame was pushed.
 */
boolean dispatch_special (MethodRecord *methodRecord, byte *retAddr)
{
  #if DEBUG_METHODS
  int debug_ctr;
  #endif

  StackFrame *stackFrame;
  byte newStackFrameIndex;

  #if DEBUG_BYTECODE
  printf ("\n------ dispatch special - %d ------------------\n\n",
          methodRecord->signatureId);
  #endif

  #if DEBUG_METHODS
  printf ("dispatch_special: %d, %d\n", 
          (int) methodRecord, (int) retAddr);
  printf ("-- signature id = %d\n", methodRecord->signatureId);
  printf ("-- code offset  = %d\n", methodRecord->codeOffset);
  printf ("-- flags        = %d\n", methodRecord->mflags);
  printf ("-- num params   = %d\n", methodRecord->numParameters);
  printf ("-- stack ptr    = %d\n", (int) get_stack_ptr());
  printf ("-- max stack ptr= %d\n", (int) (currentThread->stackArray + (get_array_size(currentThread->stackArray))*2));
  #endif

  pop_words (methodRecord->numParameters);
  pc = retAddr;

  if (is_native (methodRecord))
  {
  #if DEBUG_METHODS
  printf ("-- native\n");
  #endif 
    dispatch_native (methodRecord->signatureId, get_stack_ptr() + 1);
    // Stack frame not pushed
    return false;
  }

  newStackFrameIndex = currentThread->stackFrameArraySize;
  
  if (newStackFrameIndex >= get_array_length((Object *) word2ptr (currentThread->stackFrameArray)))
  {
#if !FIXED_STACK_SIZE
  	// int len = get_array_length((Object *) word2ptr (currentThread->stackFrameArray));
  	int newlen = get_array_length((Object *) word2ptr (currentThread->stackFrameArray)) * 3 / 2;
  	JINT newStackFrameArray = JNULL;

	// Stack frames are indexed by a byte value so limit the size.  	
  	if (newlen <= 255)
  	{
  	    // increase the stack frame size
  		newStackFrameArray = ptr2word(reallocate_array(word2ptr(currentThread->stackFrameArray), newlen));
  	}
  	
  	// If can't allocate new stack, give in!
    if (newStackFrameArray == JNULL)
    {
#endif
      throw_exception (stackOverflowError);
      return false;
#if !FIXED_STACK_SIZE
    }
      	
  	// Assign new array
  	currentThread->stackFrameArray = newStackFrameArray;
#endif
  }
  
  if (newStackFrameIndex == 0)
  {
    // Assign NEW stack frame
    stackFrame = stackframe_array();
  }
  else
  {
    #if DEBUG_METHODS
    for (debug_ctr = 0; debug_ctr < methodRecord->numParameters; debug_ctr++)
      printf ("-- param[%d]    = %ld\n", debug_ctr, (long) get_stack_ptr()[debug_ctr+1]);  
    #endif

    // Save OLD stackFrame state
    stackFrame = stackframe_array() + (newStackFrameIndex - 1);
    update_stack_frame (stackFrame);
    // Push NEW stack frame
    stackFrame++;
  }
  // Increment size of stack frame array
  currentThread->stackFrameArraySize++;
  // Initialize rest of new stack frame
  stackFrame->methodRecord = methodRecord;
  stackFrame->monitor = null;
  stackFrame->localsBase = get_stack_ptr() + 1;
  // Initialize auxiliary global variables (registers)
  pc = get_code_ptr(methodRecord);

  #if DEBUG_METHODS
  printf ("pc set to 0x%X\n", (int) pc);
  #endif

  init_sp (stackFrame, methodRecord);
  update_constant_registers (stackFrame);
  
  //printf ("m %d stack = %d\n", (int) methodRecord->signatureId, (int) (localsBase - stack_array())); 
  
  // Check for stack overflow
  // (stackTop + methodRecord->maxOperands) >= (stack_array() + STACK_SIZE);
  if (is_stack_overflow (methodRecord))
  {
#if !FIXED_STACK_SIZE
    StackFrame *stackBase;
    int i;
    
    // Need at least this many bytes
    // int len = (int)(stackTop + methodRecord->maxOperands) - (int)(stack_array()) - HEADER_SIZE;
    
    // Need to compute new array size (as distinct from number of bytes in array).
  	int newlen = (((int)(stackTop + methodRecord->maxOperands) - (int)(stack_array()) - HEADER_SIZE + 1) / 4) * 3 / 2;
  	JINT newStackArray = ptr2word(reallocate_array(word2ptr(currentThread->stackArray), newlen));
  	
  	// If can't allocate new stack, give in!
    if (newStackArray == JNULL)
    {
#endif
      throw_exception (stackOverflowError);
      return false;
#if !FIXED_STACK_SIZE
    }
      	
    // Adjust pointers.
    newlen = newStackArray - currentThread->stackArray;
    stackBase = stackframe_array();
    stackTop = word2ptr(ptr2word(stackTop) + newlen);
    localsBase = word2ptr(ptr2word(localsBase) + newlen);
#if DEBUG_MEMORY
	printf("thread=%d, stackTop(%d), localsBase(%d)=%d\n", currentThread->threadId, (int)stackTop, (int)localsBase, (int)(*localsBase));
#endif
    for (i=currentThread->stackFrameArraySize-1;
         i >= 0;
         i--)
    {
    	stackBase[i].localsBase = word2ptr(ptr2word(stackBase[i].localsBase) + newlen);
   		stackBase[i].stackTop = word2ptr(ptr2word(stackBase[i].stackTop) + newlen);
#if DEBUG_MEMORY
	printf("stackBase[%d].localsBase(%d) = %d\n", i, (int)stackBase[i].localsBase, (int)(*stackBase[i].localsBase));
#endif
    }
    
  	// Assign new array
  	currentThread->stackArray = newStackArray;
#endif
  } 
  return true;
}

/**
 */
void do_return (byte numWords)
{
  StackFrame *stackFrame;
  STACKWORD *fromStackPtr;

  stackFrame = current_stackframe();

  #if DEBUG_BYTECODE
  printf ("\n------ return ----- %d ------------------\n\n",
          stackFrame->methodRecord->signatureId);
  #endif

  #if DEBUG_METHODS
  printf ("do_return: method: %d  #  num. words: %d\n", 
          stackFrame->methodRecord->signatureId, numWords);
  #endif

  #ifdef VERIFY
  assert (stackFrame != null, LANGUAGE3);
  #endif
  if (stackFrame->monitor != null)
    exit_monitor (currentThread, stackFrame->monitor);

  #if DEBUG_THREADS || DEBUG_METHODS
  printf ("do_return: stack frame array size: %d\n", currentThread->stackFrameArraySize);
  #endif

  if (currentThread->stackFrameArraySize == 1)
  {
    #if DEBUG_METHODS
    printf ("do_return: thread is done: %d\n", (int) currentThread);
    #endif
    currentThread->state = DEAD;
    schedule_request (REQUEST_SWITCH_THREAD);
    return;
  }

  // Place source ptr below data to be copied up the stack
  fromStackPtr = get_stack_ptr_at (numWords);
  // Pop stack frame
  currentThread->stackFrameArraySize--;
  stackFrame--;
  // Assign registers
  update_registers (stackFrame);

  #if DEBUG_METHODS
  printf ("do_return: stack reset to:\n");
  printf ("-- stack ptr = %d\n", (int) get_stack_ptr());
  #endif

  while (numWords--)
  {
    push_word (*(++fromStackPtr));
  }  
}

/**
 * @return 1 or 0.
 */
STACKWORD instance_of (Object *obj, byte classIndex)
{
  byte rtType;

  if (obj == null)
    return 0;
  rtType = get_class_index(obj);
  // TBD: support for interfaces
  if (is_interface (get_class_record(classIndex)))
    return 1;
 LABEL_INSTANCE:
  if (rtType == classIndex)
    return 1;
  if (rtType == JAVA_LANG_OBJECT)
    return 0;
  rtType = get_class_record(rtType)->parentClass;
  goto LABEL_INSTANCE;
}





