#include "j3/j3thread.h"
#include "j3/j3method.h"
#include "j3/j3class.h"
#include "j3/j3.h"

#include "vmkit/safepoint.h"
#include "vmkit/compiler.h"

using namespace j3;

J3Thread::J3Thread(J3* vm) : 
	Thread(vm),
	_allocator(vmkit::BumpAllocator::create()),
	_localReferences(_allocator) {
	_jniEnv.functions = &jniEnvTable;
	_javaVM.functions = &javaVMTable;
}

J3Thread::~J3Thread() {
	vmkit::BumpAllocator::destroy(_allocator);
}

void J3Thread::run() {
	J3ObjectHandle* handle = javaThread();
	vm()->threadClassRun->invokeVirtual(handle);
}

void J3Thread::start(J3ObjectHandle* handle) {
	vmkit::BumpAllocator* allocator = vmkit::BumpAllocator::create();
	J3Thread* thread = new J3Thread(get()->vm());
	thread->assocJavaThread(handle);
	thread->Thread::start();
}

J3Method* J3Thread::getJavaCaller(uint32_t level) {
	vmkit::Safepoint* sf = 0;
	vmkit::StackWalker walker;

	while(walker.next()) {
		vmkit::Safepoint* sf = vm()->getSafepoint(walker.ip());

		if(sf) {
			if(level < sf->inlineDepth())
				return (J3Method*)sf->unit()->getSymbol(sf->functionName(level));
			else
				level -= sf->inlineDepth();
		}
	}

	return 0;
}

J3ObjectHandle* J3Thread::pendingException() {
	if(_pendingException) {
		return push(_pendingException);
	} else
		return 0;
}

void J3Thread::replayException() {
	J3Thread* self = J3Thread::get();
	J3ObjectHandle* pending = self->_pendingException;
	if(pending) {
		self->_pendingException = 0;
		vmkit::VMKit::throwException(pending->obj());
	}
}

void J3Thread::ensureCapacity(uint32_t capacity) {
	_localReferences.ensureCapacity(capacity);
}

J3Thread* J3Thread::nativeThread(J3ObjectHandle* handle) {
	return (J3Thread*)handle->getLong(get()->vm()->threadClassVMData);
}

void J3Thread::assocJavaThread(J3ObjectHandle* javaThread) {
	_javaThread = *javaThread;
	_javaThread.setLong(vm()->threadClassVMData, (int64_t)(uintptr_t)this);
}

J3ObjectHandle* J3Thread::push(J3ObjectHandle* handle) { 
	return _localReferences.push(handle); 
}

J3ObjectHandle* J3Thread::push(J3Object* obj) { 
	return _localReferences.push(obj); 
}

J3ObjectHandle* J3Thread::tell() { 
	return _localReferences.tell(); 
}

void J3Thread::restore(J3ObjectHandle* ptr) { 
	_localReferences.restore(ptr); 
}

J3Thread* J3Thread::get() { 
	return (J3Thread*)Thread::get(); 
}

J3ThreadBootstrap::J3ThreadBootstrap(J3* vm) : J3Thread(vm) {
}

void J3ThreadBootstrap::run() {
	vm()->run();
}
