#include "DeerRender/Tools/Path.h"
#include "DeerStudio/AngelScriptEngine.h"
#include "DeerStudio/AngelScriptEngine/ErrorHandle.h"
#include "angelscript.h"
#include "scriptbuilder.h"

#include "DeerRender/Log.h"

#include <filesystem>
#include <iostream>
#include <string>

namespace fs = std::filesystem;

namespace Deer {
	namespace AngelScriptEngine {
		void loadModule(const Path& path);
	} // namespace AngelScriptEngine

	asIScriptFunction* AngelScriptEngine::getFactory(asITypeInfo* type) {
		std::string callString = "";
		if (strlen(type->GetNamespace())) {
			callString += type->GetNamespace();
			callString += "::";
		}
		callString += type->GetName();
		callString += " @";
		if (strlen(type->GetNamespace())) {
			callString += type->GetNamespace();
			callString += "::";
		}
		callString += type->GetName();
		callString += "()";

		return type->GetFactoryByDecl(callString.c_str());
	}

	void AngelScriptEngine::loadModule(const Path& path) {
		int err;

		scriptBuilder.StartNewModule(scriptEngine, path.stem().string().c_str());
		if (err < 0) {
			DEER_EDITOR_ENGINE_ERROR("Failed to init module for {0}", path.string().c_str());
			return;
		}

		for (const auto& entry : fs::recursive_directory_iterator(path)) {
			if (!entry.is_regular_file() || entry.path().extension() != ".as")
				continue;

			DEER_CORE_TRACE("{}", entry.path().filename().string().c_str());
			err = scriptBuilder.AddSectionFromFile(entry.path().string().c_str());
			if (err < 0) {
				DEER_EDITOR_ENGINE_ERROR("Failed loading script for module {0}\nscript: {1}", path.string().c_str(), entry.path().string().c_str());
			}
		}

		err = scriptBuilder.BuildModule();
		asIScriptModule* module = scriptBuilder.GetModule();

		if (err < 0) {
			DEER_EDITOR_ENGINE_ERROR("Failed compiling module {}\nerror: {}", path.stem().string().c_str(),
				Deer::AngelScriptEngine::getAngelScriptReturnCodeString(err));
			return;
		}

		modules.push_back({module});
	}

	void AngelScriptEngine::loadModules() {
		generateAngelscriptPredefined();
		const Path path = Path("Editor") / Path("Modules");

		if (!fs::exists(path) || !fs::is_directory(path)) {
			DEER_EDITOR_ENGINE_ERROR("Could not find folder {}", path.string().c_str());
			return;
		}

		for (const auto& module : fs::directory_iterator(path)) {
			// saveAngelscriptPredefined(_dir);
			if (module.path().extension() == ".disabled") {
				DEER_CORE_TRACE("Ignoring {}", module.path().stem().string().c_str());
				continue;
			}
			DEER_CORE_TRACE("=== Loading module {} ===", module.path().stem().string().c_str());
			saveAngelscriptPredefined(module.path());
			loadModule(module.path());
		}
	}
} // namespace Deer