#include "stats.h"
#include "application_class.h"

stats::stats() : fps_(nullptr), drawcalls_(0)
{
    total_triangle_count_ = std::make_shared<int>(0);
    total_vertex_count_ = std::make_shared<int>(0);
}

stats::~stats()
{
    fps_ = nullptr;
}

bool stats::initialize(application_class* app)
{
    if (!app) {
        LOG_ERROR("Application pointer is null");
        R_FALSE
    }
    
    drawcalls_ = 0;
    app_ = app;

    fps_ = app_->get_fps();

    if (!fps_) {
        LOG_ERROR("FPS object is null");
        R_FALSE
    }
    
    LOG_INIT("Stats initialized successfully");
    
    R_TRUE
}

void stats::update_geometric_stats()
{
    // Utiliser le système ECS pour accéder aux entités avec des composants de rendu
    if (app_ && app_->get_entity_manager())
    {
        *total_vertex_count_ = get_total_vertex_count();
        *total_triangle_count_ = get_total_triangle_count();
        
        update_visible_count();

        LOG_DEBUG("Statistics updated: " + std::to_string(*total_vertex_count_) + " vertices, " + std::to_string(*total_triangle_count_) + " triangles");
    }
}

void stats::update_visible_count()
{
    visible_triangle_count_ = get_visible_triangle_count();
}

void stats::update_display_stats()
{
    
    current_fps_ = fps_->GetFps();
    min_fps_ = fps_->GetMinFps();
    max_fps_ = fps_->GetMaxFps();
    drawcalls_ = get_draw_calls();
    current_frame_time_ = fps_->GetFrameTime();
}

int stats::get_total_vertex_count() const
{
    int totalVertices = 0;
    
    // Utiliser le système ECS pour accéder aux entités avec des composants de rendu
    if (app_ && app_->get_entity_manager())
    {
        auto entities = app_->get_entity_manager()->GetEntitiesWithComponent<ecs::RenderComponent>();
        
        for (const auto& entity : entities)
        {
            auto renderComponent = entity->GetComponent<ecs::RenderComponent>();
            if (renderComponent && renderComponent->GetModel())
            {
                totalVertices += renderComponent->GetModel()->GetVertexCount();
            }
        }
    }
    
    // Ajouter également les éventuels objets restants de l'ancien système
    for (const auto& obj : object_vec_)
    {
        if (obj && obj->get_model())
        {
            totalVertices += obj->get_model()->GetVertexCount();
        }
    }
    
    for (const auto& cube : cubes_vec_)
    {
        if (cube && cube->get_model())
        {
            totalVertices += cube->get_model()->GetVertexCount();
        }
    }
    
    for (const auto& chunk : terrain_chunk_vec_)
    {
        if (chunk && chunk->get_model())
        {
            totalVertices += chunk->get_model()->GetVertexCount();
        }
    }
    
    return totalVertices;
}

int stats::get_total_triangle_count() const
{
    int totalTriangles = 0;
    
    // Utiliser le système ECS pour accéder aux entités avec des composants de rendu
    if (app_ && app_->get_entity_manager())
    {
        auto entities = app_->get_entity_manager()->GetEntitiesWithComponent<ecs::RenderComponent>();
        
        for (const auto& entity : entities)
        {
            auto renderComponent = entity->GetComponent<ecs::RenderComponent>();
            if (renderComponent && renderComponent->GetModel())
            {
                // Dans une topologie de liste de triangles, chaque triangle utilise 3 indices
                totalTriangles += renderComponent->GetModel()->GetIndexCount() / 3;
            }
        }
    }
    
    // Ajouter également les éventuels objets restants de l'ancien système
    for (const auto& obj : object_vec_)
    {
        if (obj && obj->get_model())
        {
            totalTriangles += obj->get_model()->GetIndexCount() / 3;
        }
    }
    
    for (const auto& cube : cubes_vec_)
    {
        if (cube && cube->get_model())
        {
            totalTriangles += cube->get_model()->GetIndexCount() / 3;
        }
    }
    
    for (const auto& chunk : terrain_chunk_vec_)
    {
        if (chunk && chunk->get_model())
        {
            totalTriangles += chunk->get_model()->GetIndexCount() / 3;
        }
    }
    
    return totalTriangles;
}

int stats::get_visible_triangle_count() const
{
    int visibleTriangles = 0;
    
    // Utiliser le système ECS pour accéder aux entités avec des composants de rendu
    if (app_ && app_->get_entity_manager())
    {
        auto entities = app_->get_entity_manager()->GetEntitiesWithComponent<ecs::RenderComponent>();
        
        for (const auto& entity : entities)
        {
            auto renderComponent = entity->GetComponent<ecs::RenderComponent>();
            if (renderComponent && renderComponent->GetModel() && renderComponent->IsVisible())
            {
                // Dans une topologie de liste de triangles, chaque triangle utilise 3 indices
                visibleTriangles += renderComponent->GetModel()->GetIndexCount() / 3;
            }
        }
    }
    
    // Ajouter également les éventuels objets restants de l'ancien système
    for (const auto& obj : object_vec_)
    {
        if (obj && obj->get_model() && obj->IsVisible())
        {
            visibleTriangles += obj->get_model()->GetIndexCount() / 3;
        }
    }
    
    for (const auto& cube : cubes_vec_)
    {
        if (cube && cube->get_model() && cube->IsVisible())
        {
            visibleTriangles += cube->get_model()->GetIndexCount() / 3;
        }
    }
    
    for (const auto& chunk : terrain_chunk_vec_)
    {
        if (chunk && chunk->get_model() && chunk->IsVisible())
        {
            visibleTriangles += chunk->get_model()->GetIndexCount() / 3;
        }
    }
    
    return visibleTriangles;
}

std::string stats::get_cpu_name()
{
    char CPUBrandString[0x40] = {0};
    int CPUInfo[4] = {-1};
    
    // Fonction CPUID pour récupérer le nom du CPU
    __cpuid(CPUInfo, 0x80000000);
    unsigned int nExIds = CPUInfo[0];
    
    for (unsigned int i = 0x80000000; i <= nExIds; ++i)
    {
        __cpuid(CPUInfo, i);
        
        if (i == 0x80000002)
            memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
        else if (i == 0x80000003)
            memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
        else if (i == 0x80000004)
            memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
    }
    
    std::string result = CPUBrandString;
    
    // Nettoyer les caractères non-ASCII ou problématiques
    result.erase(std::remove_if(result.begin(), result.end(), 
                 [](unsigned char c) { return c < 32 || c > 126; }), 
                 result.end());
    
    // Supprimer les espaces multiples consécutifs
    std::string::iterator new_end = std::unique(result.begin(), result.end(),
                                   [](char a, char b) { return a == ' ' && b == ' '; });
    result.erase(new_end, result.end());
    
    // Supprimer les espaces au début et à la fin
    result.erase(0, result.find_first_not_of(' '));
    result.erase(result.find_last_not_of(' ') + 1);
    
    return result;
}

std::string stats::get_gpu_driver_version(ID3D11Device* device)
{
    IDXGIDevice* dxgiDevice = nullptr;
    IDXGIAdapter* adapter = nullptr;
    std::string driverVersion = "Inconnu";

    if (SUCCEEDED(device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice)))
    {
        if (SUCCEEDED(dxgiDevice->GetAdapter(&adapter)))
        {
            DXGI_ADAPTER_DESC desc;
            if (SUCCEEDED(adapter->GetDesc(&desc)))
            {
                // Conversion correcte de WCHAR (UTF-16) vers UTF-8
                int size_needed = WideCharToMultiByte(CP_UTF8, 0, desc.Description, -1, nullptr, 0, nullptr, nullptr);
                std::string deviceName;
                if (size_needed > 0) {
                    std::vector<char> buffer(size_needed);
                    WideCharToMultiByte(CP_UTF8, 0, desc.Description, -1, buffer.data(), size_needed, nullptr, nullptr);
                    deviceName = buffer.data();
                }

                char driverVersionStr[256];
                sprintf_s(driverVersionStr, "%s (ID: %04X:%04X)", deviceName.c_str(), desc.VendorId, desc.DeviceId);

                driverVersion = driverVersionStr;
            }
            adapter->Release();
        }
        dxgiDevice->Release();
    }

    return driverVersion;
}
