#include <jsc/jsc.h>
#include <Core/Core.h>

EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec){
	Cerr()<<"--> "<< exec->argument(0).toString(exec).UTF8String().data() <<"\n";
	return JSValue::encode(jsUndefined());
}

EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*){
	// We need this function for compatibility with the Mozilla JS tests but for now
	// we don't actually do any version-specific handling
	return JSValue::encode(jsUndefined());
}

EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec){
	String fileName=exec->argument(0).toString(exec).UTF8String().data();
	String script=LoadFile(fileName);
	if (!script.IsVoid())
		return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));

	JSGlobalObject* globalObject = exec->lexicalGlobalObject();

	Time t=GetSysTime();
	evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(UString(script), UString(fileName)));
	int tt=GetSysTime().Get()-t.Get();
	
	return JSValue::encode(jsNumber(globalObject->globalExec(), tt));
}

EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec){
	String fileName=exec->argument(0).toString(exec).UTF8String().data();
	String script=LoadFile(fileName);
	if (!script.IsVoid())
		return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
	
	JSGlobalObject* globalObject = exec->lexicalGlobalObject();
	Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(UString(script), UString(fileName)));
	if (result.complType() == Throw)
		throwError(exec, result.value());
	return JSValue::encode(result.value());
}

JSReturnType functionCheckSyntax(ExecState* exec){
	String fileName=exec->argument(0).toString(exec).UTF8String().data();
	String script=LoadFile(fileName);
	if (!script.IsVoid())
		return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
	
	JSGlobalObject* globalObject = exec->lexicalGlobalObject();
	Completion result = checkSyntax(globalObject->globalExec(),makeSource(UString(script),UString(fileName)));
	if (result.complType() == Throw)
		throwError(exec, result.value());
	return JSValue::encode(result.value());
}

JSReturnType functionPrint(ExecState* exec) {
	for (unsigned i = 0; i < exec->argumentCount(); ++i)
		Cout()<< (i?" ":"") << exec->argument(i).toString(exec).UTF8String().data();
	Cout()<<"\n";
	return JSValue::encode(jsUndefined());
}

JSReturnType functionGC(ExecState* exec) {
	JSLock lock(SilenceAssertionsOnly);
	exec->heap()->collectAllGarbage();
	//Cout()<<"Garbage collected\n";
	return JSValue::encode(jsUndefined());
}

JSReturnType functionReadline(ExecState* exec) {
	return JSValue::encode(jsString(exec, UString(ReadStdIn())));
}

JSReturnType functionQuit(ExecState* exec) {
	// Technically, destroying the heap in the middle of JS execution is a no-no,
	// but we want to maintain compatibility with the Mozilla test suite, so
	// we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
	JSLock lock(SilenceAssertionsOnly);
	exec->globalData().dynamicGlobalObject = 0;
	exec->globalData().deref();
	exec->globalData().heap.destroy();
	exit(EXIT_SUCCESS);
}

using namespace Upp;


CONSOLE_APP_MAIN
{
	JavaScript js;
	js.AddNativeFunction("print",functionPrint);
	js.AddNativeFunction("quit",functionQuit);
	js.AddNativeFunction("readline",functionReadline);
	js.AddNativeFunction("gc",functionGC);
	js.AddNativeFunction("run",functionRun);
	js.AddNativeFunction("load",functionLoad);
	js.AddNativeFunction("debug",functionDebug);
	js.AddNativeFunction("version",functionVersion);
	js.AddNativeFunction("checkSyntax",functionCheckSyntax);
	Upp::Vector<String> cmd=CommandLine();
	bool success=true;
	String result;
	#if HAVE(SIGNAL_H)
	signal(SIGILL, _exit);
	signal(SIGFPE, _exit);
	signal(SIGBUS, _exit);
	signal(SIGSEGV, _exit);
	#endif

	for(int i = 0; i < cmd.GetCount(); i++){
		if(cmd[i]=="-f"||cmd[i]=="-s") continue;
		if(js.ExecuteFile(cmd[i],result)){
			RLOG("ok");
			success=success && true;
			Cout()<<result<<"\n";
		}else{
			RLOG("fail");RDUMP(result);
			success=success && false;
		}
	}
	SetExitCode(success?0:3);
	RDUMP(GetExitCode());
}

