//===----------- JavaThread.h - Java thread description -------------------===//
//
//                            The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef JNJVM_JAVA_THREAD_H
#define JNJVM_JAVA_THREAD_H

#include <ostream>

#include "vmkit/Cond.h"
#include "vmkit/Locks.h"
#include "vmkit/ObjectLocks.h"
#include "vmkit/Thread.h"

#include "MutatorThread.h"

#include "JavaObject.h"
#include "JNIReferences.h"

namespace j3 {

class Class;
class JavaMethod;
class JavaObject;
class Jnjvm;


#define BEGIN_NATIVE_EXCEPTION(level)
#define END_NATIVE_EXCEPTION


/*
 * FIXME Have to find a solution for both implementations
 */
#ifdef USE_OPENJDK
#define BEGIN_JNI_EXCEPTION \
  JavaThread* th = JavaThread::get(); \
  word_t SP = th->getLastSP(); \
  th->leaveUncooperativeCode(); \
  vmkit::KnownFrame Frame; \
  th->startKnownFrame(Frame); \
  Frame.currentFP = vmkit::System::GetCallerAddress();\
  TRY {
#else
#define BEGIN_JNI_EXCEPTION \
  JavaThread* th = JavaThread::get(); \
  word_t SP = th->getLastSP(); \
  th->leaveUncooperativeCode(); \
  vmkit::KnownFrame Frame; \
  th->startKnownFrame(Frame); \
  TRY {

#endif


#define END_JNI_EXCEPTION \
  } CATCH { \
    th->throwFromJNI(SP); \
  } END_CATCH;

#define RETURN_FROM_JNI(a) {\
  th->endKnownFrame(); \
  th->enterUncooperativeCode(SP); \
  return (a); } \

#define RETURN_VOID_FROM_JNI {\
  th->endKnownFrame(); \
  th->enterUncooperativeCode(SP); \
  return; } \

/// This is used to implement park/unpark behavior.
/// The functionalities is the foundation for java.util.concurrency package
class ParkLock {
private:
	vmkit::LockNormal lock;
	vmkit::Cond cond;
	bool permit;

public:
	ParkLock();
	~ParkLock();

	void park(bool isAbsolute, int64_t time, JavaThread* thread);
	void unpark();
	void interrupt();
};

/// JavaThread - This class is the internal representation of a Java thread.
/// It maintains thread-specific information such as its state, the current
/// exception if there is one, the layout of the stack, etc.
///
class JavaThread : public vmkit::MutatorThread {
public:
  
  /// jniEnv - The JNI environment of the thread.
  ///
  void* jniEnv;
  
  /// pendingException - The Java exception currently pending.
  ///
  JavaObject* pendingException;

  /// javaThread - The Java representation of this thread.
  ///
  JavaObject* javaThread;

  /// vmThread - The VMThread object of this thread. (GNU Classpath)
  /// sleepObject - Empty Object used to wait on for sleeping (OpenJDK)
  ///
  union {
    JavaObject* vmThread;
    JavaObject* sleepObject;
  };

  vmkit::LockingThread lockingThread;
  
  /// JNI implementation fields
  ///

  /// currentAddedReferences - Current number of added local references.
  ///
  uint32_t* currentAddedReferences;

  /// localJNIRefs - List of local JNI references.
  ///
  JNILocalReferences* localJNIRefs;

  /// JNIlocalFrames - vector of JNI Frames
  /// pair represents { oldAddedReferences, capacity }
  ///
  std::vector< std::pair<uint32_t*, int> > JNIlocalFrames;

  ///
  ///

  // State of this Thread
  int state;

  // Lock to implement park/unpark
  ParkLock parkLock;


  JavaObject** pushJNIRef(JavaObject* obj) {
    llvm_gcroot(obj, 0);
    if (!obj) return 0;
   
    ++(*currentAddedReferences);
    return localJNIRefs->addJNIReference(this, obj);

  }

  /// tracer - Traces GC-objects pointed by this thread object.
  ///
  virtual void tracer(word_t closure);

  /// JavaThread - Empty constructor, used to get the VT.
  ///
  JavaThread() {
	  state = vmkit::LockingThread::StateRunning;
  }

  /// ~JavaThread - Delete any potential malloc'ed objects used by this thread.
  ///
  ~JavaThread();
  
  /// JavaThread - Creates a Java thread.
  ///
  JavaThread(vmkit::VirtualMachine* isolate);

  void initialise(JavaObject* thread, JavaObject* vmth);
  
  /// get - Get the current thread as a J3 object.
  ///
  static JavaThread* get() {
    return (JavaThread*)vmkit::Thread::get();
  }

  /// getJVM - Get the Java VM in which this thread executes.
  ///
  Jnjvm* getJVM() const {
    return (Jnjvm*)MyVM;
  }

  /// currentThread - Return the current thread as a Java object.
  ///
  JavaObject* currentThread() const {
    return javaThread;
  }
 
  /// throwException - Throw the given exception in the current thread.
  ///
  void throwException(JavaObject* obj);

  /// throwPendingException - Throw a pending exception.
  ///
  void throwPendingException();
  
  /// getJavaException - Return the pending exception.
  ///
  JavaObject* getJavaException() {
    return pendingException;
  }

  /// throwFromJNI - Throw an exception after executing JNI code.
  ///
  void throwFromJNI(word_t SP) {
    // Nothing to do. The RETURN_FROM_JNI will take care of it.
  }
  
  /// throwFromNative - Throw an exception after executing Native code.
  ///
  void throwFromNative() {
  }
  
  /// throwFromJava - Throw an exception after executing Java code.
  ///
  void throwFromJava() {
    throwPendingException();
  }

  /// startJava - Interesting, but actually does nothing :)
  void startJava() {}
  
  /// endJava - Interesting, but actually does nothing :)
  void endJava() {}

  /// startJNI - Record that we are entering native code.
  ///
  void startJNI();

  void endJNI();

  /// getCallingMethod - Get the Java method in the stack at the specified
  /// level.
  ///
  JavaMethod* getCallingMethodLevel(uint32 level);
  
  /// getCallingClassLevel - Get the Java method in the stack at the
  /// specified level.
  ///
  UserClass* getCallingClassLevel(uint32 level);
  
  /// getNonNullClassLoader - Get the first non-null class loader on the
  /// stack.
  ///
  JavaObject* getNonNullClassLoader();
    
  /// printJavaBacktrace - Prints the backtrace of this thread. Only prints
  /// the Java methods on the stack.
  ///
  void printJavaBacktrace();

  /// getJavaFrameContext - Fill the buffer with Java methods currently on
  /// the stack.
  ///
  uint32 getJavaFrameContext(void** buffer);

  inline void setState(int a) { state= a; }

private:
  /// internalClearException - Clear the C++ and Java exceptions
  /// currently pending.
  ///
  virtual void internalClearException() {
    pendingException = NULL;
  }

public:
  friend std::ostream& operator << (std::ostream&, const JavaThread&);
  void dump() const __attribute__((noinline));

  virtual void throwNullPointerException(word_t methodIP);
};

} // end namespace j3

#endif
