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

#include "angelscript.h"

namespace Deer {
	namespace EditorEngine {
		ServiceScriptContext::ServiceScriptContext(asIScriptModule* _module,
		                                           ServiceScriptInfo* _info)
		    : info(_info), module(_module) {

			size_t nScripts = module->GetObjectTypeCount();
			asITypeInfo* serviceScriptType =
			    scriptEngine->GetTypeInfoByName("ServiceScript");

			if (!serviceScriptType) {
				DEER_EDITOR_ENGINE_ERROR(
				    "Could not load ServiceScript interface type");
				return;
			}

			context = scriptEngine->CreateContext();
			context->AddRef();
			for (size_t i = 0; i < nScripts; i++) {
				asITypeInfo* info = module->GetObjectTypeByIndex(i);

				// If it does not implement service script we are not interested
				// int it
				if (!info->Implements(serviceScriptType))
					continue;

				scriptServices.push_back({info, context});
			}
		}

		ServiceScriptContext::~ServiceScriptContext() {
			scriptServices.clear();

			if (context)
				context->Release();

			delete info;
		}

		ServiceScriptContext::ServiceScriptContext(ServiceScriptContext&& o)
		    : scriptServices(std::move(o.scriptServices)) {
			context = o.context;
			module = o.module;
			info = o.info;

			o.context = nullptr;
			o.module = nullptr;
			o.info = nullptr;
		}

		ServiceScriptContext&
		ServiceScriptContext::operator=(ServiceScriptContext&& o) {
			if (&o != this) {
				scriptServices = std::move(o.scriptServices);
				context = o.context;
				module = o.module;
				info = o.info;

				o.context = nullptr;
				o.module = nullptr;
				o.info = nullptr;
			}
			return *this;
		}

		void ServiceScriptContext::update() {
			for (auto& scriptService : scriptServices) {
				scriptService.update();
			}
		}

		void ServiceScriptContext::init() {
			for (auto& scriptService : scriptServices) {
				scriptService.init();
			}
		}

		void ServiceScriptContext::bindFunctions() {
			std::string ns;
			ns = info->name;

			DEER_CORE_INFO(ns.c_str());

			scriptEngine->SetDefaultNamespace(ns.c_str());
			for (ServiceScriptObject& object : scriptServices) {
				object.bindFunctions();
			}
			scriptEngine->SetDefaultNamespace("");
		}

	} // namespace EditorEngine
} // namespace Deer