#include "main.h"

#include <uf/utils/io/iostream.h>
#include <uf/utils/time/time.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/window/payloads.h>

#include <uf/utils/memory/pool.h>
#include <uf/spec/renderer/universal.h>


#include <filesystem>
#include <signal.h>
#include <cstdlib>

namespace {
	bool killing = true;
	namespace handlers {

		void exit() {
		#if UF_ENV_DREAMCAST
			arch_stk_trace(1);
		#endif

			std::ofstream output;
			output.open(uf::io::root+"/logs/crash.txt");
			for ( const auto& str : uf::iostream.getHistory() ) output << str << "\n";
			output.close();
			
			if ( client::terminated ) return;
			UF_MSG_INFO("Termination via std::atexit()!");
			
			client::ready = false;
			//ext::ready = false;
			uf::ready = false;
			
			client::terminated = true;
			
			ext::terminate();
			uf::terminate();
			client::terminate();
		}

		void abrt( int sig ) {
			UF_MSG_ERROR("Abort detected");
		#if UF_ENV_DREAMCAST
			arch_stk_trace(1);
			exit();
		#else
			if ( ::killing ) {
				std::_Exit(0);
			} else if ( !client::terminated ) {
				::killing = true;
				exit();
			}
		#endif
		}

		void segv( int sig ) {
			UF_MSG_ERROR("Segfault detected");
		#if UF_ENV_DREAMCAST
			arch_stk_trace(1);
			exit();
		#else
			if ( ::killing ) {
				std::_Exit(0);
			} else if ( !client::terminated ) {
				::killing = true;
				exit();
			}
		#endif
		}
	}
}

int main(int argc, char** argv){
	for ( size_t i = 0; i < argc; ++i ) {
		char* c_str = argv[i];
		std::string string(argv[i]);
		// uf::arguments.emplace_back(string);
	}

	std::atexit(::handlers::exit);
	signal(SIGABRT, ::handlers::abrt);
	signal(SIGSEGV, ::handlers::segv);

	client::initialize();
	uf::initialize();
	ext::initialize();

	// For Multithreaded initialization
	while ( !client::ready || !uf::ready ) {
		static uf::Timer<long long> timer(false);
		static double next = 1;
		if ( !timer.running() ) timer.start();

		if ( timer.elapsed().asDouble() >= next ) {
		//	UF_MSG_INFO("Waiting for " << ( client::ready ? "client" : "extension / engine" ) << " to initialize... Retrying in " << next << " seconds.");
			UF_MSG_INFO("Waiting for {} to initialize; retrying in {} seconds", ( client::ready ? "client" : "extension / engine" ), next);
			next *= 2;
		}
	}

	while ( client::ready && uf::ready ) {
		++uf::time::frame;
		
	#if UF_EXCEPTIONS
		try {	
	#endif
			if ( uf::renderer::settings::experimental::dedicatedThread /*&& !uf::renderer::states::rebuild*/ ) {
			//	auto& thread = uf::thread::fetchWorker();
				auto& thread = uf::thread::get("Render");
				uf::thread::queue(thread, [&]{
					ext::render();
					uf::render();
					client::render();
				});
				
				client::tick();
				ext::tick();
				uf::tick();

				uf::thread::wait( thread );
			} else {
				client::tick();
				uf::tick();
				ext::tick();

				ext::render();
				uf::render();
				client::render();
			}
	#if UF_EXCEPTIONS
		} catch ( std::runtime_error& e ) {
			UF_MSG_ERROR("RUNTIME ERROR: {}", e.what());
			abort();
		} catch ( std::exception& e ) {
			UF_MSG_ERROR("EXCEPTION ERROR: {}", e.what());
			abort();
		} catch ( bool handled ) {
			if (!handled) {
				UF_MSG_ERROR("UNHANDLED ERROR: {}", "???");
				abort();
			}
		} catch ( ... ) {
			UF_MSG_ERROR("UNKNOWN ERROR: {}", "???");
			abort();
		}
	#endif
	}
	if ( !client::terminated ) {
		client::terminated = true;
		UF_MSG_INFO("Natural termination!");
		ext::terminate();
		uf::terminate();
		client::terminate();
	}
	return 0;
}