/*********************************************************************
 * \file   Asset.hpp
 *
 * \author Romain BOULLARD
 * \date   February 2026
 *********************************************************************/
#ifndef BIGFOOT_ENGINE_ASSET_HPP
#define BIGFOOT_ENGINE_ASSET_HPP
#include <Engine/BigFile/Asset/AssetHeader_generated.hpp>

#include <Engine/EngineAssertHandler.hpp>

#include <EASTL/vector.h>
#include <rapidhash.h>

namespace Bigfoot
{
template<typename FLAT_ASSET>
concept FlatAssetConcept = requires(FLAT_ASSET p_flatAsset) {
    requires std::is_base_of_v<::flatbuffers::Table, FLAT_ASSET> &&
                 std::derived_from<typename FLAT_ASSET::NativeTableType, ::flatbuffers::NativeTable>;

    { FLAT_ASSET::GetFullyQualifiedName() } -> std::convertible_to<const char*>;
    { p_flatAsset.asset_header() } -> std::same_as<const Bigfoot::Flat::AssetHeader*>;
};

template<FlatAssetConcept FLAT_ASSET>
class FlatAssetWrapper
{
  private:
    using FLAT_ASSET_NATIVE = FLAT_ASSET::NativeTableType;
    using FLAT_ASSET_BUILDER = FLAT_ASSET::Builder;

  public:
    FlatAssetWrapper()
    {
        m_asset.asset_header = eastl::make_unique<Flat::AssetHeaderT>();
        m_asset.asset_header->type_id = TypeID();
        m_asset.asset_header->type_name = TypeName();
    }

    FlatAssetWrapper(const eastl::span<const std::byte> p_flatBuffer)
    {
        flatbuffers::Verifier::Options verifierOptions;
        flatbuffers::Verifier verifier {std::bit_cast<const std::uint8_t*>(p_flatBuffer.data()),
                                        p_flatBuffer.size(),
                                        verifierOptions};
        CRITICAL_ASSERT(EngineAssertHandler,
                        verifier.VerifyBuffer<FLAT_ASSET>(),
                        "Flatbuffer verifier failed for FLAT_ASSET!");

        const FLAT_ASSET* flatAsset = flatbuffers::GetRoot<FLAT_ASSET>(p_flatBuffer.data());
        CRITICAL_ASSERT(EngineAssertHandler,
                        flatAsset->asset_header()->type_id()->value() == TypeID() &&
                            (std::strcmp(flatAsset->asset_header()->type_name()->c_str(), TypeName().data()) == 0),
                        "Mismatch between flatbufer type and actual type! flatbuffer = {}/{}; expected = {}/{}",
                        flatAsset->asset_header()->type_name()->c_str(),
                        flatAsset->asset_header()->type_id()->value(),
                        TypeName(),
                        TypeID());

        flatAsset->UnPackTo(&m_asset);
    }

    FlatAssetWrapper(const FlatAssetWrapper& p_assetWrapper) = delete;
    FlatAssetWrapper(FlatAssetWrapper&& p_assetWrapper) = delete;

    ~FlatAssetWrapper() = default;

    eastl::vector<std::byte> Pack() const
    {
        flatbuffers::FlatBufferBuilder builder;
        builder.Finish(FLAT_ASSET::Pack(builder, &m_asset));

        eastl::vector<std::byte> result {builder.GetSize()};
        eastl::transform(builder.GetBufferSpan().begin(),
                         builder.GetBufferSpan().end(),
                         result.begin(),
                         [](const std::uint8_t p_value)
                         {
                             return static_cast<std::byte>(p_value);
                         });

        return result;
    }

    [[nodiscard]]
    FLAT_ASSET_NATIVE& Asset()
    {
        return m_asset;
    }

    [[nodiscard]]
    const FLAT_ASSET_NATIVE& Asset() const
    {
        return m_asset;
    }

    static AssetTypeID TypeID()
    {
        static eastl::string_view typeName = TypeName();
        static AssetTypeID typeID = rapidhash(typeName.data(), typeName.size());

        return typeID;
    }

    static eastl::string_view TypeName()
    {
        static eastl::string_view typeName {FLAT_ASSET::GetFullyQualifiedName()};

        return typeName;
    }

    FlatAssetWrapper& operator=(const FlatAssetWrapper p_assetWrapper) = delete;
    FlatAssetWrapper& operator=(FlatAssetWrapper&& p_assetWrapper) = delete;

  private:
    FLAT_ASSET_NATIVE m_asset;
};
}   // namespace Bigfoot

#endif
