#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <math.h>

#include "types.hh"
#include "ovm_heap_exports.hh"

extern double _j2c_NaN;
extern double _j2c_nzero;
extern float _j2c_NaNf;
extern float _j2c_nzerof;

typedef e_ovm_core_domain_Code Code;
typedef e_ovm_core_domain_Oop Oop;
typedef e_ovm_core_execution_Context Context;
typedef e_ovm_core_execution_InvocationMessage InvocationMessage;
typedef e_s3_core_domain_S3Blueprint S3Blueprint;
typedef e_s3_core_domain_S3Blueprint_Array S3Blueprint_Array;
typedef e_s3_core_domain_S3Blueprint_Scalar S3Blueprint_Scalar;
typedef e_s3_core_domain_S3Blueprint_Primitive S3Blueprint_Primitive;

typedef e_s3_core_execution_S3CoreServicesAccess CSA;

typedef struct J2cContext NativeContext;
typedef void *NativeCodeFragment;

inline jboolean subtype_of_scalar(S3Blueprint *sub,
				  S3Blueprint_Scalar *zuper) {
  return (sub->ovm_typeDisplay_1->values[zuper->ovm_typeBucket_1]
	  == zuper->ovm_typeBucketID_1);
}

/*
 * Variations:  If zuper's element type is know statically not to be
 * Object, Cloneable, or Serializable, we know the actual depth
 * statically, and can maybe specialize for length == 1
 */
inline jboolean subtype_of_array(S3Blueprint *sub,
				 S3Blueprint *zuper) {
  while (sub->ovm_arrayDepth_1 && zuper->ovm_arrayDepth_1) {
    sub = ((S3Blueprint_Array *) sub)->ovm_componentBlueprint_1;
    zuper = ((S3Blueprint_Array *) zuper)->ovm_componentBlueprint_1;
  }
  if (zuper->ovm_arrayDepth_1)
    return false;
  else
    return subtype_of_scalar(sub, (S3Blueprint_Scalar *) zuper);
}

/*
 * If invoke_native returned a (C++) jvalue instead of a jlong, none
 * of this would be needed.  We must be careful to convert the return
 * value to an appropriately sized integer before we stuff it into any
 * unions
 */
#define jlong_to_jwide(name)						\
static inline name to_##name(jlong l) {					\
  jwide ret;								\
  ret.jw_jlong = (jlong) l;						\
  return ret.jw_##name;							\
}
#define jlong_to_jnarrow(name)						\
static inline name to_##name(jlong l) {					\
  jnarrow ret;								\
  ret.jn_jint = (jint) l;						\
  return ret.jn_##name;							\
}
#define jlong_to_itype(name)						\
static inline name to_##name(jlong l) { return (name) l; }

jlong_to_itype(jboolean);
jlong_to_itype(jbyte);
jlong_to_itype(jchar);
jlong_to_itype(jshort);
jlong_to_itype(jint);
jlong_to_jnarrow(jfloat);
jlong_to_jnarrow(jref);
jlong_to_itype(jlong);
jlong_to_jwide(jdouble);

#define jvalue_ctor(T) 							\
static inline jvalue to_jvalue(T v) {					\
  jvalue ret;								\
  ret.jv_##T = v;							\
  return ret;								\
}

#define jwide_ctor(T)							\
jvalue_ctor(T)								\
static inline jwide to_jwide(T v) {					\
  jwide ret;								\
  ret.jw_##T = v;							\
  return ret;								\
}

#define jnarrow_ctor(T)							\
jvalue_ctor(T)								\
static inline jnarrow to_jnarrow(T v) {					\
  jnarrow ret;								\
  ret.jn_##T = v;							\
  return ret;								\
}

//jnarrow_ctor(jboolean);
jnarrow_ctor(jbyte);
jnarrow_ctor(jchar);
jnarrow_ctor(jshort);
jnarrow_ctor(jint);
jnarrow_ctor(jfloat);
jnarrow_ctor(jref);
jwide_ctor(jlong);
jwide_ctor(jdouble);

#ifdef PRECISE_GC
struct GCFrame {
  GCFrame *next;
  HEADER arrayHeader;
  int length;
};
#endif

// #undef OVM_X86 /* for testing ucontext crap */

// aix/linux have slightly different calling conventions
#if defined(OVM_PPC) 
# if defined(OSX_BUILD)
#   define DARWIN_PPC
# elif defined linux
#   define LINUX_PPC
# endif
#endif

#if defined(OVM_X86) || defined(DARWIN_PPC) || defined(LINUX_PPC)
# define THREAD_SETJMP
#elif defined(HAVE_SWAPCONTEXT)
  /* The thread-launching asm for ppc-linux seems to work, but unless
   * we use swapcontext, certain library functions such as malloc can
   * only be called from the main thread.
   */
# define THREAD_UCONTEXT
#else
# error "No threading support for this system"
#endif

#ifdef THREAD_SETJMP
# include <setjmp.h>
#else
# include <ucontext.h>
#endif

struct J2cContext {
  // Should include NativeContext, use the VarArea &c?
  Context *ovmContext;
#ifdef THREAD_SETJMP
  jmp_buf buf;
#else
  ucontext_t uc;
#endif

  void *stackBase;
  void *stackTop;

  Code *code;
  InvocationMessage *args;

#ifdef PRECISE_GC
  GCFrame *gcTop;
#endif

  int topFrame;
  int bottomFrame;

  J2cContext(Context *oc): ovmContext(oc) {
#ifdef PRECISE_GC
    gcTop = 0;
#endif
  }
};

extern J2cContext *currentContext;

extern int **initializedBlueprints;

// Force pointer dereference.  This check could be made safer by
// adding the keyword volatile, but gcc already treats asm statements
// with no outputs specially.
#define NULLCHECK(p) ({ asm ("" : : "r"(HEADER_BLUEPRINT(p))); p; })

extern "C" {
#include "atomicops.h"
#include "engine.h"
#include "activation.h"
#include "native_calls.h"
#include "eventmanager.h"
#include "timer.h"

  // A function to call in place of native/boot-time/otherwise undefined
  // methods. What should the return type of j2cFail be, exactly?
  extern jint j2cFail(...) __attribute__((noreturn));

  extern jboolean is_subtype_of(S3Blueprint *, S3Blueprint *);

  inline jboolean array_store_valid(HEADER *arr, HEADER *o) {
    S3Blueprint_Array *abp = (S3Blueprint_Array *) HEADER_BLUEPRINT(arr);
    return (!o ||
	    is_subtype_of(HEADER_BLUEPRINT(o), abp->ovm_componentBlueprint_1));
  }
#define ARRAY_STORE_INVALID(a, o) !array_store_valid(a, o)

  inline jint needs_init(jint ctx, jint bp) {
    int w = bp >> 5;
    int b = 1 << (bp & 31);
    return (!(initializedBlueprints[ctx][w] & b));
  }
  extern void did_init(jint ctx, jint bp);

  // INVOKE_SYSTEM support
  inline jint getContext(int processorHandle) {
    assert(processorHandle == 0);
    return (jint) currentContext->ovmContext;
  }
  extern jvalue j2cInvoke(Code *, InvocationMessage *);
  extern jint newNativeContext(Context* ovm_context);
  extern void destroyNativeContext(jint nc);
  extern void run(jint processor, jint nc);
  extern jint makeActivation(jint nc, Code *, InvocationMessage *);

  // GC support:
  extern void *getOVMContextLoc(jint nc);

  // precise:
  extern void *getInvocationArgsLoc(jint nc);
  extern e_Array<Oop*> *getLocalReferences(jint gcFrame);
  extern jint getGcRecord(jint nc, jint frameHandle);
  extern jint getNextGcRecord(jint gcFrame);

  // conservative:
  extern void *getStackBase(jint nc);
  extern void *getStackTop(jint nc);
  extern jboolean stackGrowsUp();
  extern void *getSavedRegisters(jint nc);
  extern jint getSavedRegisterSize();

  // stack trace support:
  extern void *j2c_getCode(void *pc);
  extern jint j2c_bottomFrame(jint nc);
  extern jint j2c_topFrame(jint nc);

  // Generated functions with well-known names.

  // Note that j2cThrow's signature must be compatible with
  // CoreServicesAccess.processThrowable()
  extern e_java_lang_Error *j2c_throw(HEADER *) __attribute__((noreturn));
  extern void boot();  
  extern void throwWildcardException(HEADER *) __attribute__((noreturn));
  extern void generateThrowable(int)           __attribute__((noreturn));

  // Not currently used.
  extern e_s3_services_j2c_J2cCodeFragment *codeFromPC(jint pc);

  // generateThrowable uses the following variable, which is set and
  // maintained by C++ code
  extern sigset_t j2cNormalMask;
}

/**
 * An engine-specific primitive operation.  Force the calling
 * method to save all call-preserved registers.
 */
#undef SAVE_REGISTERS
#if defined(OVM_X86)
# define SAVE_REGISTERS() asm("" : : : "ebx", "esi", "edi")
#elif defined(OVM_PPC) && defined(linux)
# define SAVE_REGISTERS() asm("" : : :					\
			      "%r14", "%r15", "%r16", "%r17", "%r18",	\
			      "%r19", "%r20", "%r21", "%r22", "%r23",	\
			      "%r24", "%r25", "%r26", "%r27", "%r28",	\
			      "%r29", "%r30", "%r31")
#elif defined(OVM_PPC) && defined(OSX_BUILD)
# if __GNUC__ >= 4
/* If r13 does not play a dedicated, and undisclosed, role, it is
 * callee-save.
 */
# define SAVE_REGISTERS() asm("" : : :					\
			      "r14", "r15", "r16", "r17", "r18",	\
			      "r19", "r20", "r21", "r22", "r23",	\
			      "r24", "r25", "r26", "r27", "r28",	\
			      "r29", "r30", "r13")
# else
#  define SAVE_REGISTERS() asm("" : : :					\
			       "r14", "r15", "r16", "r17", "r18",	\
			       "r19", "r20", "r21", "r22", "r23",	\
			       "r24", "r25", "r26", "r27", "r28",	\
			       "r29", "r30", "r31", "r13")
# endif
#else
# warning "SAVE_REGISTERS not implemented, optimized programs should not GC"
# define SAVE_REGISTERS()
#endif


