// SoundEffect.cpp - Implementation of sound effects system
#include "SoundEffect.h"
#include <SDL3/SDL.h>
#include "audio/Audio.h"
#include "audio/AudioManager.h"
#include <cstdio>
#include <algorithm>
#include <random>
#include <cstring>
#include <memory>

#ifdef _WIN32
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <objbase.h>
#include <wrl/client.h>
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "ole32.lib")
using Microsoft::WRL::ComPtr;
#elif defined(__APPLE__)
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

// SoundEffect implementation
bool SoundEffect::load(const std::string& filePath) {
    // Determine file type by extension
    std::string extension = filePath.substr(filePath.find_last_of('.') + 1);
    std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
    
    bool success = false;
    if (extension == "wav") {
        success = loadWAV(filePath);
    } else if (extension == "mp3") {
        success = loadMP3(filePath);
    } else {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Unsupported file format: %s", extension.c_str());
        return false;
    }
    
    if (!success) {
        return false;
    }
    
    loaded = true;
    return true;
}

void SoundEffect::play(float volume) {
    if (!loaded || pcmData.empty()) {
    SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Cannot play - loaded=%d, pcmData.size()=%zu", loaded, pcmData.size());
        return;
    }
        
    // Calculate final volume
    float finalVolume = defaultVolume * volume;
    finalVolume = (std::max)(0.0f, (std::min)(1.0f, finalVolume));
    
    // Use the simple audio player to play this sound
    SimpleAudioPlayer::instance().playSound(pcmData, channels, sampleRate, finalVolume);
}

void SoundEffect::setVolume(float volume) {
    defaultVolume = (std::max)(0.0f, (std::min)(1.0f, volume));
}

// SimpleAudioPlayer implementation
SimpleAudioPlayer& SimpleAudioPlayer::instance() {
    static SimpleAudioPlayer inst;
    return inst;
}

bool SimpleAudioPlayer::init() {
    if (initialized) {
        return true;
    }
    
    initialized = true;
    //std::printf("[SimpleAudioPlayer] Initialized\n");
    return true;
}

void SimpleAudioPlayer::shutdown() {
    initialized = false;
    //std::printf("[SimpleAudioPlayer] Shut down\n");
}

void SimpleAudioPlayer::playSound(const std::vector<int16_t>& pcmData, int channels, int sampleRate, float volume) {
    if (!initialized || pcmData.empty()) {
        return;
    }
    // Route through shared Audio mixer so SFX always play over music
    if (auto sys = AudioManager::get()) {
        sys->playSfx(pcmData, channels, sampleRate, volume);
    }
}

bool SoundEffect::loadWAV(const std::string& filePath) {
    SDL_AudioSpec wavSpec;
    Uint8* wavBuffer;
    Uint32 wavLength;
    
    if (!SDL_LoadWAV(filePath.c_str(), &wavSpec, &wavBuffer, &wavLength)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Failed to load WAV file %s: %s", 
                     filePath.c_str(), SDL_GetError());
        return false;
    }
    
    // Store audio format info
    channels = wavSpec.channels;
    sampleRate = wavSpec.freq;
    
    // Convert to 16-bit signed if needed
    if (wavSpec.format == SDL_AUDIO_S16) {
        // Already in the right format
        size_t samples = wavLength / sizeof(int16_t);
        pcmData.resize(samples);
        std::memcpy(pcmData.data(), wavBuffer, wavLength);
    } else {
        // Need to convert format
        SDL_AudioSpec srcSpec = wavSpec;
        SDL_AudioSpec dstSpec = wavSpec;
        dstSpec.format = SDL_AUDIO_S16;
        
        SDL_AudioStream* converter = SDL_CreateAudioStream(&srcSpec, &dstSpec);
        if (converter) {
            SDL_PutAudioStreamData(converter, wavBuffer, wavLength);
            SDL_FlushAudioStream(converter);
            
            int convertedLength = SDL_GetAudioStreamAvailable(converter);
            if (convertedLength > 0) {
                pcmData.resize(convertedLength / sizeof(int16_t));
                SDL_GetAudioStreamData(converter, pcmData.data(), convertedLength);
            }
            
            SDL_DestroyAudioStream(converter);
        }
    }
    
    SDL_free(wavBuffer);
    return !pcmData.empty();
}

bool SoundEffect::loadMP3(const std::string& filePath) {
#if defined(_WIN32)
    static bool mfInitialized = false;
    if (!mfInitialized) {
        if (FAILED(MFStartup(MF_VERSION))) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] MFStartup failed");
            return false;
        }
        mfInitialized = true;
    }
    
    ComPtr<IMFSourceReader> reader;
    wchar_t wpath[MAX_PATH];
    int wlen = MultiByteToWideChar(CP_UTF8, 0, filePath.c_str(), -1, wpath, MAX_PATH);
    if (!wlen) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Failed to convert path to wide char");
        return false;
    }
    
    if (FAILED(MFCreateSourceReaderFromURL(wpath, nullptr, &reader))) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Failed to create source reader for %s", filePath.c_str());
        return false;
    }
    
    // Request PCM output
    ComPtr<IMFMediaType> outType;
    MFCreateMediaType(&outType);
    outType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
    outType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
    outType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, 2);
    outType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100);
    outType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4);
    outType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100 * 4);
    outType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
    outType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK, 3);
    reader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, outType.Get());
    reader->SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE);
    
    // Read all samples
    std::vector<int16_t> tempData;
    while (true) {
        DWORD flags = 0;
        ComPtr<IMFSample> sample;
        if (FAILED(reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, &sample))) {
            break;
        }
        if (flags & MF_SOURCE_READERF_ENDOFSTREAM) {
            break;
        }
        if (!sample) {
            continue;
        }
        
        ComPtr<IMFMediaBuffer> buffer;
        if (FAILED(sample->ConvertToContiguousBuffer(&buffer))) {
            continue;
        }
        
        BYTE* data = nullptr;
        DWORD maxLen = 0, curLen = 0;
        if (SUCCEEDED(buffer->Lock(&data, &maxLen, &curLen)) && curLen) {
            size_t samples = curLen / 2;
            size_t oldSize = tempData.size();
            tempData.resize(oldSize + samples);
            std::memcpy(tempData.data() + oldSize, data, curLen);
        }
        if (data) {
            buffer->Unlock();
        }
    }
    
    if (tempData.empty()) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] No audio data decoded from %s", filePath.c_str());
        return false;
    }
    
    pcmData = std::move(tempData);
    channels = 2;
    sampleRate = 44100;
    return true;
#elif defined(__APPLE__)
    CFURLRef url = CFURLCreateFromFileSystemRepresentation(nullptr, reinterpret_cast<const UInt8*>(filePath.c_str()), filePath.size(), false);
    if (!url) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Failed to create URL for %s", filePath.c_str());
        return false;
    }

    ExtAudioFileRef audioFile = nullptr;
    OSStatus status = ExtAudioFileOpenURL(url, &audioFile);
    CFRelease(url);
    if (status != noErr || !audioFile) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] ExtAudioFileOpenURL failed (%d) for %s", static_cast<int>(status), filePath.c_str());
        return false;
    }

    AudioStreamBasicDescription clientFormat{};
    clientFormat.mSampleRate = 44100.0;
    clientFormat.mFormatID = kAudioFormatLinearPCM;
    clientFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    clientFormat.mBitsPerChannel = 16;
    clientFormat.mChannelsPerFrame = 2;
    clientFormat.mFramesPerPacket = 1;
    clientFormat.mBytesPerFrame = (clientFormat.mBitsPerChannel / 8) * clientFormat.mChannelsPerFrame;
    clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame * clientFormat.mFramesPerPacket;

    status = ExtAudioFileSetProperty(audioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
    if (status != noErr) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] Failed to set client format (%d) for %s", static_cast<int>(status), filePath.c_str());
        ExtAudioFileDispose(audioFile);
        return false;
    }

    const UInt32 framesPerBuffer = 2048;
    std::vector<int16_t> buffer(framesPerBuffer * clientFormat.mChannelsPerFrame);
    while (true) {
        AudioBufferList abl{};
        abl.mNumberBuffers = 1;
        abl.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame;
        abl.mBuffers[0].mDataByteSize = framesPerBuffer * clientFormat.mBytesPerFrame;
        abl.mBuffers[0].mData = buffer.data();

        UInt32 framesToRead = framesPerBuffer;
        status = ExtAudioFileRead(audioFile, &framesToRead, &abl);
        if (status != noErr) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] ExtAudioFileRead failed (%d) for %s", static_cast<int>(status), filePath.c_str());
            ExtAudioFileDispose(audioFile);
            return false;
        }

        if (framesToRead == 0) {
            break; // EOF
        }

        size_t samplesRead = static_cast<size_t>(framesToRead) * clientFormat.mChannelsPerFrame;
        pcmData.insert(pcmData.end(), buffer.data(), buffer.data() + samplesRead);
    }

    ExtAudioFileDispose(audioFile);
    channels = static_cast<int>(clientFormat.mChannelsPerFrame);
    sampleRate = static_cast<int>(clientFormat.mSampleRate);
    return !pcmData.empty();
#else
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[SoundEffect] MP3 support not available on this platform");
    return false;
#endif
}

// SoundEffectManager implementation
SoundEffectManager& SoundEffectManager::instance() {
    static SoundEffectManager inst;
    return inst;
}

bool SoundEffectManager::init() {
    if (initialized) {
        return true;
    }
    
    // Initialize the simple audio player
    SimpleAudioPlayer::instance().init();
    
    initialized = true;
    //std::printf("[SoundEffectManager] Initialized\n");
    return true;
}

void SoundEffectManager::shutdown() {
    soundEffects.clear();
    SimpleAudioPlayer::instance().shutdown();
    initialized = false;
    //std::printf("[SoundEffectManager] Shut down\n");
}

bool SoundEffectManager::loadSound(const std::string& id, const std::string& filePath) {
    if (!initialized) {
        std::fprintf(stderr, "[SoundEffectManager] Not initialized\n");
        return false;
    }
    
    auto soundEffect = std::make_unique<SoundEffect>();
    if (!soundEffect->load(filePath)) {
        std::fprintf(stderr, "[SoundEffectManager] Failed to load sound: %s\n", filePath.c_str());
        return false;
    }
    
    // Remove existing sound with the same ID
    soundEffects.erase(
        std::remove_if(soundEffects.begin(), soundEffects.end(),
            [&id](const auto& pair) { return pair.first == id; }),
        soundEffects.end());
    
    soundEffects.emplace_back(id, std::move(soundEffect));
    //std::printf("[SoundEffectManager] Loaded sound '%s' from %s\n", id.c_str(), filePath.c_str());
    return true;
}

void SoundEffectManager::playSound(const std::string& id, float volume) {
    if (!enabled || !initialized) {
        return;
    }
    
    auto it = std::find_if(soundEffects.begin(), soundEffects.end(),
        [&id](const auto& pair) { return pair.first == id; });
    
    if (it != soundEffects.end()) {
        it->second->play(volume * masterVolume);
    } else {
        std::fprintf(stderr, "[SoundEffectManager] Sound not found: %s\n", id.c_str());
    }
}

void SoundEffectManager::playRandomSound(const std::vector<std::string>& soundIds, float volume) {
    if (!enabled || !initialized || soundIds.empty()) {
        return;
    }
    
    static std::random_device rd;
    static std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, soundIds.size() - 1);
    
    const std::string& selectedId = soundIds[dis(gen)];
    playSound(selectedId, volume);
}

void SoundEffectManager::setMasterVolume(float volume) {
    masterVolume = (std::max)(0.0f, (std::min)(1.0f, volume));
}

void SoundEffectManager::setEnabled(bool enabled_) {
    enabled = enabled_;
}

bool SoundEffectManager::isEnabled() const {
    return enabled;
}
