#pragma once
#include <v8.h>
#include <memory>
#include <mutex>
#include "isolate/holder.h"
#include "isolate/remote_handle.h"
#include "transferable_handle.h"

namespace catbox {

struct ModuleInfo {
	// Underlying data on the module. Some information is stored outside of v8 so there is a separate
	// struct to hold this data, which is then referenced by any number of handles.
	friend struct InstantiateRunner;
	friend class ModuleHandle;
	enum class LinkStatus { None, Linking, Linked };
	std::mutex mutex;
	class ModuleLinker* linker = nullptr;
	LinkStatus link_status = LinkStatus::None;
	std::vector<std::string> dependency_specifiers;
	std::unordered_map<std::string, std::shared_ptr<ModuleInfo>> resolutions;
	RemoteHandle<v8::Module> handle;
	std::shared_ptr<RemoteHandle<v8::Context>> context_handle;
	std::shared_ptr<RemoteHandle<v8::Value>> global_namespace;
	explicit ModuleInfo(v8::Local<v8::Module> handle);
	~ModuleInfo();
};

class ModuleHandle : public TransferableHandle {
	private:
		class ModuleHandleTransferable : public Transferable {
			private:
				std::shared_ptr<ModuleInfo> info;
			public:
				explicit ModuleHandleTransferable(std::shared_ptr<ModuleInfo> info);
				v8::Local<v8::Value> TransferIn() final;
		};

		std::shared_ptr<ModuleInfo> info;

	public:
		explicit ModuleHandle(std::shared_ptr<ModuleInfo> info);

		static v8::Local<v8::FunctionTemplate> Definition();
		std::unique_ptr<Transferable> TransferOut() final;

		v8::Local<v8::Value> GetDependencySpecifiers();
		std::shared_ptr<ModuleInfo> GetInfo() const;

		v8::Local<v8::Value> Instantiate(class ContextHandle* context_handle, v8::Local<v8::Function> callback);
		v8::Local<v8::Value> InstantiateSync(class ContextHandle* context_handle, v8::Local<v8::Function> callback);

		template <int async>
		v8::Local<v8::Value> Evaluate(v8::MaybeLocal<v8::Object> maybe_options);

		v8::Local<v8::Value> GetNamespace();
};

} // namespace catbox
