#pragma once
#include "singleton.h"
#include "res/resource_config.h"
#include "res/resource_bundle.h"
#include "res/package_path.h"
#include "res/meta_bundle.h"
#include "meta/result.inl"
#include <memory>
#include <optional>
namespace engineapi {
    using std::array;
    using std::shared_ptr;
    class IFileLoader;
    enum class ResourceLoadError : char
    {
        ExtensionNotRegistered,
        FileDoesNotExist,
        FailedToLoadResource,
    };
    template<typename Res>
    using LoadResult = result<Res, ResourceLoadError>;
    class ResourceManager : public ISingleton<ResourceManager>
    {
    public:
        struct FileControlBlock;
        template<typename R>
        struct ResourceControlBlock;
        template<typename R>
        using ResourceStorage = hash_table<Guid, ResourceControlBlock<R>>;
        using GenericPtr = shared_ptr<void>;
    private:
        uint32_t mFileFlag;
        array<GenericPtr, ResourceCount>      mResourceTable;
        hash_table<NameID, IFileLoader*>      mFileLoader;
        hash_table<NameID, std::pair<string, uint32_t>>          mFileFlagTable;
        hash_table<NameID, FileControlBlock>  mFileTable;
        vector<FileControlBlock*>             mDirtyBlocks;
    public:
        void Init() override;
        void LateInit()override;
        void Shutdown() override;

    public:
        template<typename Res> 
        Res* Get(const RscHandle<Res>&);

        template<typename Res> 
        auto& GetTable() { 
            return *reinterpret_cast<ResourceStorage<Res>*>  (mResourceTable[ResourceID<Res>].get());
        }
        template<typename Res>
        ResourceControlBlock<Res>* GetControlBlock(RscHandle<Res> handle);
        FileControlBlock& GetFileBlock(PackagePath path);

        template<typename Res, typename ... Args> 
        [[nodiscard]] RscHandle<Res> LoaderEmplaceResource(Args&& ... args) {
            return LoaderEmplaceResource<Res>(Guid::Make(), args...);
        };
        template<typename Res, typename ... Args> 
        [[nodiscard]] RscHandle<Res> LoaderEmplaceResource(Guid, Args&& ... args);

        IFileLoader* GetLoader(Name extension);
        template<typename FLoader, typename ... Args> 
        FLoader& RegisterLoader(Name ext, Args&& ... args);

        template<typename Res>  
        RscHandle<Res> Load(PackagePath path, bool reload_resource = true);
        ResourceBundle&  Load(PackagePath path, bool reload_resource = true);
        MetaBundle GetMeta(PackagePath path);
        MetaBundle GetVisitMeta(const ResourceBundle& bundle);
        void SaveMeta(PackagePath path, const MetaBundle& bundle);
        void SaveDirtyFiles();

        void LoadFileFlag();
        void SaveFileFlag();
        uint32_t GetFileFlag(const Name& name);
        void SetFileFlag(const Name& name, uint32_t flag);
    };
    struct ResourceManager::FileControlBlock
    {
        ResourceBundle bundle;
        PackagePath path;
        bool is_dirty{ false };
        bool is_meta_dirty{ false };
    };
    template<typename R>
    struct ResourceManager::ResourceControlBlock
    {
        R*    resource;
        FileControlBlock* file;
        bool valid() const { return resource; }
    };
}
#include "resource_manager.inl"