#include "sound_handler.h"
#include <cstdlib>
#include <cstring>          

sound_handler::sound_handler(HINSTANCE hInst, std::initializer_list<int> waveIds)
    : m_hInst(hInst), m_wavIds(waveIds)
{
    for (int id : m_wavIds)
        m_peaks.push_back(AnalyzeWavResource(id));
}

void sound_handler::PlayRandom()
{
    if (m_wavIds.empty()) return;

    int idx = rand() % static_cast<int>(m_wavIds.size());
    m_currentWave = idx;
    m_soundStart = Now();
    m_nextPeak = 0;

    int id = m_wavIds[idx];
    PlaySoundW(MAKEINTRESOURCEW(id), m_hInst,
        SND_RESOURCE | SND_ASYNC | SND_NODEFAULT);
}

void sound_handler::Update(double now)
{
    if (m_currentWave < 0 || m_currentWave >= static_cast<int>(m_peaks.size()))
        return;

    const auto& peaks = m_peaks[m_currentWave].times;
    double elapsed = now - m_soundStart;

    while (m_nextPeak < static_cast<int>(peaks.size()) && elapsed >= peaks[m_nextPeak]) {
        m_screamUntil = now + p_hold;
        ++m_nextPeak;
    }
}

bool sound_handler::IsAwAwA(double now) const
{
    return now < m_screamUntil;
}

sound_handler::PeakData sound_handler::AnalyzeWavResource(int resId)
{
    PeakData data;

    HRSRC hRes = FindResource(m_hInst, MAKEINTRESOURCE(resId), L"WAVE");
    if (!hRes) return data;

    HGLOBAL hData = LoadResource(m_hInst, hRes);
    if (!hData) return data;

    DWORD size = SizeofResource(m_hInst, hRes);
    const BYTE* buf = static_cast<const BYTE*>(LockResource(hData));
    if (!buf) return data;

    if (size < 44) return data;
    if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0)
        return data;

    WORD  channels = 0;
    DWORD sampleRate = 0;
    WORD  bitsPerSample = 0;
    const BYTE* pcm = nullptr;
    DWORD pcmSize = 0;

    DWORD pos = 12;
    while (pos + 8 <= size) {
        DWORD chunkSize = *reinterpret_cast<const DWORD*>(buf + pos + 4);

        if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
            channels = *reinterpret_cast<const WORD*>(buf + pos + 10);
            sampleRate = *reinterpret_cast<const DWORD*>(buf + pos + 12);
            bitsPerSample = *reinterpret_cast<const WORD*>(buf + pos + 22);
        }
        else if (memcmp(buf + pos, "data", 4) == 0) {
            pcm = buf + pos + 8;
            pcmSize = chunkSize;
            break;
        }

        pos += 8 + chunkSize;
        if (chunkSize & 1) pos++;   
    }

    if (!pcm || bitsPerSample != 16) return data;

    DWORD totalSamples = pcmSize / (channels * sizeof(int16_t));

    int maxAmp = 1;
    for (DWORD i = 0; i < pcmSize / sizeof(int16_t); i++) {
        int s = abs(static_cast<int>(
            *reinterpret_cast<const int16_t*>(pcm + i * sizeof(int16_t))));
        if (s > maxAmp) maxAmp = s;
    }

    int threshold = static_cast<int>(maxAmp * p_thresh);
    DWORD windowSamples = sampleRate / 50;      // 20 ms windows
    DWORD debounce = sampleRate / 8;            // 125 ms minimum gap
    DWORD lastPeak = 0xFFFFFFFF;

    for (DWORD s = 0; s + windowSamples <= totalSamples; s += windowSamples / 2) {
        int winMax = 0;
        for (DWORD j = 0; j < windowSamples * channels; j++) {
            DWORD byteOff = (s * channels + j) * sizeof(int16_t);
            if (byteOff + sizeof(int16_t) > pcmSize) break;
            int amp = abs(static_cast<int>(
                *reinterpret_cast<const int16_t*>(pcm + byteOff)));
            if (amp > winMax) winMax = amp;
        }

        bool gapOk = (lastPeak == 0xFFFFFFFF) || (s - lastPeak >= debounce);
        if (winMax >= threshold && gapOk && data.times.size() < p_max) {
            data.times.push_back(static_cast<double>(s) / sampleRate);
            lastPeak = s;
            if (data.times.size() >= p_max) break;
        }
    }

    return data;
}

double sound_handler::Now()
{
    static LARGE_INTEGER freq = [] {
        LARGE_INTEGER f;
        QueryPerformanceFrequency(&f);
        return f;
        }();
    LARGE_INTEGER t;
    QueryPerformanceCounter(&t);
    return static_cast<double>(t.QuadPart) / static_cast<double>(freq.QuadPart);
}