#pragma once
#include <vector>
#include "DeerRender/Tools/Path.h"

class asIScriptEngine;
class asIScriptModule;
class asIScriptContext;
class asIScriptObject;
class asIScriptFunction;
class asITypeInfo;
class CScriptBuilder;

namespace Deer {
	namespace AngelScriptEngine {
		void initialize();
		void update();
		void shutdown();
		void render();
		void deinitialize();

		struct Service {
		  public:
			Service(asITypeInfo*);
			~Service();

			Service(Service&) = delete;
			Service(Service&&) = default;
			Service& operator=(Service&&) = default;

			void init();
			void update();
			void shutdown();

		  protected:
			asITypeInfo* type= nullptr;
			asIScriptObject* object = nullptr;
			asIScriptFunction* updateFunction= nullptr;
			asIScriptFunction* initFunction= nullptr;
			asIScriptFunction* shutdownFunction= nullptr;
		};

		struct Panel : public Service {
		  public:
			Panel(asITypeInfo*);
			~Panel();

			Panel(Panel&) = delete;
			Panel(Panel&&) = default;
			Panel& operator=(Panel&&) = default;

			void render();

		  private:
			asIScriptFunction* renderFunction;
			asIScriptFunction* menuBarFunction;
		};

		struct Module {
		  public:
			Module(asIScriptModule*);
			~Module();

			Module(const Module&) = delete;
			Module(Module&&) = default;
			Module& operator=(Module&&) = default;

			void init();
			void update();
			void render();
			void shutdown();

			inline void invalidate() { invalidated = true; }

		  private:
			std::vector<Panel> panels;
			std::vector<Service> services;
			asIScriptModule* angelscriptModule;
			bool invalidated = false;
		};

		extern asIScriptEngine* scriptEngine;
		extern asIScriptContext* scriptContext;
		extern CScriptBuilder scriptBuilder;
		extern std::vector<Module> modules;

		extern asITypeInfo* panelBaseType;
		extern asITypeInfo* serviceBaseType;
		extern asITypeInfo* arrayStringBaseType;

		extern Module* executingModule;

		void loadModules();
		asIScriptFunction* getFactory(asITypeInfo*);

		void raiseError();
		void generateAngelscriptPredefined();
		void saveAngelscriptPredefined(const Path& path);

		void registerStructs();
		void registerFunctions();
		void extractBaseTypes();
	} // namespace AngelScriptEngine
} // namespace Deer