// AudioComponent.h
#pragma once
#include "../component.h"
#include <Fmod/core/inc/fmod.hpp>
#include <string>
#include <filesystem>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <commdlg.h> // Pour GetOpenFileName

#include "camera_class.h"
#include "transform_component.h"

namespace ecs {
class AudioComponent : public Component {
public:
    AudioComponent() : m_system(nullptr), m_sound(nullptr), m_channel(nullptr) {}
    ~AudioComponent() override {
        if (m_sound) m_sound->release();
        // Ne pas libérer m_system ici car il peut être partagé
    }

    void Initialize() override
    {
        // create FMOD system if not already created
        if (!m_system)
        {
            FMOD_RESULT result = FMOD::System_Create(&m_system);
            if (result != FMOD_OK) {
                m_lastError = "Échec de la création du système FMOD: " + std::to_string(result);
                return;
            }
            
            result = m_system->init(512, FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED, nullptr);
            if (result != FMOD_OK) {
                m_lastError = "Échec de l'initialisation du système FMOD: " + std::to_string(result);
                m_system->release();
                m_system = nullptr;
            }
        }

        auto parent = GetParent();
        if (parent && parent->GetCamera())
        {
            SetCamera(parent->GetCamera());
        }
        
    }

    bool Load(const std::string& path) {
        if (!m_system) {
            Initialize();
            if (!m_system) return false;
        }

        m_soundPath = path;

        if (!std::filesystem::exists(path)) {
            m_lastError = "Fichier non trouvé: " + path;
            return false;
        }

        if (m_sound) {
            m_sound->release();
            m_sound = nullptr;
        }

        FMOD_MODE mode = FMOD_DEFAULT;
        if (m_spatialized)
            mode |= FMOD_3D;
        if (m_looping)
            mode |= FMOD_LOOP_NORMAL;
        else
            mode |= FMOD_LOOP_OFF;

        std::filesystem::path absolutePath = std::filesystem::absolute(path);
        
        FMOD_RESULT result = m_system->createSound(absolutePath.string().c_str(), mode, nullptr, &m_sound);
        if (result != FMOD_OK) {
            m_lastError = "Échec du chargement du son: " + std::to_string(result) + 
                          " (chemin: " + absolutePath.string() + ")";
            return false;
        }

        return true;
    }

    void Play() {
        if (m_system && m_sound) {
            bool isPlaying = false;
            if (m_channel) {
                m_channel->isPlaying(&isPlaying);
            }

            if (isPlaying) {
                m_channel->stop();
            }

            m_system->playSound(m_sound, nullptr, false, &m_channel);

            if (m_channel) {
                m_channel->setVolume(m_volume);
                m_channel->setPan(m_pan);
                m_channel->setPitch(m_pitch);
                m_channel->setMute(m_muted);
                m_channel->setPriority(m_priority);
                m_channel->setPaused(m_paused);
            }
        }
    }

    void Pause() {
        if (m_channel) {
            m_paused = true;
            m_channel->setPaused(true);
        }
    }

    void Resume() {
        if (m_channel) {
            m_paused = false;
            m_channel->setPaused(false);
        }
    }

    void Stop() {
        if (m_channel)
            m_channel->stop();
    }

    void SetCamera(camera_class* camera) {m_camera = camera; }

    void Update(float deltaTime) override {
        if (!m_spatialized) return;

        if (!m_system || !m_camera) return;

        auto parent = GetParent();
        if (!parent) return;

        auto transform = parent->GetComponent<TransformComponent>();
        if (!transform) return;

        XMVECTOR pos = transform->GetPosition();

        m_position.x = XMVectorGetX(pos);
        m_position.y = XMVectorGetY(pos);
        m_position.z = XMVectorGetZ(pos);

        if (m_channel) {
            FMOD_VECTOR velocity = { 0.f, 0.f, 0.f }; // à améliorer si possible - calculer depuis déplacement temps+position ?
            m_channel->set3DAttributes(&m_position, &velocity);
        }

        // Mise à jour du listener FMOD
        XMFLOAT3 camPos = m_camera->get_position();
        XMFLOAT3 camForward = m_camera->get_forward();
        XMFLOAT3 camUp = m_camera->get_up();

        FMOD_VECTOR listenerPos = { camPos.x, camPos.y, camPos.z };
        FMOD_VECTOR forward = { camForward.x, camForward.y, camForward.z };
        FMOD_VECTOR up = { camUp.x, camUp.y, camUp.z };

        FMOD_VECTOR listenerVel = { 0.f, 0.f, 0.f };
        
        if (m_use_velocity)
        {
            static XMFLOAT3 lastCamPos = camPos;
            XMFLOAT3 currentCamPos = camPos;

            float deltaTimeSec = deltaTime > 0.0001f ? deltaTime : 0.016f; // éviter division par zéro

            XMFLOAT3 listenerVelocity = {
                (currentCamPos.x - lastCamPos.x) / deltaTimeSec,
                (currentCamPos.y - lastCamPos.y) / deltaTimeSec,
                (currentCamPos.z - lastCamPos.z) / deltaTimeSec
            };
            lastCamPos = currentCamPos;

            listenerVel = { listenerVelocity.x, listenerVelocity.y, listenerVelocity.z };
        }
        
        m_system->set3DListenerAttributes(0, &listenerPos, &listenerVel, &forward, &up);
        m_system->update();
    }


    void OnImGuiRender() override {
        if (!m_sound) {
            ImGui::Text("No audio file loaded");

             if (ImGui::Button("Load audio file")) {
                OPENFILENAME ofn;
                wchar_t szFile[MAX_PATH] = L"";
                ZeroMemory(&ofn, sizeof(ofn));
                ofn.lStructSize = sizeof(ofn);
                ofn.hwndOwner = NULL; // Renseigner si possible avec handle de la fenêtre
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = MAX_PATH;
                ofn.lpstrFilter = L"Audio Files\0*.mp3;*.wav;*.ogg;*.flac\0All Files\0*.*\0";
                ofn.nFilterIndex = 1;
                ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

                if (GetOpenFileName(&ofn)) {
                    wchar_t exePath[MAX_PATH];
                    GetModuleFileName(NULL, exePath, MAX_PATH);
                    PathRemoveFileSpec(exePath);

                    std::wstring targetDir = std::wstring(exePath) + L"\\assets\\sounds";

                    DWORD ftyp = GetFileAttributes(targetDir.c_str());
                    if (ftyp == INVALID_FILE_ATTRIBUTES) {
                        CreateDirectory((std::wstring(exePath) + L"\\assets").c_str(), NULL);
                        CreateDirectory(targetDir.c_str(), NULL);
                    }

                    const wchar_t* filename = wcsrchr(szFile, L'\\');
                    std::wstring fileNameStr = filename ? (filename + 1) : szFile;

                    std::wstring targetPath = targetDir + L"\\" + fileNameStr;

                    if (!CopyFile(szFile, targetPath.c_str(), FALSE)) {
                        MessageBox(NULL, L"Erreur lors de la copie du fichier audio.", L"Erreur", MB_OK | MB_ICONERROR);
                    } else {
                        int utf8size = WideCharToMultiByte(CP_UTF8, 0, targetPath.c_str(), -1, NULL, 0, NULL, NULL);
                        std::string pathStr(utf8size, 0);
                        WideCharToMultiByte(CP_UTF8, 0, targetPath.c_str(), -1, &pathStr[0], utf8size, NULL, NULL);
                        pathStr.resize(utf8size - 1);

                        if (!Load(pathStr)) {
                            // erreur dans m_lastError
                        }
                    }
                }
            }

            if (!m_lastError.empty()) {
                ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", m_lastError.c_str());
            }
        } else {
            ImGui::Text("Loaded: %s", m_soundPath.c_str());

            if (ImGui::Button("Change audio")) {
                OPENFILENAME ofn;
                wchar_t szFile[MAX_PATH] = L"";
                ZeroMemory(&ofn, sizeof(ofn));
                ofn.lStructSize = sizeof(ofn);
                ofn.hwndOwner = NULL;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = MAX_PATH;
                ofn.lpstrFilter = L"Audio Files\0*.mp3;*.wav;*.ogg;*.flac\0All Files\0*.*\0";
                ofn.nFilterIndex = 1;
                ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

                if (GetOpenFileName(&ofn)) {
                    wchar_t exePath[MAX_PATH];
                    GetModuleFileName(NULL, exePath, MAX_PATH);
                    PathRemoveFileSpec(exePath);

                    std::wstring targetDir = std::wstring(exePath) + L"\\assets\\sounds";

                    DWORD ftyp = GetFileAttributes(targetDir.c_str());
                    if (ftyp == INVALID_FILE_ATTRIBUTES) {
                        CreateDirectory((std::wstring(exePath) + L"\\assets").c_str(), NULL);
                        CreateDirectory(targetDir.c_str(), NULL);
                    }

                    const wchar_t* filename = wcsrchr(szFile, L'\\');
                    std::wstring fileNameStr = filename ? (filename + 1) : szFile;

                    std::wstring targetPath = targetDir + L"\\" + fileNameStr;

                    if (!CopyFile(szFile, targetPath.c_str(), FALSE)) {
                        MessageBox(NULL, L"Erreur lors de la copie du fichier audio.", L"Erreur", MB_OK | MB_ICONERROR);
                    } else {
                        int utf8size = WideCharToMultiByte(CP_UTF8, 0, targetPath.c_str(), -1, NULL, 0, NULL, NULL);
                        std::string pathStr(utf8size, 0);
                        WideCharToMultiByte(CP_UTF8, 0, targetPath.c_str(), -1, &pathStr[0], utf8size, NULL, NULL);
                        pathStr.resize(utf8size - 1);

                        if (!Load(pathStr)) {
                            // erreur dans m_lastError
                        }
                    }
                }
            }
            ImGui::SameLine();
            if (ImGui::Button("Remove audio")) {
                Stop();
                if (m_sound) m_sound->release();
                m_sound = nullptr;
                m_soundPath.clear();
            }

            ImGui::Separator();

            if (ImGui::Button("Play")) {
                Play();
            }
            ImGui::SameLine();
            if (ImGui::Button(m_paused ? "Resume" : "Pause")) {
                if (m_paused) Resume();
                else Pause();
            }
            ImGui::SameLine();
            if (ImGui::Button("Stop")) {
                Stop();
            }

            ImGui::Separator();

            if (ImGui::SliderFloat("Volume", &m_volume, 0.0f, 1.0f)) {
                if (m_channel) m_channel->setVolume(m_volume);
            }

            if (ImGui::SliderFloat("Pan", &m_pan, -1.0f, 1.0f)) {
                if (m_channel) m_channel->setPan(m_pan);
            }

            if (ImGui::SliderFloat("Pitch", &m_pitch, 0.5f, 2.0f)) {
                if (m_channel) m_channel->setPitch(m_pitch);
            }

            if (ImGui::Checkbox("Looping", &m_looping)) {
                // Recharger le son pour appliquer le mode loop
                Load(m_soundPath);
            }

            if (ImGui::Checkbox("Spatialization 3D", &m_spatialized)) {
                if (!m_soundPath.empty()) {
                    Load(m_soundPath); // Recharger le son avec ou sans 3D selon le choix
                }
            }
            
            // check box to use velocity for doppler effect (only if spatialized)
            if (m_spatialized) {
                ImGui::SameLine();
                ImGui::Checkbox("Use Velocity for Doppler", &m_use_velocity);
            }

            if (ImGui::Checkbox("Mute", &m_muted)) {
                if (m_channel) m_channel->setMute(m_muted);
            }

            if (ImGui::SliderInt("Priority", &m_priority, 0, 256)) {
                if (m_channel) m_channel->setPriority(m_priority);
            }

            ImGui::Separator();

            if (m_channel && m_sound) {
                unsigned int pos = 0, len = 0;
                m_channel->getPosition(&pos, FMOD_TIMEUNIT_MS);
                m_sound->getLength(&len, FMOD_TIMEUNIT_MS);

                float progress = (len > 0) ? (float)pos / len : 0.0f;
                ImGui::ProgressBar(progress, ImVec2(300, 0), nullptr);

                auto to_minsec = [](unsigned int ms) {
                    unsigned int totalSec = ms / 1000;
                    return std::make_pair(totalSec / 60, totalSec % 60);
                };
                auto [curMin, curSec] = to_minsec(pos);
                auto [lenMin, lenSec] = to_minsec(len);

                ImGui::Text("%02u:%02u / %02u:%02u", curMin, curSec, lenMin, lenSec);
            }

            // afficher la position de l'objet et de la camera ainsi que la rotaion de la camera si spatialisé
            if (m_spatialized) {
                auto parent = GetParent();
                if (parent) {
                    auto transform = parent->GetComponent<TransformComponent>();
                    if (transform) {
                        XMVECTOR pos = transform->GetPosition();
                        ImGui::Text("Object Position: (%.2f, %.2f, %.2f)", XMVectorGetX(pos), XMVectorGetY(pos), XMVectorGetZ(pos));
                    }
                }
                if (m_camera) {
                    XMFLOAT3 camPos = m_camera->get_position();
                    XMFLOAT3 camRot = m_camera->get_rotation();
                    ImGui::Text("Camera Position: (%.2f, %.2f, %.2f)", camPos.x, camPos.y, camPos.z);
                    ImGui::Text("Camera Rotation: (%.2f, %.2f, %.2f)", camRot.x, camRot.y, camRot.z);
                }
            }

            if (!m_lastError.empty()) {
                ImGui::TextColored(ImVec4(1,0,0,1), "Error: %s", m_lastError.c_str());
            }
        }
    }



private:
    FMOD::System* m_system;
    FMOD::Sound* m_sound;
    FMOD::Channel* m_channel;
    std::string m_soundPath;
    std::string m_lastError;
    
    float m_volume = 1.0f;
    float m_pan = 0.0f;
    float m_pitch = 1.0f;
    bool m_looping = false;
    bool m_muted = false;
    bool m_paused = false;
    int m_priority = 128;
    bool m_spatialized = false;
    bool m_use_velocity = false;

    FMOD_VECTOR m_position = {0.0f, 0.0f, 0.0f};
    camera_class* m_camera = nullptr;
};
}
