/*
 *  Copyright (C) 2024 DOSBox-X Team
 *
 *  SoundBlaster Microphone Input Support - Windows WASAPI Implementation
 */

#ifdef WIN32
#if defined(_MSC_VER) || defined(__MINGW64__)

#include "mic_input_win32.h"
#include "logging.h"
#include <cstdio>
#include <cstdarg>
#include <cmath>
#include <algorithm>
#include <vector>
#include "control.h"

// Link required libraries
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "uuid.lib")

// Define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT GUID locally
// {00000003-0000-0010-8000-00aa00389b71}
static const GUID LOCAL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
    { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };

// Reference time units (100-nanosecond intervals)
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

// Buffer duration for capture (in milliseconds)
#define CAPTURE_BUFFER_DURATION_MS  100

// Global instance
MicrophoneInput* g_MicrophoneInput = nullptr;

// ============================================================================
// MicCircularBuffer Implementation
// ============================================================================

MicCircularBuffer::MicCircularBuffer(size_t capacity)
    : buffer_(capacity), readPos_(0), writePos_(0), count_(0) {
}

MicCircularBuffer::~MicCircularBuffer() {
}

size_t MicCircularBuffer::Write(const uint8_t* data, size_t bytes) {
    std::lock_guard<std::mutex> lock(mutex_);

    size_t capacity = buffer_.size();
    size_t available = capacity - count_;
    size_t toWrite = std::min(bytes, available);

    for (size_t i = 0; i < toWrite; i++) {
        buffer_[writePos_] = data[i];
        writePos_ = (writePos_ + 1) % capacity;
    }
    count_ += toWrite;

    return toWrite;
}

size_t MicCircularBuffer::Read(uint8_t* data, size_t bytes) {
    std::lock_guard<std::mutex> lock(mutex_);

    size_t toRead = std::min(bytes, count_);
    size_t capacity = buffer_.size();

    for (size_t i = 0; i < toRead; i++) {
        data[i] = buffer_[readPos_];
        readPos_ = (readPos_ + 1) % capacity;
    }
    count_ -= toRead;

    return toRead;
}

size_t MicCircularBuffer::Available() const {
    std::lock_guard<std::mutex> lock(mutex_);
    return count_;
}

void MicCircularBuffer::Clear() {
    std::lock_guard<std::mutex> lock(mutex_);
    readPos_ = 0;
    writePos_ = 0;
    count_ = 0;
}

// ============================================================================
// MicrophoneInput Implementation
// ============================================================================

MicrophoneInput::MicrophoneInput()
    : deviceEnumerator_(nullptr)
    , device_(nullptr)
    , audioClient_(nullptr)
    , captureClient_(nullptr)
    , state_(MIC_STATE_CLOSED)
    , mixFormat_(nullptr)
    , captureThread_(nullptr)
    , stopEvent_(nullptr)
    , threadRunning_(false)
    , resamplePos_(0.0)
    , lastSampleL_(0.0f)
    , lastSampleR_(0.0f)
    , inputGain_(1.0f)  // Normal gain (no amplification)
    , selectedDeviceIndex_(-1) {

    memset(&captureFormat_, 0, sizeof(captureFormat_));
    memset(lastError_, 0, sizeof(lastError_));
}

MicrophoneInput::~MicrophoneInput() {
    Shutdown();
}

void MicrophoneInput::SetError(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vsnprintf(lastError_, sizeof(lastError_), format, args);
    va_end(args);
}

bool MicrophoneInput::GetPreferHFP() {
    const Section_prop* section = static_cast<Section_prop*>(control->GetSection("sblaster"));
    return section->Get_bool("prefer hfp");
}

bool MicrophoneInput::Initialize() {
    if (state_ != MIC_STATE_CLOSED) {
        return true;  // Already initialized
    }

    // Initialize COM for this thread
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
        SetError("Failed to initialize COM: 0x%08X", hr);
        return false;
    }

    // Create device enumerator
    hr = CoCreateInstance(
        __uuidof(MMDeviceEnumerator),
        nullptr,
        CLSCTX_ALL,
        __uuidof(IMMDeviceEnumerator),
        (void**)&deviceEnumerator_
    );

    if (FAILED(hr)) {
        SetError("Failed to create device enumerator: 0x%08X", hr);
        return false;
    }

    // Create stop event for capture thread
    stopEvent_ = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    if (!stopEvent_) {
        SetError("Failed to create stop event");
        ReleaseDevice();
        return false;
    }

    state_ = MIC_STATE_OPEN;
    return true;
}

void MicrophoneInput::Shutdown() {
    StopCapture();
    ReleaseDevice();

    if (stopEvent_) {
        CloseHandle(stopEvent_);
        stopEvent_ = nullptr;
    }

    if (deviceEnumerator_) {
        deviceEnumerator_->Release();
        deviceEnumerator_ = nullptr;
    }

    state_ = MIC_STATE_CLOSED;
}

std::vector<std::wstring> MicrophoneInput::GetDeviceList() {
    std::vector<std::wstring> devices;

    if (!deviceEnumerator_) {
        return devices;
    }

    IMMDeviceCollection* collection = nullptr;
    HRESULT hr = deviceEnumerator_->EnumAudioEndpoints(
        eCapture, DEVICE_STATE_ACTIVE, &collection);

    if (FAILED(hr)) {
        return devices;
    }

    UINT count = 0;
    collection->GetCount(&count);

    for (UINT i = 0; i < count; i++) {
        IMMDevice* device = nullptr;
        if (SUCCEEDED(collection->Item(i, &device))) {
            IPropertyStore* props = nullptr;
            if (SUCCEEDED(device->OpenPropertyStore(STGM_READ, &props))) {
                PROPVARIANT varName;
                PropVariantInit(&varName);
                if (SUCCEEDED(props->GetValue(PKEY_Device_FriendlyName, &varName))) {
                    devices.push_back(varName.pwszVal);
                    PropVariantClear(&varName);
                }
                props->Release();
            }
            device->Release();
        }
    }

    collection->Release();
    return devices;
}

bool MicrophoneInput::SelectDevice(int deviceIndex) {
    if (state_ == MIC_STATE_CAPTURING) {
        StopCapture();
    }

    ReleaseDevice();
    selectedDeviceIndex_ = deviceIndex;

    return InitializeDevice();
}

std::wstring MicrophoneInput::GetCurrentDeviceName() const {
    if (!device_) {
        return L"(none)";
    }

    IPropertyStore* props = nullptr;
    std::wstring name = L"Unknown Device";

    if (SUCCEEDED(device_->OpenPropertyStore(STGM_READ, &props))) {
        PROPVARIANT varName;
        PropVariantInit(&varName);
        if (SUCCEEDED(props->GetValue(PKEY_Device_FriendlyName, &varName))) {
            name = varName.pwszVal;
            PropVariantClear(&varName);
        }
        props->Release();
    }

    return name;
}

bool MicrophoneInput::InitializeDevice() {
    HRESULT hr;

    // Enumerate all active capture devices
    IMMDeviceCollection* collection = nullptr;

    hr = deviceEnumerator_->EnumAudioEndpoints(
        eCapture, DEVICE_STATE_ACTIVE, &collection);

    if(FAILED(hr) || !collection) {
        SetError("Failed to enumerate capture devices: 0x%08X", hr);
        return false;
    }

    UINT count = 0;
    collection->GetCount(&count);

    IMMDevice* selectedDevice = nullptr;
    int bestScore = INT_MIN;
    std::string acpname;

    for(UINT i = 0; i < count; i++) {
        IMMDevice* dev = nullptr;
        if(FAILED(collection->Item(i, &dev)))
            continue;

        int score = 0;
        std::wstring name;

        // Get device friendly name
        IPropertyStore* props = nullptr;

        if(SUCCEEDED(dev->OpenPropertyStore(STGM_READ, &props))) {
            PROPVARIANT varName;
            PropVariantInit(&varName);

            if(SUCCEEDED(props->GetValue(PKEY_Device_FriendlyName, &varName))) {
                if(varName.vt == VT_LPWSTR && varName.pwszVal) {
                    name = varName.pwszVal;
                }
            }

            PropVariantClear(&varName);
            props->Release();
        }

        // Activate temporarily to inspect format
        IAudioClient* tempClient = nullptr;
        WAVEFORMATEX* fmt = nullptr;

        if(SUCCEEDED(dev->Activate(__uuidof(IAudioClient),
            CLSCTX_ALL, nullptr,
            (void**)&tempClient))) {

            if(SUCCEEDED(tempClient->GetMixFormat(&fmt))) {

                /*
                    HFP (Hands-Free Profile) typically uses 8 kHz or 16 kHz sample rates and is mono.
                */
                bool isLikelyHFP =
                    (fmt->nSamplesPerSec <= 16000) &&
                    (fmt->nChannels == 1);

                // ---- HFP handling ----
                if(isLikelyHFP) {
                    if(!GetPreferHFP()) {
                        // Strongly penalize HFP when option is disabled
                        score -= 300;
                    }
                    else {
                        // Boost if user explicitly prefers HFP
                        if(fmt->nSamplesPerSec >= 12000) score += 300;
                        else if(fmt->nSamplesPerSec > 0) score += 200;
                    }
                }
                else {
                    if(fmt->nSamplesPerSec >= 48000) score += 100;
                    else if(fmt->nSamplesPerSec >= 32000) score += 50;
                    else score += 20;

                }
                CoTaskMemFree(fmt);
            }

            tempClient->Release();
        }

        // Select highest scoring device
        if(score > bestScore) {
            if(selectedDevice)
                selectedDevice->Release();

            selectedDevice = dev;
            selectedDevice->AddRef();
            bestScore = score;
            int size = WideCharToMultiByte(
                CP_ACP, 0,
                name.c_str(), -1,
                nullptr, 0,
                nullptr, nullptr);

            if(size > 0) {
                std::string temp(size, 0);

                WideCharToMultiByte(
                    CP_ACP, 0,
                    name.c_str(), -1,
                    &temp[0], size,
                    nullptr, nullptr);

                acpname = temp.c_str();
            }
            else {
                acpname = "(unknown)";
            }
        }

        dev->Release();
    }

    collection->Release();

    if(!selectedDevice) {
        SetError("No usable capture device found");
        return false;
    }

    device_ = selectedDevice;
    LOG_MSG("mic_input: Selected microphone device: %s", acpname.c_str());

    // Activate audio client
    hr = device_->Activate(
        __uuidof(IAudioClient),
        CLSCTX_ALL,
        nullptr,
        (void**)&audioClient_
    );

    if (FAILED(hr)) {
        SetError("Failed to activate audio client: 0x%08X", hr);
        ReleaseDevice();
        return false;
    }

    // Get default format
    hr = audioClient_->GetMixFormat(&mixFormat_);
    if (FAILED(hr)) {
        SetError("Failed to get mix format: 0x%08X", hr);
        ReleaseDevice();
        return false;
    }

    // Store capture format info
    captureFormat_.sampleRate = mixFormat_->nSamplesPerSec;
    captureFormat_.channels = mixFormat_->nChannels;

    // Handle extended format
    if (mixFormat_->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
        WAVEFORMATEXTENSIBLE* extFormat = (WAVEFORMATEXTENSIBLE*)mixFormat_;
        captureFormat_.bitsPerSample = extFormat->Samples.wValidBitsPerSample;
    } else {
        captureFormat_.bitsPerSample = mixFormat_->wBitsPerSample;
    }

    // Initialize audio client for capture
    REFERENCE_TIME bufferDuration = CAPTURE_BUFFER_DURATION_MS * REFTIMES_PER_MILLISEC;
    hr = audioClient_->Initialize(
        AUDCLNT_SHAREMODE_SHARED,
        0,
        bufferDuration,
        0,
        mixFormat_,
        nullptr
    );

    if (FAILED(hr)) {
        SetError("Failed to initialize audio client: 0x%08X", hr);
        ReleaseDevice();
        return false;
    }

    // Get capture client
    hr = audioClient_->GetService(
        __uuidof(IAudioCaptureClient),
        (void**)&captureClient_
    );

    if (FAILED(hr)) {
        SetError("Failed to get capture client: 0x%08X", hr);
        ReleaseDevice();
        return false;
    }

    return true;
}

void MicrophoneInput::ReleaseDevice() {
    if (captureClient_) {
        captureClient_->Release();
        captureClient_ = nullptr;
    }

    if (audioClient_) {
        audioClient_->Release();
        audioClient_ = nullptr;
    }

    if (device_) {
        device_->Release();
        device_ = nullptr;
    }

    if (mixFormat_) {
        CoTaskMemFree(mixFormat_);
        mixFormat_ = nullptr;
    }
}

bool MicrophoneInput::StartCapture() {
    if (state_ == MIC_STATE_CAPTURING) {
        return true;  // Already capturing
    }

    if (!audioClient_ || !captureClient_) {
        if (!InitializeDevice()) {
            return false;
        }
    }

    // Clear buffer and reset resampling state
    audioBuffer_.Clear();
    resamplePos_ = 0.0;
    lastSampleL_ = 0.0f;
    lastSampleR_ = 0.0f;

    // Reset stop event
    ResetEvent(stopEvent_);

    // Start audio client
    HRESULT hr = audioClient_->Start();
    if (FAILED(hr)) {
        SetError("Failed to start audio client: 0x%08X", hr);
        state_ = MIC_STATE_ERROR;
        return false;
    }

    // Start capture thread
    threadRunning_ = true;
    captureThread_ = CreateThread(
        nullptr, 0, CaptureThreadProc, this, 0, nullptr);

    if (!captureThread_) {
        SetError("Failed to create capture thread");
        audioClient_->Stop();
        state_ = MIC_STATE_ERROR;
        return false;
    }

    state_ = MIC_STATE_CAPTURING;
    return true;
}

void MicrophoneInput::StopCapture() {
    if (state_ != MIC_STATE_CAPTURING) {
        return;
    }

    // Signal thread to stop
    threadRunning_ = false;
    SetEvent(stopEvent_);

    // Wait for thread to finish
    if (captureThread_) {
        WaitForSingleObject(captureThread_, 1000);
        CloseHandle(captureThread_);
        captureThread_ = nullptr;
    }

    // Stop audio client
    if (audioClient_) {
        audioClient_->Stop();
    }

    state_ = MIC_STATE_OPEN;
}

DWORD WINAPI MicrophoneInput::CaptureThreadProc(LPVOID param) {
    MicrophoneInput* mic = static_cast<MicrophoneInput*>(param);

    // Initialize COM for this thread
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);

    mic->CaptureLoop();

    CoUninitialize();
    return 0;
}

void MicrophoneInput::CaptureLoop() {
    // Capture interval (process every 10ms)
    const DWORD captureInterval = 10;

    while (threadRunning_) {
        // Wait for data or stop signal
        DWORD waitResult = WaitForSingleObject(stopEvent_, captureInterval);
        if (waitResult == WAIT_OBJECT_0) {
            break;  // Stop signaled
        }

        if (!captureClient_) {
            break;
        }

        UINT32 packetLength = 0;
        HRESULT hr = captureClient_->GetNextPacketSize(&packetLength);
        if (FAILED(hr)) {
            continue;
        }

        while (packetLength > 0) {
            BYTE* data = nullptr;
            UINT32 numFrames = 0;
            DWORD flags = 0;

            hr = captureClient_->GetBuffer(&data, &numFrames, &flags, nullptr, nullptr);
            if (FAILED(hr)) {
                break;
            }

            if (numFrames > 0) {
                // Calculate bytes
                size_t bytesPerFrame = mixFormat_->nBlockAlign;
                size_t totalBytes = numFrames * bytesPerFrame;

                if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
                    // Generate silence in the expected format
                    std::vector<uint8_t> silenceData(totalBytes, 0);
                    audioBuffer_.Write(silenceData.data(), totalBytes);
                } else if (data) {
                    // Write actual audio data to circular buffer
                    audioBuffer_.Write(data, totalBytes);
                }
            }

            captureClient_->ReleaseBuffer(numFrames);
            captureClient_->GetNextPacketSize(&packetLength);
        }
    }
}

void MicrophoneInput::SetInputGain(float gain) {
    inputGain_ = std::max(0.0f, std::min(1000.0f, gain));
}

size_t MicrophoneInput::GetSamples(uint8_t* buffer, size_t requestedBytes,
                                    uint32_t targetSampleRate, bool stereo,
                                    bool signed16bit) {
    if (state_ != MIC_STATE_CAPTURING) {
        memset(buffer, signed16bit ? 0 : 0x80, requestedBytes);
        return requestedBytes;
    }

    size_t targetBytesPerSample = signed16bit ? 2 : 1;
    size_t targetChannels = stereo ? 2 : 1;
    size_t targetBytesPerFrame = targetBytesPerSample * targetChannels;
    size_t targetFrames = requestedBytes / targetBytesPerFrame;

    // Calculate source frames needed
    // Use ceil + 1 to ensure we always have enough input for the resampler.
    // At 48000->44100, ratio=1.088, so for 882 output frames we need ~960 input.
    // The +1 accounts for floating point rounding and ensures no underrun.
    double ratio = (double)captureFormat_.sampleRate / (double)targetSampleRate;
    size_t sourceFramesNeeded = (size_t)ceil(targetFrames * ratio) + 1;

    size_t sourceBytesPerFrame = mixFormat_->nBlockAlign;
    size_t sourceBytesNeeded = sourceFramesNeeded * sourceBytesPerFrame;

    // Ensure we have enough buffer space
    if (resampleBuffer_.size() < sourceBytesNeeded / sizeof(float) + 1) {
        resampleBuffer_.resize(sourceBytesNeeded / sizeof(float) + 1);
    }
    uint8_t* sourceData = reinterpret_cast<uint8_t*>(resampleBuffer_.data());

    // Read from circular buffer - only what we need
    size_t bytesRead = audioBuffer_.Read(sourceData, sourceBytesNeeded);

    if (bytesRead == 0) {
        // No data - fill with last known sample to avoid clicks
        if (signed16bit) {
            int16_t* out = (int16_t*)buffer;
            int16_t lastVal = (int16_t)(lastSampleL_ * 32767.0f);
            for (size_t i = 0; i < requestedBytes / 2; i++) {
                out[i] = lastVal;
            }
        } else {
            uint8_t lastVal = (uint8_t)(lastSampleL_ * 127.0f + 128.0f);
            memset(buffer, lastVal, requestedBytes);
        }
        return requestedBytes;
    }

    size_t sourceFrames = bytesRead / sourceBytesPerFrame;

    // Convert to float
    std::vector<float> floatData(sourceFrames * 2);

    bool sourceFloat = (captureFormat_.bitsPerSample == 32) ||
        (mixFormat_->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) ||
        (mixFormat_->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
         IsEqualGUID(((WAVEFORMATEXTENSIBLE*)mixFormat_)->SubFormat, LOCAL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT));

    for (size_t i = 0; i < sourceFrames; i++) {
        float left, right;

        if (sourceFloat) {
            float* src = (float*)(sourceData + i * sourceBytesPerFrame);
            left = src[0];
            right = (captureFormat_.channels >= 2) ? src[1] : src[0];
        } else if (captureFormat_.bitsPerSample == 16) {
            int16_t* src = (int16_t*)(sourceData + i * sourceBytesPerFrame);
            left = src[0] / 32768.0f;
            right = (captureFormat_.channels >= 2) ? src[1] / 32768.0f : left;
        } else {
            uint8_t* src = sourceData + i * sourceBytesPerFrame;
            left = (src[0] - 128) / 128.0f;
            right = (captureFormat_.channels >= 2) ? (src[1] - 128) / 128.0f : left;
        }

        floatData[i * 2] = left * inputGain_;
        floatData[i * 2 + 1] = right * inputGain_;
    }

    // Store last sample for continuity
    if (sourceFrames > 0) {
        lastSampleL_ = floatData[(sourceFrames - 1) * 2];
        lastSampleR_ = floatData[(sourceFrames - 1) * 2 + 1];
    }

    // Resample and convert
    size_t bytesWritten = ResampleAndConvert(floatData.data(), sourceFrames, buffer, requestedBytes,
                                              targetSampleRate, stereo, signed16bit);

    // If we didn't fill the buffer, pad with last sample (avoid silence gaps)
    if (bytesWritten < requestedBytes) {
        if (signed16bit) {
            int16_t* out = (int16_t*)buffer;
            int16_t lastVal = (bytesWritten >= 2) ? out[bytesWritten/2 - 1] : 0;
            for (size_t i = bytesWritten / 2; i < requestedBytes / 2; i++) {
                out[i] = lastVal;
            }
        } else {
            uint8_t lastVal = (bytesWritten >= 1) ? buffer[bytesWritten - 1] : 0x80;
            memset(buffer + bytesWritten, lastVal, requestedBytes - bytesWritten);
        }
    }

    return requestedBytes;
}

size_t MicrophoneInput::ResampleAndConvert(const float* input, size_t inputFrames,
                                            uint8_t* output, size_t maxOutputBytes,
                                            uint32_t targetRate, bool stereo,
                                            bool signed16bit) {
    if (inputFrames == 0) {
        return 0;
    }

    size_t bytesPerSample = signed16bit ? 2 : 1;
    size_t channels = stereo ? 2 : 1;
    size_t bytesPerFrame = bytesPerSample * channels;
    size_t maxOutputFrames = maxOutputBytes / bytesPerFrame;

    // Simple decimation: pick every Nth sample
    // For 48000 -> 11025, we pick roughly every 4.35th sample
    double step = (double)captureFormat_.sampleRate / (double)targetRate;
    size_t outputFrames = 0;
    double pos = 0.0;

    while (outputFrames < maxOutputFrames && (size_t)pos < inputFrames) {
        size_t idx = (size_t)pos;

        // Get sample (nearest neighbor - simple and click-free)
        float left = input[idx * 2];
        float right = input[idx * 2 + 1];

        // Clamp to valid range
        left = std::max(-1.0f, std::min(1.0f, left));
        right = std::max(-1.0f, std::min(1.0f, right));

        // Convert to target format
        if (signed16bit) {
            int16_t* out = (int16_t*)(output + outputFrames * bytesPerFrame);
            out[0] = (int16_t)(left * 32767.0f);
            if (stereo) {
                out[1] = (int16_t)(right * 32767.0f);
            }
        } else {
            // 8-bit unsigned
            uint8_t* out = output + outputFrames * bytesPerFrame;
            out[0] = (uint8_t)((left * 127.0f) + 128.0f);
            if (stereo) {
                out[1] = (uint8_t)((right * 127.0f) + 128.0f);
            }
        }

        outputFrames++;
        pos += step;
    }

    return outputFrames * bytesPerFrame;
}

// ============================================================================
// Global Functions
// ============================================================================

bool MIC_Initialize() {
    if (g_MicrophoneInput) {
        return true;  // Already initialized
    }

    g_MicrophoneInput = new MicrophoneInput();
    if (!g_MicrophoneInput->Initialize()) {
        delete g_MicrophoneInput;
        g_MicrophoneInput = nullptr;
        return false;
    }

    // Start capturing immediately
    if (!g_MicrophoneInput->StartCapture()) {
        delete g_MicrophoneInput;
        g_MicrophoneInput = nullptr;
        return false;
    }

    return true;
}

void MIC_Shutdown() {
    if (g_MicrophoneInput) {
        g_MicrophoneInput->Shutdown();
        delete g_MicrophoneInput;
        g_MicrophoneInput = nullptr;
    }
}

bool MIC_IsAvailable() {
    return g_MicrophoneInput != nullptr &&
           g_MicrophoneInput->GetState() != MIC_STATE_CLOSED &&
           g_MicrophoneInput->GetState() != MIC_STATE_ERROR;
}

void MIC_GenerateInput(uint8_t* buffer, size_t bytes,
                       uint32_t sampleRate, bool stereo, bool signed16bit) {
    if (!g_MicrophoneInput || g_MicrophoneInput->GetState() != MIC_STATE_CAPTURING) {
        // Fill with silence
        if (signed16bit) {
            memset(buffer, 0, bytes);
        } else {
            memset(buffer, 0x80, bytes);  // 8-bit unsigned silence = 128
        }
        return;
    }

    g_MicrophoneInput->GetSamples(buffer, bytes, sampleRate, stereo, signed16bit);
}

// Direct ADC - Time-based at measured average polling rate (~4500 Hz)
// Returns sample based on real elapsed time, ensuring correct playback speed.
static uint8_t directADCBuffer[32768];  // Ring buffer (~7 sec at 4500 Hz)
static const size_t DIRECT_ADC_BUFFER_SIZE = sizeof(directADCBuffer);
static const uint32_t DIRECT_ADC_RATE = 4500;  // Measured average polling rate

static LARGE_INTEGER directADCPerfFreq;
static LARGE_INTEGER directADCStartTime;
static bool directADCInitialized = false;
static size_t directADCGeneratedSamples = 0;

// Helper: Generate more samples
static void DirectADC_GenerateMore(size_t count) {
    if (!g_MicrophoneInput || g_MicrophoneInput->GetState() != MIC_STATE_CAPTURING) {
        return;
    }

    size_t remaining = count;
    while (remaining > 0) {
        size_t writePos = directADCGeneratedSamples % DIRECT_ADC_BUFFER_SIZE;
        size_t availableSpace = DIRECT_ADC_BUFFER_SIZE - writePos;
        size_t toGenerate = (remaining < availableSpace) ? remaining : availableSpace;
        toGenerate = (toGenerate < 2048) ? toGenerate : 2048;

        g_MicrophoneInput->GetSamples(
            &directADCBuffer[writePos],
            toGenerate,
            DIRECT_ADC_RATE,
            false,   // mono
            false    // 8-bit unsigned
        );

        directADCGeneratedSamples += toGenerate;
        remaining -= toGenerate;
    }
}

uint8_t MIC_GetDirectADCSample() {
    if (!g_MicrophoneInput || g_MicrophoneInput->GetState() != MIC_STATE_CAPTURING) {
        return 0x80;  // Silence
    }

    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);

    // Initialize on first call
    if (!directADCInitialized) {
        QueryPerformanceFrequency(&directADCPerfFreq);
        directADCStartTime = now;
        directADCGeneratedSamples = 0;

        // Pre-generate 100ms of audio (450 samples)
        DirectADC_GenerateMore(450);

        directADCInitialized = true;
    }

    // Calculate which sample corresponds to current real time
    double elapsedSec = (double)(now.QuadPart - directADCStartTime.QuadPart) /
                        (double)directADCPerfFreq.QuadPart;
    size_t timeSample = (size_t)(elapsedSec * DIRECT_ADC_RATE);

    // Ensure we have enough samples generated ahead (+100ms)
    size_t needed = timeSample + (DIRECT_ADC_RATE / 10);
    if (directADCGeneratedSamples < needed) {
        DirectADC_GenerateMore(needed - directADCGeneratedSamples + 450);
    }

    // Bounds check
    if (timeSample >= directADCGeneratedSamples) {
        timeSample = directADCGeneratedSamples - 1;
    }

    // Handle ring buffer wrap
    size_t oldestValid = 0;
    if (directADCGeneratedSamples > DIRECT_ADC_BUFFER_SIZE) {
        oldestValid = directADCGeneratedSamples - DIRECT_ADC_BUFFER_SIZE;
    }
    if (timeSample < oldestValid) {
        timeSample = oldestValid;
    }

    return directADCBuffer[timeSample % DIRECT_ADC_BUFFER_SIZE];
}

#endif //defined(_MSC_VER) || defined(__MINGW64__)
#endif // WIN32
