#include "Deer/Log.h"
#include "DeerStudio/EditorEngine.h"
#include "DeerStudio/EditorEngine/DockPanel/DockPanelContext.h"
#include "DeerStudio/EditorEngine/DockPanel/DockPanelInfo.h"
#include "DeerStudio/EditorEngine/ErrorHandle.h"

#include "DeerStudio/EditorEngine/AngelscriptPredefined.h"
#include "DeerStudio/EditorEngine/ServiceScript/ServiceScriptContext.h"

#include "Deer/DataStore.h"
#include "Deer/Path.h"

#include "angelscript.h"
#include "scriptbuilder.h"

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

namespace fs = std::filesystem;

namespace Deer {
	namespace EditorEngine {
		CScriptBuilder scriptBuilder;

		ServiceScriptContext *
		getServiceScriptContext(const ServiceScriptRef &ref) {
			for (ServiceScriptContext &ctx : serviceScriptModules) {
				const ServiceScriptInfo &info = ctx.getInfo();

				if (info.name == ref.name && info.version == ref.version) {
					return &ctx;
				}
			}

			return nullptr;
		}
	} // namespace EditorEngine

	void EditorEngine::loadDockPanels() {
		Path path = DataStore::rootPath / DEER_EDITOR_PANEL_PATH;

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

		DEER_CORE_TRACE("Extracting UI Engine Scripts ");
		for (const auto &_dir : fs::directory_iterator(path)) {
			Path panelInfo_path = _dir.path() / "dockPanelModule.json";

			// A panel info is neded to load a panel
			if (!fs::exists(panelInfo_path) ||
			    !fs::is_regular_file(panelInfo_path)) {
				DEER_EDITOR_ENGINE_WARN("Editor engine did not find "
				                        "dockPanelModule.json in folder {0}",
				                        panelInfo_path.c_str());
				continue;
			}

			DockPanelInfo *dockPanelInfo = loadDockPanelInfo(panelInfo_path);
			if (dockPanelInfo->name == "null") {
				DEER_EDITOR_ENGINE_ERROR(
				    "Failed to load dock panel module from {0},\n incorrect "
				    "panelInfo.json file",
				    path.string().c_str());
				delete dockPanelInfo;
				continue;
			}

			saveAngelscriptPredefined(_dir);

			int r;
			r = scriptBuilder.StartNewModule(scriptEngine,
			                                 dockPanelInfo->name.c_str());
			if (r < 0) {
				DEER_EDITOR_ENGINE_ERROR(
				    "Failed to create module for dock panel module {0}",
				    path.string().c_str());
				delete dockPanelInfo;
				continue;
			}

			asIScriptModule *module = scriptBuilder.GetModule();

			// Extract apis
			for (const ServiceScriptRef &serviceRef : dockPanelInfo->services) {
				ServiceScriptContext *ctx = getServiceScriptContext(serviceRef);

				if (ctx == nullptr) {
					DEER_EDITOR_ENGINE_ERROR(
					    "Editor engine could not find the Service with name "
					    "{0} and version {1}",
					    serviceRef.name.c_str(), serviceRef.version.c_str());
					continue;
				}

				DEER_EDITOR_ENGINE_INFO("added service {0}",
				                        serviceRef.name.c_str());
			}

			for (const auto &entry : fs::recursive_directory_iterator(_dir)) {
				if (entry.is_regular_file() &&
				    entry.path().extension() == ".as") {
					r = scriptBuilder.AddSectionFromFile(
					    entry.path().string().c_str());
					if (r < 0) {
						DEER_EDITOR_ENGINE_ERROR(
						    "Failed loading script for module {0}\nscript: {1}",
						    path.string().c_str(),
						    entry.path().string().c_str());
					}
				}
			}

			r = scriptBuilder.BuildModule();
			if (r < 0) {
				DEER_EDITOR_ENGINE_ERROR("Failed compiling module {0}",
				                         _dir.path().stem().string().c_str());
				delete dockPanelInfo;
				continue;
			}

			dockPanelModules.push_back({module, dockPanelInfo});
		}
	}
} // namespace Deer