if defined(INCLUDEOS):
	import <service>

if defined(USE_PYTHON):
	import <Python.h>

import "tp.gen.h"
import <thread>
import <stdio.h>
import <signal.h>
import <stdlib.h>
import <unistd.h>

#ifndef SUPER_TINY
import <iostream>
import <fstream>
import <sstream>

if defined(PROFILE_HASHING):
	def print_hash_stats();

#ifndef __MINGW64__
	#ifndef __EMSCRIPTEN_major__
	#ifndef INCLUDEOS
		#include <execinfo.h>
		// https://gist.github.com/fmela/591333
		#include <dlfcn.h>    // for dladdr
		#include <cxxabi.h>   // for __cxa_demangle
		#include <cstdio>
		#include <cstdlib>
		// This function produces a stack backtrace with demangled function & method names.
		std::string cpp_backtrace(int skip = 1) {
				void *callstack[128];
				const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
				char buf[1024];
				int nFrames = backtrace(callstack, nMaxFrames);
				char **symbols = backtrace_symbols(callstack, nFrames);
				std::ostringstream trace_buf;
				for (int i = skip; i < nFrames; i++) {
						printf("%s\n", symbols[i]);
						Dl_info info;
						if (dladdr(callstack[i], &info) && info.dli_sname) {
								char *demangled = NULL;
								int status = -1;
								if (info.dli_sname[0] == '_') demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
								snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n",
												 i, int(2 + sizeof(void*) * 2), callstack[i],
												 status == 0 ? demangled :
												 info.dli_sname == 0 ? symbols[i] : info.dli_sname,
												 (char *)callstack[i] - (char *)info.dli_saddr);
								free(demangled);
						} else {
								snprintf(buf, sizeof(buf), "%-3d %*p %s\n",
												 i, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]);
						}
						trace_buf << buf;
				}
				free(symbols);
				if (nFrames == nMaxFrames) trace_buf << "[truncated]\n";
				return trace_buf.str();
		}
	#endif
	#endif
#endif


## https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
def crash_handler(int sig):
	fprintf(stderr, "Error: signal %d:\n", sig);
	#ifdef __MINGW64__
	#else
	  #ifndef __EMSCRIPTEN_major__
	    #ifndef INCLUDEOS
	      if (sig != 2) std::cout << cpp_backtrace() << std::endl;
	    #endif
	  #endif
	#endif
	exit(1);

## end of SUPER_TINY
#endif

#ifndef PURE_AOT
def _tp_import_modules(TP) ->void*;

## from runtime.pyc++
def tp_load(TP, const char*) ->tp_obj;

#ifdef USE_EMBEDDED_BYTECODE
def decrypt_user_bytecode(unsigned char*) ->unsigned char*;

def run_vm_embedded(int argc, char *argv[]):
	#ifdef DEBUG
	  std::cout << "run_vm_embedded..." << std::endl;
	#endif
	tp_vm *tp = tp_init(argc, argv);
	#ifdef DEBUG
	  std::cout << "tp_init OK..." << std::endl;
	#endif
	try:
		#ifdef DEBUG
		  std::cout << "trying to import from buffer..." << std::endl;
		  std::cout << __embedded_bytecode_gen_h__ << std::endl;
		#endif
		##tp_import_from_buffer(tp, "__main__", __embedded_bytecode_gen_h__,  sizeof(__embedded_bytecode_gen_h__));
		tp_import_from_buffer(tp, "__main__", decrypt_user_bytecode(__embedded_bytecode_gen_h__),  sizeof(__embedded_bytecode_gen_h__));
	catch (const char * str):
		std::cout << "Runtime Exception: " << str << std::endl;
		crash_handler(-1);
	tp_deinit(tp);

#else
void run_vm(int argc, char *argv[], int script_index){
	tp_vm *tp = tp_init(argc, argv);
	tp_obj fname = tp_string_atom(tp, argv[script_index]);
	tp_obj code = tp_load(tp, argv[script_index]);
	try:
		tp_obj module = tp_import(tp, tp_string_atom(tp, "__main__"), code, fname);
	catch (const char * str):
		std::cout << "Runtime Exception: " << str << std::endl;
		crash_handler(-1);
	tp_deinit(tp);
#endif
## end of PURE_AOT
#endif

#ifdef USE_PYTHON
def compile_and_run_py_file(std::string filename):
	std::fstream* f
	std::ostringstream c
	f = new std::fstream(filename.c_str(), std::fstream::in | std::fstream::binary)
	c << f->rdbuf()
	f->close()
	std::string pyscript = c.str()
	std::string bc = tpython_compile(pyscript, filename)
	tp_vm *tp = tp_init(0, NULL)
	try:
		tp_import_from_buffer(tp, "__main__", (unsigned char*)bc.c_str(),  bc.size())
	catch (const char * str):
		std::cout << "Runtime Exception: " << str << std::endl;
	tp_deinit(tp)
#endif


#ifdef PURE_AOT

import "__user_pythonic__.gen.h"

@static
def __aot_main__(int argc, char *argv[]):
	std::cout << "__aot_main__" << std::endl
	__aot_user_main__()

#else

@static
def __tpy_main__(int argc, char *argv[]):
	#ifdef USE_EMBEDDED_BYTECODE
	  run_vm_embedded(argc, argv);
	#else
	if argc==1:
		std::cout << "Error: no bytecode or py file provided" << std::endl;
	elif argc==2:
		std::string f = std::string(argv[1])
		if defined(DEBUG):
			print( f )
		if f.size() > 3:
			if f.substr( f.size()-3, f.size() ) == std::string(".py"):
				#ifdef USE_PYTHON
				compile_and_run_py_file(f)
				#else
				print("Error: tpython must be built with CPython to compile py files to bytecode")
				#endif
				return
		run_vm(argc, argv, 1)
	elif argc==3:
		std::thread t1( [=]{run_vm(argc, argv, 1);} );
		std::thread t2( [=]{run_vm(argc, argv, 2);} );
		t1.join();
		t2.join();
	else:
		std::cout << "TODO: more than two threads it not yet supported" << std::endl;
	#endif


#endif

#ifdef INCLUDEOS
def Service::start():
	if defined(DEBUG):
		std::cout << "starting tpython interpreter..." << std::endl;
	run_vm_embedded(0, NULL);

#else

def main(int argc,  char *argv[]) -> int:
	if defined(DEBUG):
		std::cout << "starting tpython interpreter..." << std::endl;
	if defined(DEBUG_SIZES):
		std::cout << "tpython interpreter exit" << std::endl;
		std::cout << "sizeof tp_vm = " << sizeof(tp_vm) << std::endl;
		std::cout << "sizeof tp_obj = " << sizeof(tp_obj) << std::endl;
		std::cout << "sizeof TPTypeInfo = " << sizeof(TPTypeInfo) << std::endl;
		std::cout << "sizeof tpd_frame = " << sizeof(tpd_frame) << std::endl;
		std::cout << "sizeof tpd_list = " << sizeof(tpd_list) << std::endl;
		std::cout << "sizeof tpd_dict = " << sizeof(tpd_dict) << std::endl;
		std::cout << "sizeof tpd_item = " << sizeof(tpd_item) << std::endl;
		std::cout << "sizeof tpd_string = " << sizeof(tpd_string) << std::endl;
		std::cout << "sizeof tpd_func = " << sizeof(tpd_func) << std::endl;
	#ifndef SUPER_TINY
	#ifndef __EMSCRIPTEN_major__
	  signal(SIGSEGV, crash_handler); // memory segfault
	  signal(SIGABRT, crash_handler); // some old places in tinypy code had used `abort()`
	  signal(SIGINT, crash_handler);  // allow CTRL+C to halt the program
	  #ifndef __MINGW64__
	    #ifdef DEBUG
	      std::cout << "set crash_handlers OK" << std::endl;
	      std::cout << "__init_libself__..." << std::endl;
	    #endif
	    __init_libself__()
	  #endif
	#endif
	#endif
	if defined(USE_RPMALLOC):
		rpmalloc_initialize();
	if defined(USE_PYTHON):
		Py_SetStandardStreamEncoding("utf-8", "surrogateescape");
		Py_Initialize();
	if defined(PURE_AOT):
		__aot_main__(argc, argv)
	else:
		__tpy_main__(argc, argv)
	if defined(PROFILE_HASHING):
		print_hash_stats()
	return 0;
#endif
