#include "resource_manager.h"
#include "file_manager.h"
#include "file_handle.h"
#include "yaml/yaml.h"
namespace engineapi {
	void ResourceManager::Init()
	{
		mResourceTable = detail::ResourceHelper::GenResourceTables();
		LoadFileFlag();
	}
	void ResourceManager::LateInit()
	{
		SaveFileFlag();
		SaveDirtyFiles();
	}
	void ResourceManager::Shutdown()
	{
		constexpr static auto release_tables = detail::ResourceHelper::ReleaseTableResources();
		for (auto& elem : release_tables)
			elem(this);
		SaveFileFlag();
		SaveDirtyFiles();
	}
	IFileLoader* ResourceManager::GetLoader(Name extension)
	{
		auto itr = mFileLoader.find(extension);
		if (itr == mFileLoader.end())
			return nullptr;
		return itr->second;
	}
	ResourceBundle& ResourceManager::Load(PackagePath path, bool reload_resource)
	{
		auto it = mFileTable.find(path.path);
		if (it != mFileTable.end()) {
			if (!reload_resource) {
				return it->second.bundle;
			}
		}
		auto& res = mFileTable[path.path];
		Name ext = path.GetExtension();
		IFileLoader* loader = GetLoader(ext);
		MetaBundle meta = GetMeta(path);
		ResourceBundle bundle = loader->LoadFile(path, meta);
		MetaBundle new_meta = GetVisitMeta(bundle);
		bool is_dirty_meta = meta != new_meta;
		for (auto& elem : bundle.GetAll()) {
			std::visit([&](auto& handle) {
				using T = std::decay_t<decltype(*handle)>;
				GetControlBlock<T>(handle)->file = &res;
			}, elem);
		}
		if (is_dirty_meta || res.is_dirty) {
			mDirtyBlocks.push_back(&res);
		}
		res.path = path.SafePath();
		res.bundle = bundle;
		res.is_meta_dirty = is_dirty_meta;
		res.bundle = bundle;
		return res.bundle;
	}

	MetaBundle ResourceManager::GetMeta(PackagePath path)
	{
		FileHandle handle(path + ".meta");
		if (!handle.Open(FILE_OP::READ, mFileFlag & FileFlag::File_Binary)) {
			return {};
		}
		meta::result<MetaBundle, YAML::SerializeError> res;
		if (mFileFlag & FileFlag::File_Binary) {

		}
		else {
			string text = handle.ReadAll<string>();
			res = YAML::Text_Unserialize<MetaBundle>(text);
		}
		if (!res) {
			return {};
		}
		return res.value();
	}
	void ResourceManager::SaveMeta(PackagePath path, const MetaBundle& bundle)
	{
		FileHandle handle(path + ".meta");
		handle.Open(FILE_OP::WRITE, mFileFlag & FileFlag::File_Binary);
		if (mFileFlag & FileFlag::File_Binary) {
			
		}
		else {
			string text = YAML::Text_Serialize(bundle);
			handle.Write(text.data(), text.size());
		}
	}
	void ResourceManager::SaveDirtyFiles()
	{
		for (auto block : mDirtyBlocks) {
			if (block->is_meta_dirty) {
				block->is_meta_dirty = false;
				MetaBundle new_meta = GetVisitMeta(block->bundle);
				SaveMeta(block->path, new_meta);
			}
			if (block->is_dirty) {
				block->is_dirty = false;
				auto loader = GetLoader(block->path.GetExtension());
				loader->SaveFile(block->path, block->bundle);
			}
		}
		mDirtyBlocks.clear();
	}
	constexpr const char* FileFlagName = "/engine/assets/file_flag.meta";
	void ResourceManager::LoadFileFlag()
	{
		FileHandle handle(FileFlagName);
		handle.Open(FILE_OP::READ);
		if (handle) {
			string text = handle.ReadAll<string>();
			auto res = YAML::Text_Unserialize<table<string, uint32_t>>(text);
			if (res) {
				for (auto it : res.value()) {
					mFileFlagTable.emplace(NameID(it.first), it);
				}
				mFileFlag = GetFileFlag(".meta");
			}
		}
	}
	void ResourceManager::SaveFileFlag()
	{
		table<string, uint32_t> res;
		for (auto it : mFileFlagTable) {
			res.emplace(it.second);
		}
		FileHandle handle(FileFlagName);
		handle.Open(FILE_OP::WRITE);
		string text = YAML::Text_Serialize(res);
		handle.Write(text.data(), text.size());
	}
}

