#pragma once
namespace engineapi {
	class IFileLoader
	{
	protected:
		uint32_t mFileFlag = 0;
	public:
		void SetFileFlag(uint32_t flag) {
			mFileFlag = flag;
		}
		virtual ResourceBundle LoadFile(PackagePath handle, const MetaBundle& meta) = 0;
		virtual void SaveFile(PackagePath handle, const ResourceBundle& bundle) {};
		virtual ~IFileLoader() = default;
	};
	namespace detail {
		template<typename T> struct ResourceManager_detail;

		template<typename ... Rs>
		struct ResourceManager_detail<std::tuple<Rs...>>
		{
			constexpr static array<shared_ptr<void>, sizeof...(Rs)> GenResourceTables()
			{
				return array<shared_ptr<void>, sizeof...(Rs)>{
					std::make_shared<ResourceManager::ResourceStorage<Rs>>()...
				};
			}

			constexpr static array<void(*)(ResourceManager*), sizeof...(Rs)> ReleaseTableResources()
			{
				return array<void(*)(ResourceManager*), sizeof...(Rs)>{
					[](ResourceManager* resource_man)
						{
							if (auto table = &resource_man->GetTable<Rs>())
								table->clear();
						}...
				};
			}
		};

		using ResourceHelper = ResourceManager_detail<Resources>;
	}
	template<typename Res, typename ...Args>
	inline RscHandle<Res> ResourceManager::LoaderEmplaceResource(Guid guid, Args&& ...args)
	{
		auto& table = GetTable<Res>();
		auto& control_block = table[guid]; // don't care just replace
		// attempt to put on other thread
		Res* res = new Res(std::forward<Args>(args)...);
		control_block.resource = res;
		return RscHandle<Res>{guid, res};
	}
	template<typename FLoader, typename ...Args>
	inline FLoader& ResourceManager::RegisterLoader(Name ext, Args&& ...args)
	{
		auto& ptr = mFileLoader[ext];
		if (ptr) {
			delete ptr;
		}
		ptr = new FLoader(std::forward<Args>(args)...);
		ptr->SetFileFlag(GetFileFlag(ext));
		return *(FLoader*)ptr;
	}
	template<typename Res>
	inline RscHandle<Res> ResourceManager::Load(PackagePath path, bool reload_resource)
	{
		auto& res = Load(path, reload_resource);
		return res.Get<Res>();
	}
	template<typename Res>
	inline Res* ResourceManager::Get(const RscHandle<Res>& handle)
	{
		auto& table = GetTable<Res>();
		auto itr = table.find(handle.guid);
		if (itr == table.end())
			return nullptr;
		return itr->second.resource;
	}
	inline uint32_t ResourceManager::GetFileFlag(const Name& name)
	{
		auto it = mFileFlagTable.find(name.GetID());
		if (it == mFileFlagTable.end()) {
			return 0;
		}
		return it->second.second;
	}
	inline void ResourceManager::SetFileFlag(const Name& name, uint32_t flag)
	{
		auto it = mFileFlagTable.find(name.GetID());
		if (it == mFileFlagTable.end()) {
			mFileFlagTable.emplace(name.GetID(), std::make_pair(string(name.GetView()), flag));
		}
		else {
			it->second.second = flag;
		}
	}
	template<typename Res>
	inline ResourceManager::ResourceControlBlock<Res>* ResourceManager::GetControlBlock(RscHandle<Res> handle)
	{
		auto& table = GetTable<Res>();
		return &table[handle.guid];
	}
	inline ResourceManager::FileControlBlock& ResourceManager::GetFileBlock(PackagePath path) {
		return mFileTable[NameID(path.path)];
	}
	template<typename Res>
	inline void RscHandle<Res>::Init()
	{
		res = ResourceManager::GetSingleton().Get(*this);
	}
}