#include "AssetManager.h"
#include "utils.h"
#include <fstream>
#include <iostream>
#include "../audio/AudioEngine.h"
#include "LoadingWindow.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include "../../Entitys/Object.h"

#include <ft2build.h>
#include FT_FREETYPE_H


std::unordered_map<uint64_t, std::shared_ptr<AssetInfo>> AssetManager::s_Assets;
std::unordered_map<std::string, uint64_t> AssetManager::s_PathToUAID;
uint64_t AssetManager::s_NextUAID = 1;



AssetType AssetManager::AssetTypeFromExtension(std::string ext)
{
    // strip leading dot
    if (!ext.empty() && ext.front() == '.')
        ext.erase(0, 1);
    // lowercase
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

    // — Image formats —
    if (ext=="png"  || ext=="jpg"  || ext=="jpeg" || ext=="bmp"  ||
        ext=="tga"  || ext=="gif"  || ext=="dds"  || ext=="webp" ||
        ext=="hdr")
        return AssetType::Image;

    // — Audio formats —
    if (ext=="wav"  || ext=="mp3"  || ext=="ogg"  || ext=="flac" ||
        ext=="aac"  || ext=="mid"  || ext=="midi" || ext=="mod"  ||
        ext=="xm")
        return AssetType::Audio;

    // — Font formats —
    if (ext=="ttf"  || ext=="otf"  || ext=="woff" || ext=="woff2"||
        ext=="fnt")
        return AssetType::Font;

    // — Shader formats —
    if (ext=="vert" || ext=="frag" || ext=="glsl" || ext=="hlsl" ||
        ext=="shader" || ext=="spv" || ext=="cg"    ||
        ext=="geom"  || ext=="tesc" || ext=="tese"  || ext=="comp")
        return AssetType::Shader;

    // — Script formats —
    if (ext=="lua"  || ext=="js"   || ext=="ts"   ||
        ext=="py"   || ext=="cs"   || ext=="rb")
        return AssetType::Script;

    // — Scene / data formats —
    if (ext=="scene"|| ext=="json"|| ext=="yaml"|| ext=="yml" ||
        ext=="xml")
        return AssetType::Scene;

    // — Video formats —
    if (ext=="mp4"  || ext=="avi"  || ext=="mov"  || ext=="webm"||
        ext=="ogv"  || ext=="mkv"  || ext=="mpg"  || ext=="mpeg"||
        ext=="flv"  || ext=="m4v")
        return AssetType::Video;

    // — Prefab format —
    if (ext=="cpfb")
        return AssetType::Prefab;

    // — Fallback —
    return AssetType::Unknown;
}




const char *MiniaudioResultToString(ma_result result)
{
    switch (result)
    {
    case MA_SUCCESS:
        return "Success";
    case MA_ERROR:
        return "Generic error";
    case MA_INVALID_ARGS:
        return "Invalid arguments";
    case MA_INVALID_OPERATION:
        return "Invalid operation";
    case MA_OUT_OF_MEMORY:
        return "Out of memory";
    case MA_OUT_OF_RANGE:
        return "Out of range";
    case MA_ACCESS_DENIED:
        return "Access denied";
    case MA_DOES_NOT_EXIST:
        return "File does not exist";
    case MA_ALREADY_EXISTS:
        return "File already exists";
    case MA_TOO_MANY_OPEN_FILES:
        return "Too many open files";
    case MA_INVALID_FILE:
        return "Invalid or corrupted file";
    case MA_TOO_BIG:
        return "File too big";
    case MA_PATH_TOO_LONG:
        return "Path too long";
    case MA_NAME_TOO_LONG:
        return "Name too long";
    case MA_NOT_DIRECTORY:
        return "Not a directory";
    case MA_IS_DIRECTORY:
        return "Is a directory";
    case MA_DIRECTORY_NOT_EMPTY:
        return "Directory not empty";
    case MA_AT_END:
        return "End of file or stream";
    case MA_NO_SPACE:
        return "No space left on device";
    case MA_BUSY:
        return "Resource busy";
    case MA_IO_ERROR:
        return "I/O error";
    case MA_INTERRUPT:
        return "Operation interrupted";
    case MA_UNAVAILABLE:
        return "Resource unavailable";
    case MA_ALREADY_IN_USE:
        return "Already in use";
    case MA_BAD_ADDRESS:
        return "Bad memory address";
    case MA_BAD_SEEK:
        return "Bad seek operation";
    case MA_BAD_PIPE:
        return "Bad pipe";
    case MA_DEADLOCK:
        return "Deadlock detected";
    case MA_TOO_MANY_LINKS:
        return "Too many symbolic links";
    case MA_NOT_IMPLEMENTED:
        return "Not implemented";
    case MA_NO_MESSAGE:
        return "No message available";
    case MA_BAD_MESSAGE:
        return "Bad message format";
    case MA_NO_DATA_AVAILABLE:
        return "No data available";
    case MA_INVALID_DATA:
        return "Invalid data";
    case MA_TIMEOUT:
        return "Operation timed out";
    case MA_NO_NETWORK:
        return "Network unavailable";
    case MA_NOT_UNIQUE:
        return "Not unique";
    case MA_NOT_SOCKET:
        return "Not a socket";
    case MA_NO_ADDRESS:
        return "No address available";
    case MA_BAD_PROTOCOL:
        return "Bad protocol";
    case MA_PROTOCOL_UNAVAILABLE:
        return "Protocol unavailable";
    case MA_PROTOCOL_NOT_SUPPORTED:
        return "Protocol not supported";
    case MA_PROTOCOL_FAMILY_NOT_SUPPORTED:
        return "Protocol family not supported";
    case MA_ADDRESS_FAMILY_NOT_SUPPORTED:
        return "Address family not supported";
    case MA_SOCKET_NOT_SUPPORTED:
        return "Socket not supported";
    case MA_CONNECTION_RESET:
        return "Connection reset";
    case MA_ALREADY_CONNECTED:
        return "Already connected";
    case MA_NOT_CONNECTED:
        return "Not connected";
    case MA_CONNECTION_REFUSED:
        return "Connection refused";
    case MA_NO_HOST:
        return "Host not found";
    case MA_IN_PROGRESS:
        return "Operation in progress";
    case MA_CANCELLED:
        return "Operation cancelled";
    case MA_MEMORY_ALREADY_MAPPED:
        return "Memory already mapped";

    case MA_CRC_MISMATCH:
        return "CRC mismatch";

    case MA_FORMAT_NOT_SUPPORTED:
        return "Audio format not supported";
    case MA_DEVICE_TYPE_NOT_SUPPORTED:
        return "Device type not supported";
    case MA_SHARE_MODE_NOT_SUPPORTED:
        return "Share mode not supported";
    case MA_NO_BACKEND:
        return "No backend available";
    case MA_NO_DEVICE:
        return "No device available";
    case MA_API_NOT_FOUND:
        return "API not found";
    case MA_INVALID_DEVICE_CONFIG:
        return "Invalid device configuration";
    case MA_LOOP:
        return "Loop detected";
    case MA_BACKEND_NOT_ENABLED:
        return "Backend not enabled";

    case MA_DEVICE_NOT_INITIALIZED:
        return "Device not initialized";
    case MA_DEVICE_ALREADY_INITIALIZED:
        return "Device already initialized";
    case MA_DEVICE_NOT_STARTED:
        return "Device not started";
    case MA_DEVICE_NOT_STOPPED:
        return "Device not stopped";
    case MA_FAILED_TO_INIT_BACKEND:
        return "Failed to initialize backend";
    case MA_FAILED_TO_OPEN_BACKEND_DEVICE:
        return "Failed to open backend device";
    case MA_FAILED_TO_START_BACKEND_DEVICE:
        return "Failed to start backend device";
    case MA_FAILED_TO_STOP_BACKEND_DEVICE:
        return "Failed to stop backend device";

    default:
        return "Unknown error code";
    }
}

void AssetManager::Init()
{
    Logger::LogOk("AssetManager Core");
}

uint64_t AssetManager::GenerateUAID()
{
    return s_NextUAID++;
}

void AssetManager::LoadAssetAsync(const std::string &path, AssetType type)
{
    if (auto itPath = s_PathToUAID.find(path); itPath != s_PathToUAID.end() && s_Assets[itPath->second]->loaded)
    {
        Logger::LogVerbose("[AssetManager] Skipping already-loaded asset: %s (UAID: %llu)", path.c_str(), itPath->second);
        return;
    }

    uint64_t uaid = GenerateUAID();
    std::shared_ptr<AssetInfo> asset;

    if (type == AssetType::Image)
        asset = std::make_shared<ImageAssetInfo>();
    else if (type == AssetType::Audio)
        asset = std::make_shared<AudioAssetInfo>();
    else if (type == AssetType::Prefab)
        asset = std::make_shared<PrefabAssetInfo>();
    else if (type == AssetType::Font)
        asset = std::make_shared<FontAssetInfo>();
    else
    {
        Logger::LogWarning("[AssetManager] Unknown Asset Type: '%d'", static_cast<int>(type));
        return;
    }

    asset->uaid = uaid;
    asset->path = path;
    asset->filename = GetFilenameFromPath(path);
    asset->filetype = ::GetFileExtension(path);
    asset->type = type;
    s_Assets[uaid] = asset;
    s_PathToUAID[path] = uaid;

    if (type == AssetType::Image)
        LoadImageInternal(path, uaid);
    else if (type == AssetType::Audio)
        LoadAudioInternal(path, uaid);
    else if (type == AssetType::Prefab)
        LoadPrefabInternal(path, uaid);
    else if (type == AssetType::Font)
        LoadFontInternal(path, uaid);
    
}

void AssetManager::LoadImageInternal(const std::string &path, uint64_t uaid)
{
    std::string ext = GetFileExtension_Name(path);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
    if (ext == "webp")
    {
        Logger::LogError("[AssetManager] WebP images are not supported: %s", path.c_str());
        return;
    }


    auto it = s_Assets.find(uaid);
    if (it == s_Assets.end())
        return;

    auto image = std::dynamic_pointer_cast<ImageAssetInfo>(it->second);
    if (!image)
        return;

    stbi_set_flip_vertically_on_load(false);

    std::ifstream file(path, std::ios::binary);
    if (!file)
    {
        Logger::LogError("[AssetManager] Failed to open image file: %s", path.c_str());
        return;
    }

    std::vector<unsigned char> fileData((std::istreambuf_iterator<char>(file)), {});
    int w, h, channels;
    stbi_uc *pixels = stbi_load_from_memory(fileData.data(), static_cast<int>(fileData.size()), &w, &h, &channels, STBI_rgb_alpha);

    if (!pixels)
    {
        Logger::LogError("[AssetManager] Failed to decode image: %s", path.c_str());
        return;
    }

    GLuint texID;
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, 0);
    stbi_image_free(pixels);

    if (texID == 0)
    {
        Logger::LogError("[AssetManager] Failed to create GL texture.");
        return;
    }

    image->size = {static_cast<float>(w), static_cast<float>(h)};
    image->textureID = texID;
    image->channels = channels;
    image->format = "GL_RGBA";
    image->loaded = true;
    image->lastModified = GetFileLastWrite(path);
    image->hash = GetFileHash(path);

    Logger::LogVerbose("[AssetManager] Loaded image: %s (%dx%d)", path.c_str(), w, h);
}

void AssetManager::LoadAudioInternal(const std::string &path, uint64_t uaid)
{
    auto it = s_Assets.find(uaid);
    if (it == s_Assets.end())
        return;

    auto audio = std::dynamic_pointer_cast<AudioAssetInfo>(it->second);
    if (!audio)
        return;

    audio->sound = new ma_sound();
    ma_result result = ma_sound_init_from_file(AudioEngine::GetEngine(), path.c_str(), 0, nullptr, nullptr, audio->sound);

    if (result != MA_SUCCESS)
    {
        Logger::LogError("[AssetManager] Failed to load audio file: %s (Reason: %s)", path.c_str(), MiniaudioResultToString(result));

        delete audio->sound;
        audio->sound = nullptr;
        return;
    }

    audio->loaded = true;
    audio->lastModified = GetFileLastWrite(path);
    audio->hash = GetFileHash(path);

    Logger::LogVerbose("[AssetManager] Loaded audio: %s", path.c_str());
}

void AssetManager::LoadPrefabInternal(const std::string &path, uint64_t uaid)
{

    
    if (auto it = s_Assets.find(uaid); it != s_Assets.end() && it->second->loaded)
        return;


    if (!FileExists(path))
    {
        Logger::LogError("[AssetManager] Prefab file not found: %s", path.c_str());
        return;
    }


    std::string fileContent;
    if (!ReadTextFile(path, fileContent))
    {
        Logger::LogError("[AssetManager] Failed to read prefab file: %s", path.c_str());
        return;
    }


    YAML::Node root;
    try
    {
        root = YAML::Load(fileContent);
    }
    catch (const YAML::Exception &e)
    {
        Logger::LogError("[AssetManager] YAML parse error in %s: %s", path.c_str(), e.what());
        return;
    }


    YAML::Node inner = root["prefabYAML"];
    if (!inner || !inner.IsMap())
    {
        Logger::LogError("[AssetManager] Missing or invalid 'prefabYAML' in: %s", path.c_str());
        return;
    }

    std::string prefabName = root["prefabName"] ? root["prefabName"].as<std::string>() : "Unnamed Prefab";


    auto prefab = std::make_shared<PrefabAssetInfo>();
    prefab->uaid = uaid;
    prefab->path = path;
    prefab->filename = GetFilenameFromPath(path);
    prefab->filetype = GetFileExtension(path);
    prefab->type = AssetType::Prefab;
    prefab->prefabYAML = inner;
    prefab->prefabName = prefabName;
    prefab->lastModified = GetFileLastWrite(path);
    prefab->hash = GetFileHash(path);
    prefab->loaded = true;

    
    prefab->prefabRoot = std::make_shared<Object>(prefabName);
    prefab->prefabRoot->Load(inner);

    
    s_Assets[uaid] = prefab;

    Logger::LogVerbose("[AssetManager] Loaded prefab: %s (UAID: %llu)", path.c_str(), uaid);
}


void AssetManager::LoadFontInternal(const std::string& path, uint64_t uaid)
{
    auto it = s_Assets.find(uaid);
    if (it == s_Assets.end()) return;
    auto fontInfo = std::static_pointer_cast<FontAssetInfo>(it->second);
    
    
    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
        Logger::LogError("[AssetManager] Could not init FreeType\n");
        return;
    }

    FT_Face face;
    if (FT_New_Face(ft, path.c_str(), 0, &face)) {
        Logger::LogError("[AssetManager] Failed to load font: '%s'\n", path.c_str());
        FT_Done_FreeType(ft);
        return;
    }

    FT_Set_Pixel_Sizes(face, 0, 48);

    int cellW = 0, cellH = 0;
    for (unsigned char c = 32; c < 127; ++c) {
        if (FT_Load_Char(face, c, FT_LOAD_RENDER) == 0) {
            cellW = std::max(cellW, (int)face->glyph->bitmap.width);
            cellH = std::max(cellH, (int)face->glyph->bitmap.rows);
        }
    }

    const int cols = 16;
    const int count = 127 - 32;
    const int rows = (count + cols - 1) / cols;

    fontInfo->atlasWidth  = cellW * cols;
    fontInfo->atlasHeight = cellH * rows;

    std::vector<unsigned char> atlas(
        fontInfo->atlasWidth * fontInfo->atlasHeight * 4, 0
    );

    for (unsigned char c = 32; c < 127; ++c) {
        if (FT_Load_Char(face, c, FT_LOAD_RENDER)) continue;
        auto& bm = face->glyph->bitmap;
        int col = (c - 32) % cols;
        int row = (c - 32) / cols;
        for (int y = 0; y < bm.rows; ++y) {
            for (int x = 0; x < bm.width; ++x) {
                int dstX = col * cellW + x;
                int dstY = row * cellH + y;
                int idx = (dstY * fontInfo->atlasWidth + dstX) * 4;
                atlas[idx + 0] = 255;
                atlas[idx + 1] = 255;
                atlas[idx + 2] = 255;
                atlas[idx + 3] = bm.buffer[y * bm.width + x];
            }
        }
    }

    glGenTextures(1, &fontInfo->atlasTextureID);
    glBindTexture(GL_TEXTURE_2D, fontInfo->atlasTextureID);
    glTexImage2D(
        GL_TEXTURE_2D, 0, GL_RGBA,
        fontInfo->atlasWidth, fontInfo->atlasHeight,
        0, GL_RGBA, GL_UNSIGNED_BYTE,
        atlas.data()
    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    fontInfo->loaded = true;

    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    Logger::LogVerbose("[AssetManager] Font atlas generated: %s (%d×%d), UAID=%llu",
       path.c_str(),
       fontInfo->atlasWidth,
       fontInfo->atlasHeight,
       static_cast<unsigned long long>(uaid));

}


const AssetInfo *AssetManager::GetAssetByID(uint64_t uaid)
{
    auto it = s_Assets.find(uaid);
    return it != s_Assets.end() ? it->second.get() : nullptr;
}

const AssetInfo *AssetManager::GetAssetByPath(const std::string &path)
{
    auto it = s_PathToUAID.find(path);
    return it != s_PathToUAID.end() ? s_Assets[it->second].get() : nullptr;
}

const std::unordered_map<uint64_t, std::shared_ptr<AssetInfo>> &AssetManager::GetAllAssets()
{
    return s_Assets;
}

void AssetManager::UnloadAsset(uint64_t uaid)
{
    auto it = s_Assets.find(uaid);
    if (it != s_Assets.end())
    {
        if (auto img = std::dynamic_pointer_cast<ImageAssetInfo>(it->second); img && img->textureID)
            glDeleteTextures(1, &img->textureID);
        if (auto audio = std::dynamic_pointer_cast<AudioAssetInfo>(it->second); audio && audio->sound)
        {
            ma_sound_uninit(audio->sound);
            delete audio->sound;
        }
        s_PathToUAID.erase(it->second->path);
        s_Assets.erase(it);
    }
}

void AssetManager::ClearAllAssets()
{
    std::vector<uint64_t> idsToUnload;
    for (const auto &[id, _] : s_Assets)
        idsToUnload.push_back(id);

    for (uint64_t id : idsToUnload)
        UnloadAsset(id);

}

void AssetManager::Save(YAML::Emitter &out)
{
    out << YAML::Key << "Assets" << YAML::BeginSeq;
    for (const auto &[uaid, asset] : s_Assets)
    {
        out << YAML::BeginMap;
        out << YAML::Key << "uaid" << YAML::Value << asset->uaid;
        out << YAML::Key << "path" << YAML::Value << asset->path;
        out << YAML::Key << "filename" << YAML::Value << asset->filename;
        out << YAML::Key << "filetype" << YAML::Value << asset->filetype;
        out << YAML::Key << "type" << YAML::Value << static_cast<int>(asset->type);
        out << YAML::Key << "hash" << YAML::Value << asset->hash;
        out << YAML::Key << "lastModified" << YAML::Value << asset->lastModified;
        asset->Save(out);
        out << YAML::EndMap;
    }
    out << YAML::EndSeq;
}

void AssetManager::Load(const YAML::Node &node)
{
    if (!node)
        return;

    LoadingWindow loadingUI;
    loadingUI.Create("Loading Assets");

    const size_t total = node.size();
    size_t index = 0;

    for (const auto &item : node)
    {

        uint64_t uaid = item["uaid"].as<uint64_t>();
        std::string path = item["path"].as<std::string>();
        AssetType type = static_cast<AssetType>(item["type"].as<int>());

        std::shared_ptr<AssetInfo> asset;
        if (type == AssetType::Image)
            asset = std::make_shared<ImageAssetInfo>();
        else if (type == AssetType::Audio)
            asset = std::make_shared<AudioAssetInfo>();
        else if (type == AssetType::Prefab)
            asset = std::make_shared<PrefabAssetInfo>();
        else if (type == AssetType::Font)
            asset = std::make_shared<FontAssetInfo>();
        else
            continue;

        float progress = static_cast<float>(index) / total;
        loadingUI.Update("Loading Asset", path, progress);

        asset->uaid = uaid;
        asset->path = path;
        asset->filename = item["filename"] ? item["filename"].as<std::string>() : GetFilenameFromPath(path);
        asset->filetype = item["filetype"] ? item["filetype"].as<std::string>() : GetFileExtension(path);
        asset->hash = item["hash"] ? item["hash"].as<std::string>() : "";
        asset->lastModified = item["lastModified"] ? item["lastModified"].as<std::time_t>() : 0;
        asset->loaded = false;

        asset->Load(item);

        s_Assets[uaid] = asset;
        s_PathToUAID[path] = uaid;

        if (uaid >= s_NextUAID)
            s_NextUAID = uaid + 1;

        if (type == AssetType::Image)
            LoadImageInternal(path, uaid);
        else if (type == AssetType::Audio)
            LoadAudioInternal(path, uaid);
        else if (type == AssetType::Prefab)
            LoadPrefabInternal(path, uaid);
        else if (type == AssetType::Font)
            LoadFontInternal(path, uaid);
        else
            Logger::LogError("[Engine Error] Unhandled Internal Asset Loading. %s", AssetTypeToString(type));

        ++index;
    }
    loadingUI.Destroy();
}

void ImageAssetInfo::Save(YAML::Emitter &out) const
{
    out << YAML::Key << "size" << YAML::Value << YAML::Flow << YAML::BeginSeq << size.x << size.y << YAML::EndSeq;
    out << YAML::Key << "channels" << YAML::Value << channels;
    out << YAML::Key << "format" << YAML::Value << format;
}

void ImageAssetInfo::Load(const YAML::Node &node)
{
    if (node["size"])
    {
        size.x = node["size"][0].as<float>();
        size.y = node["size"][1].as<float>();
    }
    channels = node["channels"] ? node["channels"].as<int>() : 4;
    format = node["format"] ? node["format"].as<std::string>() : "GL_RGBA";
}

void AudioAssetInfo::Save(YAML::Emitter &out) const {}
void AudioAssetInfo::Load(const YAML::Node &node) {}

PrefabAssetInfo::PrefabAssetInfo()
{
    type = AssetType::Prefab;
    prefabName = "New Prefab";
}

void PrefabAssetInfo::Save(YAML::Emitter &out) const
{

    out << YAML::Key << "prefabName" << YAML::Value << prefabName;

    out << YAML::Key << "prefabYAML";
    if (prefabRoot)
    {
        YAML::Emitter innerOut;
        innerOut.SetIndent(2);
        prefabRoot->Save(innerOut);

        YAML::Node parsedInner;
        try
        {
            parsedInner = YAML::Load(innerOut.c_str());
        }
        catch (const YAML::Exception &e)
        {
            Logger::LogError("[PrefabAssetInfo] Failed to parse prefabRoot YAML for saving: %s", e.what());
            return;
        }

        out << parsedInner;
    }
    else
    {
        out << prefabYAML;
    }
}

void PrefabAssetInfo::Load(const YAML::Node &node)
{
    prefabName = node["prefabName"] ? node["prefabName"].as<std::string>() : "Unnamed Prefab";

    if (!node["prefabYAML"] || !node["prefabYAML"].IsMap())
    {
        Logger::LogError("[PrefabAssetInfo] Missing or invalid 'prefabYAML' node in prefab file.");
        prefabYAML = YAML::Node();
        prefabRoot = nullptr;
        return;
    }

    prefabYAML = node["prefabYAML"];
    ReloadFromYAML();
}

void PrefabAssetInfo::ReloadFromYAML()
{
    if (!prefabYAML || !prefabYAML.IsMap())
    {
        Logger::LogError("[PrefabAsset] Invalid or missing prefab YAML node.");
        prefabRoot = nullptr;
        return;
    }

    prefabRoot = std::make_shared<Object>(prefabName);
    prefabRoot->Load(prefabYAML);
}

std::shared_ptr<Object> PrefabAssetInfo::Instantiate() const
{
    if (!prefabYAML || !prefabYAML.IsMap())
    {
        Logger::LogError("[PrefabAsset] Cannot instantiate: prefab YAML is invalid.");
        return nullptr;
    }

    auto newObj = std::make_shared<Object>(prefabName);
    newObj->Load(prefabYAML);
    return newObj;
}



FontAssetInfo::FontAssetInfo()
{
    type = AssetType::Font;
}

void FontAssetInfo::Save(YAML::Emitter& out) const
{
      out << YAML::Key << "AtlasTextureID" << YAML::Value << atlasTextureID;
      out << YAML::Key << "AtlasSize"      << YAML::Flow  << YAML::Value
          << YAML::BeginSeq << atlasWidth << atlasHeight << YAML::EndSeq;
}

void FontAssetInfo::Load(const YAML::Node& node)
{
    if (node["AtlasTextureID"])  atlasTextureID = node["AtlasTextureID"].as<GLuint>();
    if (node["AtlasSize"]) {
        auto seq = node["AtlasSize"];
        atlasWidth  = seq[0].as<int>();
        atlasHeight = seq[1].as<int>();
    }
}
