#include "DeerStudio/AngelScriptEngine.h"
#include "DeerStudio/AngelScriptEngine/AngelScriptRegisters.h"
#include "DeerStudio/AngelScriptEngine/ErrorHandle.h"

#include "DeerStudio/StudioAPI/Debug.h"

#include "angelscript.h"
#include "scriptany.h"
#include "scriptarray.h"
#include "scriptbuilder.h"
#include "scriptdictionary.h"
#include "scripthandle.h"
#include "scriptstdstring.h"

namespace Deer {
	// GLOBAL DECLARATIONS
	namespace AngelScriptEngine {
		asIScriptEngine* scriptEngine;
		asIScriptContext* scriptContext;

		CScriptBuilder scriptBuilder;

		std::vector<Module> modules;
		Module* executingModule;

		asITypeInfo* panelBaseType;
		asITypeInfo* serviceBaseType;
		asITypeInfo* arrayStringBaseType;
	} // namespace AngelScriptEngine

	void AngelScriptEngine::raiseError() {
		if (AngelScriptEngine::executingModule)
			executingModule->invalidate();
	}

	void AngelScriptEngine::initialize() {
		int err = 0;

		// Deinitialize if its initialized
		deinitialize();

		scriptEngine = asCreateScriptEngine();
		AS_RET_CHECK(scriptEngine->SetMessageCallback(asFUNCTION(Deer::StudioAPI::errorCallback_angelscript), 0, asCALL_CDECL));

		RegisterScriptHandle(scriptEngine);
		RegisterScriptAny(scriptEngine);
		RegisterStdString(scriptEngine);
		RegisterScriptArray(scriptEngine, true);
		RegisterScriptDictionary(scriptEngine);

		registerStructs();
		registerFunctions();

		extractBaseTypes();

		loadModules();

		scriptContext = scriptEngine->CreateContext();
		for (Module& module : modules) {
			module.init();
		}
	}
	void AngelScriptEngine::update() {
		for (Module& module : modules) {
			module.update();
		}
	}
	void AngelScriptEngine::shutdown() {
		for (Module& module : modules) {
			module.shutdown();
		}
	}
	void AngelScriptEngine::render() {
		for (Module& module : modules) {
			module.render();
		}
	}

	void AngelScriptEngine::deinitialize() {
		for (Module& module : modules) {
			module.shutdown();
		}

		modules.clear();

		if (scriptContext)
			scriptContext->Release();

		if (scriptEngine)
			scriptEngine->ShutDownAndRelease();

		scriptContext = nullptr;
		scriptEngine = nullptr;
	}

	void AngelScriptEngine::registerStructs() {
		registerMathStructs();
		registerResourceStructs();
		registerEnvironmentStructs();
		registerEntityStructs();
		registerEngineStructs();
		registerFrameBufferStructs();
		registerUIStructs();
	}

	void AngelScriptEngine::registerFunctions() {
		registerUIFunctions();
		registerMathFunctions();
		registerResourceFunctions();
		registerEnvironmentFunctions();
		registerEntityFunctions();
		registerEngineFunctions();
		registerFrameBufferFunctions();
	}
} // namespace Deer