#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <signal_path/sink.h>
#include <dsp/buffer/packer.h>
#include <dsp/convert/stereo_to_mono.h>
#include <utils/flog.h>
#include <rtaudio/RtAudio.h>
#include <rnnoise.h>
#include <config.h>
#include <core.h>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <filesystem>
#include <memory>

SDRPP_MOD_INFO{
    /* Name:            */ "rnnoise_audio_sink",
    /* Description:     */ "Audio sink with RNNoise noise reduction for SDR++",
    /* Author:          */ "Jack Heinlein", 
    /* Version:         */ 1, 0, 0,
    /* Max instances    */ 1
};

ConfigManager config;

class RNNoiseAudioSink : public SinkManager::Sink {
public:
    RNNoiseAudioSink(SinkManager::Stream* stream, std::string streamName) {
        _stream = stream;
        _streamName = streamName;
        
        // Initialize DSP chain
        s2m.init(_stream->sinkOut);
        monoPacker.init(&s2m.out, 512);
        stereoPacker.init(_stream->sinkOut, 512);

        // RNNoise initialization
        rnnFrameSize = rnnoise_get_frame_size();  // Should be 480
        
        // Initialize delay buffers
        maxDelaySamples = 48000;  // 1秒 @ 48kHz
        dryDelayBufferL.resize(maxDelaySamples, 0.0f);
        dryDelayBufferR.resize(maxDelaySamples, 0.0f);
        
        // Determine model directory based on platform
#ifdef __APPLE__
        modelDirectory = std::string(getenv("HOME")) + "/Library/Application Support/sdrpp/rnnoise_model";
#else
        modelDirectory = std::string(getenv("HOME")) + "/.config/sdrpp/rnnoise_model";
#endif
        
        // Create directory if it doesn't exist
        std::filesystem::create_directories(modelDirectory);
        
        // Scan for available models
        scanModels();
        
#if RTAUDIO_VERSION_MAJOR >= 6
        audio.setErrorCallback(&errorCallback);
#endif

        // Load config with defaults
        config.acquire();
        if (!config.conf.contains(_streamName)) {
            config.conf[_streamName]["device"] = "";
            config.conf[_streamName]["devices"] = json({});
            config.conf[_streamName]["rnnoise_enabled"] = false;
            config.conf[_streamName]["attenuation_factor"] = 1.0f;
            config.conf[_streamName]["dry_delay_ms"] = 20.0f;
            config.conf[_streamName]["output_gain"] = 1.0f;
            
            // Model selection
            config.conf[_streamName]["model_selection"] = "Default";
            
            // VAD Smoothing
            config.conf[_streamName]["smooth_transitions"] = true;
            config.conf[_streamName]["vad_smoothing_factor"] = 0.8f;
            config.conf[_streamName]["vad_attack_ms"] = 5.0f;
            config.conf[_streamName]["vad_release_ms"] = 50.0f;
            config.conf[_streamName]["dynamic_mix_enabled"] = false;
            config.conf[_streamName]["vad_reduction_factor"] = 0.3f;
            
            // Filters
            config.conf[_streamName]["highpass_enabled"] = false;
            config.conf[_streamName]["highpass_freq"] = 100.0f;
            config.conf[_streamName]["lowpass_enabled"] = false;
            config.conf[_streamName]["lowpass_freq"] = 3000.0f;
            
            // Gate
            config.conf[_streamName]["gate_enabled"] = false;
            config.conf[_streamName]["gate_threshold"] = -40.0f;
            config.conf[_streamName]["gate_attack_ms"] = 5.0f;
            config.conf[_streamName]["gate_release_ms"] = 50.0f;
        }

        // Load settings
        device = config.conf[_streamName]["device"];
        rnNoiseEnabled = config.conf[_streamName]["rnnoise_enabled"];
        attenuationFactor = config.conf[_streamName]["attenuation_factor"];
        dryDelayMs = config.conf[_streamName]["dry_delay_ms"];
        outputGain = config.conf[_streamName]["output_gain"];
        
        // Model selection
        selectedModel = config.conf[_streamName]["model_selection"];
        updateModelSelection();
        
        // VAD Smoothing
        smoothTransitions = config.conf[_streamName]["smooth_transitions"];
        vadSmoothingFactor = config.conf[_streamName]["vad_smoothing_factor"];
        vadAttackMs = config.conf[_streamName]["vad_attack_ms"];
        vadReleaseMs = config.conf[_streamName]["vad_release_ms"];
        dynamicMixEnabled = config.conf[_streamName]["dynamic_mix_enabled"];
        vadReductionFactor = config.conf[_streamName]["vad_reduction_factor"];
        
        highpassEnabled = config.conf[_streamName]["highpass_enabled"];
        highpassFreq = config.conf[_streamName]["highpass_freq"];
        lowpassEnabled = config.conf[_streamName]["lowpass_enabled"];
        lowpassFreq = config.conf[_streamName]["lowpass_freq"];
        
        gateEnabled = config.conf[_streamName]["gate_enabled"];
        gateThreshold = config.conf[_streamName]["gate_threshold"];
        gateAttackMs = config.conf[_streamName]["gate_attack_ms"];
        gateReleaseMs = config.conf[_streamName]["gate_release_ms"];
        
        config.release();

        // Initialize audio devices
        RtAudio::DeviceInfo info;
#if RTAUDIO_VERSION_MAJOR >= 6
        for (int i : audio.getDeviceIds()) {
#else
        int count = audio.getDeviceCount();
        for (int i = 0; i < count; i++) {
#endif
            try {
                info = audio.getDeviceInfo(i);
#if !defined(RTAUDIO_VERSION_MAJOR) || RTAUDIO_VERSION_MAJOR < 6
                if (!info.probed) { continue; }
#endif
                if (info.outputChannels == 0) { continue; }
                if (info.isDefaultOutput) { defaultDevId = devList.size(); }
                devList.push_back(info);
                deviceIds.push_back(i);
                txtDevList += info.name;
                txtDevList += '\0';
            }
            catch (const std::exception& e) {
                flog::error("RNNoiseAudioSink Error getting audio device ({}) info: {}", i, e.what());
            }
        }
        
        selectByName(device);
    }

    ~RNNoiseAudioSink() {
        stop();
        destroyDenoiseState();
        if (customModel) {
            rnnoise_model_free(customModel);
            customModel = nullptr;
        }
    }

    void start() {
        if (running) return;
        running = doStart();
    }

    void stop() {
        if (!running) return;
        doStop();
        running = false;
    }

    void menuHandler() {
        float menuWidth = ImGui::GetContentRegionAvail().x;

        ImGui::Text("RNNoise Audio Sink v2.0");
        ImGui::Separator();

        // Device selection
        ImGui::SetNextItemWidth(menuWidth);
        if (ImGui::Combo(("Device##_rnnoise_dev_" + _streamName).c_str(), &devId, txtDevList.c_str())) {
            selectById(devId);
            config.acquire();
            config.conf[_streamName]["device"] = devList[devId].name;
            config.release(true);
        }

        // Sample rate selection
        ImGui::SetNextItemWidth(menuWidth);
        if (ImGui::Combo(("Sample Rate##_rnnoise_sr_" + _streamName).c_str(), &srId, sampleRatesTxt.c_str())) {
            sampleRate = sampleRates[srId];
            _stream->setSampleRate(sampleRate);
            updateFilters();
            updateGateParams();
            updateDelayBuffer();
            updateVADParams();
            if (running) {
                doStop();
                doStart();
            }
            config.acquire();
            config.conf[_streamName]["devices"][devList[devId].name] = sampleRate;
            config.release(true);
        }
        
        if (sampleRate != 48000) {
            ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "⚠ RNNoise works best at 48kHz");
        }
        
        ImGui::Separator();

        // RNNoise Section
        ImGui::Text("Noise Reduction");
        bool wasEnabled = rnNoiseEnabled;
        if (ImGui::Checkbox(("Enable RNNoise##_rnnoise_" + _streamName).c_str(), &rnNoiseEnabled)) {
            if (rnNoiseEnabled && !wasEnabled) {
                createDenoiseState();
            } else if (!rnNoiseEnabled && wasEnabled) {
                destroyDenoiseState();
            }
            config.acquire();
            config.conf[_streamName]["rnnoise_enabled"] = rnNoiseEnabled;
            config.release(true);
        }

        if (rnNoiseEnabled) {
            ImGui::Separator();
            ImGui::Text("RNNoise Model");
            
            std::string modelListStr = buildModelListString();
            int currentModelIdx = getModelIndex(selectedModel);
            
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::Combo(("##model_select_" + _streamName).c_str(), &currentModelIdx, modelListStr.c_str())) {
                if (currentModelIdx >= 0 && currentModelIdx < modelList.size()) {
                    selectedModel = modelList[currentModelIdx];
                    updateModelSelection();
                    recreateDenoiseStates();
                    config.acquire();
                    config.conf[_streamName]["model_selection"] = selectedModel;
                    config.release(true);
                }
            }
            
            // Refresh button to rescan models
            ImGui::SameLine();
            if (ImGui::Button(("Refresh##_refresh_models_" + _streamName).c_str())) {
                scanModels();
                modelListStr = buildModelListString();
            }
            
            if (selectedModel.find("(Custom)") != std::string::npos) {
                if (customModel) {
                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "✓ Model loaded");
                } else {
                    ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "⚠ Failed to load");
                }
            }
            
            ImGui::Text("Model Dir: %s", modelDirectory.c_str());
            
            ImGui::Separator();
            
            // Mix control
            ImGui::Text("RNNoise Mix (Dry/Wet)");
            ImGui::SetNextItemWidth(menuWidth);
            float mixPercent = attenuationFactor * 100.0f;
            if (ImGui::SliderFloat(("##attenuation_" + _streamName).c_str(), &mixPercent, 0.0f, 100.0f, "%.1f%%")) {
                attenuationFactor = mixPercent / 100.0f;
                config.acquire();
                config.conf[_streamName]["attenuation_factor"] = attenuationFactor;
                config.release(true);
            }
            
            // VAD Smoothing Section
            ImGui::Separator();
            ImGui::Text("VAD Smoothing");
            
            if (ImGui::Checkbox(("Smooth Transitions##_smooth_" + _streamName).c_str(), &smoothTransitions)) {
                config.acquire();
                config.conf[_streamName]["smooth_transitions"] = smoothTransitions;
                config.release(true);
            }
            if (ImGui::IsItemHovered()) {
                ImGui::SetTooltip("Smooths voice detection changes\nTurn OFF if you hear phasing/chorus effects");
            }
            
            if (smoothTransitions) {
                ImGui::Text("Smoothing Factor");
                ImGui::SetNextItemWidth(menuWidth * 0.7f);
                float smoothingValue = vadSmoothingFactor * 1000.0f; // Convert to 0-990 range
                if (ImGui::SliderFloat(("##vad_smooth_" + _streamName).c_str(), &smoothingValue, 0.0f, 990.0f, "%.0f")) {
                    vadSmoothingFactor = smoothingValue / 1000.0f;
                    updateVADParams();
                    config.acquire();
                    config.conf[_streamName]["vad_smoothing_factor"] = vadSmoothingFactor;
                    config.release(true);
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetTooltip("Higher = smoother but slower response\nDefault: 800");
                }
                
                // VAD Attack and Release times
                ImGui::Text("VAD Attack Time");
                ImGui::SetNextItemWidth(menuWidth * 0.7f);
                if (ImGui::SliderFloat(("ms##vad_attack_" + _streamName).c_str(), &vadAttackMs, 1.0f, 100.0f, "%.1f ms")) {
                    updateVADParams();
                    config.acquire();
                    config.conf[_streamName]["vad_attack_ms"] = vadAttackMs;
                    config.release(true);
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetTooltip("Time to react when voice is detected\nLower = faster response");
                }
                
                ImGui::Text("VAD Release Time");
                ImGui::SetNextItemWidth(menuWidth * 0.7f);
                if (ImGui::SliderFloat(("ms##vad_release_" + _streamName).c_str(), &vadReleaseMs, 10.0f, 500.0f, "%.1f ms")) {
                    updateVADParams();
                    config.acquire();
                    config.conf[_streamName]["vad_release_ms"] = vadReleaseMs;
                    config.release(true);
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetTooltip("Time to fade out when voice stops\nHigher = smoother fadeout");
                }
                
                if (ImGui::Checkbox(("Dynamic Mix##_dyn_mix_" + _streamName).c_str(), &dynamicMixEnabled)) {
                    config.acquire();
                    config.conf[_streamName]["dynamic_mix_enabled"] = dynamicMixEnabled;
                    config.release(true);
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetTooltip("Reduce denoising when voice is detected");
                }
                
                if (dynamicMixEnabled) {
                    ImGui::SetNextItemWidth(menuWidth);
                    if (ImGui::SliderFloat(("VAD Reduction##_vad_red_" + _streamName).c_str(), &vadReductionFactor, 0.1f, 0.5f, "%.2f")) {
                        config.acquire();
                        config.conf[_streamName]["vad_reduction_factor"] = vadReductionFactor;
                        config.release(true);
                    }
                }
            }
            
            // VAD Status
            if (running && lastVadProbability > 0.0f) {
                ImGui::Text("Voice Activity: %.0f%%", lastVadProbability * 100.0f);
                ImGui::SameLine();
                ImGui::ProgressBar(lastVadProbability, ImVec2(100, 0));
            }
            
            ImGui::Separator();
            
            // Dry/Wet Timing
            ImGui::Text("Adjust Dry/Wet Timing");
            ImGui::SetNextItemWidth(menuWidth * 0.3f);
            if (ImGui::InputFloat(("ms##delay_input_" + _streamName).c_str(), &dryDelayMs, 0.1f, 1.0f, "%.1f")) {
                if (dryDelayMs < 0.0f) dryDelayMs = 0.0f;
                if (dryDelayMs > 50.0f) dryDelayMs = 50.0f;
                updateDelayBuffer();
                config.acquire();
                config.conf[_streamName]["dry_delay_ms"] = dryDelayMs;
                config.release(true);
            }
            ImGui::SameLine();
            ImGui::Text("(%.0f samples)", (float)delaySamples);
        }
        
        ImGui::Separator();
        
        // Filters Section
        ImGui::Text("Filters");
        
        // High-pass
        if (ImGui::Checkbox(("High-pass##_hp_" + _streamName).c_str(), &highpassEnabled)) {
            updateFilters();
            config.acquire();
            config.conf[_streamName]["highpass_enabled"] = highpassEnabled;
            config.release(true);
        }
        if (highpassEnabled) {
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::SliderFloat(("Hz##_hp_freq_" + _streamName).c_str(), &highpassFreq, 20.0f, 500.0f, "%.0f Hz")) {
                updateFilters();
                config.acquire();
                config.conf[_streamName]["highpass_freq"] = highpassFreq;
                config.release(true);
            }
        }
        
        // Low-pass
        if (ImGui::Checkbox(("Low-pass##_lp_" + _streamName).c_str(), &lowpassEnabled)) {
            updateFilters();
            config.acquire();
            config.conf[_streamName]["lowpass_enabled"] = lowpassEnabled;
            config.release(true);
        }
        if (lowpassEnabled) {
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::SliderFloat(("Hz##_lp_freq_" + _streamName).c_str(), &lowpassFreq, 1000.0f, 8000.0f, "%.0f Hz")) {
                updateFilters();
                config.acquire();
                config.conf[_streamName]["lowpass_freq"] = lowpassFreq;
                config.release(true);
            }
        }
        
        ImGui::Separator();
        
        // Gate Section
        ImGui::Text("Noise Gate");
        if (ImGui::Checkbox(("Enable Gate##_gate_" + _streamName).c_str(), &gateEnabled)) {
            config.acquire();
            config.conf[_streamName]["gate_enabled"] = gateEnabled;
            config.release(true);
        }
        
        if (gateEnabled) {
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::SliderFloat(("Threshold##_gate_thresh_" + _streamName).c_str(), &gateThreshold, -60.0f, -10.0f, "%.1f dB")) {
                config.acquire();
                config.conf[_streamName]["gate_threshold"] = gateThreshold;
                config.release(true);
            }
            
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::SliderFloat(("Attack##_gate_attack_" + _streamName).c_str(), &gateAttackMs, 0.1f, 50.0f, "%.1f ms")) {
                updateGateParams();
                config.acquire();
                config.conf[_streamName]["gate_attack_ms"] = gateAttackMs;
                config.release(true);
            }
            
            ImGui::SetNextItemWidth(menuWidth);
            if (ImGui::SliderFloat(("Release##_gate_release_" + _streamName).c_str(), &gateReleaseMs, 1.0f, 500.0f, "%.1f ms")) {
                updateGateParams();
                config.acquire();
                config.conf[_streamName]["gate_release_ms"] = gateReleaseMs;
                config.release(true);
            }
        }
        
        ImGui::Separator();
        
        // Output
        ImGui::Text("Output Gain");
        ImGui::SetNextItemWidth(menuWidth);
        if (ImGui::SliderFloat(("##_output_gain_" + _streamName).c_str(), &outputGain, 0.1f, 2.0f, "%.2fx")) {
            config.acquire();
            config.conf[_streamName]["output_gain"] = outputGain;
            config.release(true);
        }
        
        ImGui::Separator();
        
        // Reset to Default button
        if (ImGui::Button(("Reset to Default##_reset_all_" + _streamName).c_str())) {
            // Reset all parameters to default values
            rnNoiseEnabled = false;
            selectedModel = "Default";
            attenuationFactor = 1.0f;
            dryDelayMs = 20.0f;
            outputGain = 1.0f;
            
            // VAD Smoothing
            smoothTransitions = true;
            vadSmoothingFactor = 0.8f;
            vadAttackMs = 5.0f;
            vadReleaseMs = 50.0f;
            dynamicMixEnabled = false;
            vadReductionFactor = 0.3f;
            
            // Filters
            highpassEnabled = false;
            highpassFreq = 100.0f;
            lowpassEnabled = false;
            lowpassFreq = 3000.0f;
            
            // Gate
            gateEnabled = false;
            gateThreshold = -40.0f;
            gateAttackMs = 5.0f;
            gateReleaseMs = 50.0f;
            
            // Update everything
            updateModelSelection();
            recreateDenoiseStates();
            updateFilters();
            updateGateParams();
            updateDelayBuffer();
            updateVADParams();
            
            // Save to config
            config.acquire();
            config.conf[_streamName]["rnnoise_enabled"] = rnNoiseEnabled;
            config.conf[_streamName]["model_selection"] = selectedModel;
            config.conf[_streamName]["attenuation_factor"] = attenuationFactor;
            config.conf[_streamName]["dry_delay_ms"] = dryDelayMs;
            config.conf[_streamName]["output_gain"] = outputGain;
            config.conf[_streamName]["smooth_transitions"] = smoothTransitions;
            config.conf[_streamName]["vad_smoothing_factor"] = vadSmoothingFactor;
            config.conf[_streamName]["vad_attack_ms"] = vadAttackMs;
            config.conf[_streamName]["vad_release_ms"] = vadReleaseMs;
            config.conf[_streamName]["dynamic_mix_enabled"] = dynamicMixEnabled;
            config.conf[_streamName]["vad_reduction_factor"] = vadReductionFactor;
            config.conf[_streamName]["highpass_enabled"] = highpassEnabled;
            config.conf[_streamName]["highpass_freq"] = highpassFreq;
            config.conf[_streamName]["lowpass_enabled"] = lowpassEnabled;
            config.conf[_streamName]["lowpass_freq"] = lowpassFreq;
            config.conf[_streamName]["gate_enabled"] = gateEnabled;
            config.conf[_streamName]["gate_threshold"] = gateThreshold;
            config.conf[_streamName]["gate_attack_ms"] = gateAttackMs;
            config.conf[_streamName]["gate_release_ms"] = gateReleaseMs;
            config.release(true);
        }
        if (ImGui::IsItemHovered()) {
            ImGui::SetTooltip("Reset all parameters to factory defaults");
        }
    }

private:
    void scanModels() {
        modelList.clear();
        
        // Add built-in models
        modelList.push_back("Default");
        modelList.push_back("Default Little");
        
        // Scan directory for .bin files
        if (std::filesystem::exists(modelDirectory)) {
            for (const auto& entry : std::filesystem::directory_iterator(modelDirectory)) {
                if (entry.path().extension() == ".bin") {
                    std::string filename = entry.path().filename().string();
                    modelList.push_back(filename + " (Custom)");
                }
            }
        }
        
        // Ensure selected model is still in list
        bool found = false;
        for (const auto& model : modelList) {
            if (model == selectedModel) {
                found = true;
                break;
            }
        }
        if (!found) {
            selectedModel = "Default";
        }
    }
    
    void updateVADParams() {
        // Convert ms to smoothing factors based on frame processing rate
        float framesPerSecond = (float)sampleRate / (float)rnnFrameSize;
        
        // Calculate attack and release factors
        float attackSamples = (vadAttackMs / 1000.0f) * framesPerSecond;
        float releaseSamples = (vadReleaseMs / 1000.0f) * framesPerSecond;
        
        vadAttackFactor = expf(-1.0f / attackSamples);
        vadReleaseFactor = expf(-1.0f / releaseSamples);
    }
    
    std::string buildModelListString() {
        std::string result;
        for (const auto& model : modelList) {
            result += model;
            result += '\0';
        }
        return result;
    }
    
    int getModelIndex(const std::string& modelName) {
        for (int i = 0; i < modelList.size(); i++) {
            if (modelList[i] == modelName) {
                return i;
            }
        }
        return 0; // Default
    }
    
    void updateModelSelection() {
        if (selectedModel == "Default") {
            useBuiltinModel = true;
            useLittleModel = false;
            if (customModel) {
                rnnoise_model_free(customModel);
                customModel = nullptr;
                modelData.clear();
            }
        }
        else if (selectedModel == "Default Little") {
            useBuiltinModel = true;
            useLittleModel = true;
            if (customModel) {
                rnnoise_model_free(customModel);
                customModel = nullptr;
                modelData.clear();
            }
        }
        else if (selectedModel.find("(Custom)") != std::string::npos) {
            // Extract filename from model name
            std::string filename = selectedModel.substr(0, selectedModel.find(" (Custom)"));
            std::string fullPath = modelDirectory + "/" + filename;
            loadModel(fullPath);
            useBuiltinModel = false;
            useLittleModel = false;
        }
    }
    
    bool loadModel(const std::string& path) {
        if (path.empty()) return false;
        
        // Load model file
        std::ifstream file(path, std::ios::binary | std::ios::ate);
        if (!file) {
            flog::error("Failed to open model file: {}", path);
            return false;
        }
        
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg);
        
        modelData.resize(size);
        if (!file.read(modelData.data(), size)) {
            flog::error("Failed to read model file: {}", path);
            modelData.clear();
            return false;
        }
        
        // Free old model if exists
        if (customModel) {
            rnnoise_model_free(customModel);
            customModel = nullptr;
        }
        
        // Load model from data  
        customModel = rnnoise_model_from_buffer(modelData.data(), modelData.size());
        if (!customModel) {
            flog::error("Failed to load RNNoise model from file: {}", path);
            modelData.clear();
            return false;
        }
        
        flog::info("Successfully loaded RNNoise model: {}", path);
        return true;
    }
    
    void recreateDenoiseStates() {
        destroyDenoiseState();
        if (rnNoiseEnabled) {
            createDenoiseState();
        }
    }
    
    void createDenoiseState() {
        destroyDenoiseState();
        
        // Create with appropriate model
        if (!useBuiltinModel && customModel) {
            // Use custom loaded model
            rnNoiseStateL = rnnoise_create(customModel);
            rnNoiseStateR = rnnoise_create(customModel);
        } else {
            // Use built-in model (default or little - handled by RNNoise internally)
            // Note: For little model, you'd need to rebuild RNNoise with rnnoise_data_little.c
            rnNoiseStateL = rnnoise_create(nullptr);
            rnNoiseStateR = rnnoise_create(nullptr);
        }
        
        // Reset VAD smoothing
        smoothedVadL = 0.0f;
        smoothedVadR = 0.0f;
        lastVadProbability = 0.0f;
    }
    
    void destroyDenoiseState() {
        if (rnNoiseStateL) {
            rnnoise_destroy(rnNoiseStateL);
            rnNoiseStateL = nullptr;
        }
        if (rnNoiseStateR) {
            rnnoise_destroy(rnNoiseStateR);
            rnNoiseStateR = nullptr;
        }
    }
    
    void updateDelayBuffer() {
        delaySamples = (int)(dryDelayMs * sampleRate / 1000.0f);
        delaySamples = std::min(delaySamples, maxDelaySamples - 1);
    }
    
    void updateFilters() {
        if (highpassEnabled) {
            float w = 2.0f * M_PI * highpassFreq / sampleRate;
            float cosw = cos(w);
            float sinw = sin(w);
            float alpha = sinw / sqrt(2.0f);
            
            hp_b0 = ((1.0f + cosw) / 2.0f) / (1.0f + alpha);
            hp_b1 = (-(1.0f + cosw)) / (1.0f + alpha);
            hp_b2 = ((1.0f + cosw) / 2.0f) / (1.0f + alpha);
            hp_a1 = (-2.0f * cosw) / (1.0f + alpha);
            hp_a2 = (1.0f - alpha) / (1.0f + alpha);
        }
        
        if (lowpassEnabled) {
            float w = 2.0f * M_PI * lowpassFreq / sampleRate;
            float cosw = cos(w);
            float sinw = sin(w);
            float alpha = sinw / sqrt(2.0f);
            
            lp_b0 = ((1.0f - cosw) / 2.0f) / (1.0f + alpha);
            lp_b1 = (1.0f - cosw) / (1.0f + alpha);
            lp_b2 = ((1.0f - cosw) / 2.0f) / (1.0f + alpha);
            lp_a1 = (-2.0f * cosw) / (1.0f + alpha);
            lp_a2 = (1.0f - alpha) / (1.0f + alpha);
        }
    }
    
    void updateGateParams() {
        float attackSamples = (gateAttackMs / 1000.0f) * sampleRate;
        float releaseSamples = (gateReleaseMs / 1000.0f) * sampleRate;
        gateAttack = 1.0f / attackSamples;
        gateRelease = 1.0f / releaseSamples;
    }
    
    float processHighpass(float input, float& x1, float& x2, float& y1, float& y2) {
        float output = hp_b0 * input + hp_b1 * x1 + hp_b2 * x2 - hp_a1 * y1 - hp_a2 * y2;
        x2 = x1; x1 = input; y2 = y1; y1 = output;
        return output;
    }
    
    float processLowpass(float input, float& x1, float& x2, float& y1, float& y2) {
        float output = lp_b0 * input + lp_b1 * x1 + lp_b2 * x2 - lp_a1 * y1 - lp_a2 * y2;
        x2 = x1; x1 = input; y2 = y1; y1 = output;
        return output;
    }
    
    float processGate(float input) {
        float amplitude = fabs(input);
        float amplitudeDb = 20.0f * log10f(amplitude + 1e-10f);
        float targetGate = (amplitudeDb > gateThreshold) ? 1.0f : 0.0f;
        
        if (targetGate > gateState) {
            gateState += gateAttack;
            if (gateState > 1.0f) gateState = 1.0f;
        } else {
            gateState -= gateRelease;
            if (gateState < 0.0f) gateState = 0.0f;
        }
        
        return input * gateState;
    }
    
    bool doStart() {
        updateFilters();
        updateGateParams();
        updateDelayBuffer();
        updateVADParams();  // Add VAD params update
        
        RtAudio::StreamParameters parameters;
        parameters.deviceId = deviceIds[devId];
        parameters.nChannels = 2;
        unsigned int bufferFrames = rnnFrameSize;
        
        RtAudio::StreamOptions opts;
        opts.flags = RTAUDIO_MINIMIZE_LATENCY;
        opts.streamName = _streamName;

        try {
            audio.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts);
            stereoPacker.setSampleCount(bufferFrames);
            audio.startStream();
            stereoPacker.start();
        }
        catch (const std::exception& e) {
            flog::error("Could not open audio device: {0}", e.what());
            return false;
        }
        
        if (rnNoiseEnabled && !rnNoiseStateL) {
            createDenoiseState();
        }
        
        return true;
    }

    void doStop() {
        s2m.stop();
        monoPacker.stop();
        stereoPacker.stop();
        monoPacker.out.stopReader();
        stereoPacker.out.stopReader();
        audio.stopStream();
        audio.closeStream();
        monoPacker.out.clearReadStop();
        stereoPacker.out.clearReadStop();
    }

    void selectFirst() {
        selectById(defaultDevId);
    }

    void selectByName(std::string name) {
        for (int i = 0; i < devList.size(); i++) {
            if (devList[i].name == name) {
                selectById(i);
                return;
            }
        }
        selectFirst();
    }

    void selectById(int id) {
        devId = id;
        bool created = false;
        config.acquire();
        if (!config.conf[_streamName]["devices"].contains(devList[id].name)) {
            created = true;
            config.conf[_streamName]["devices"][devList[id].name] = devList[id].preferredSampleRate;
        }
        sampleRate = config.conf[_streamName]["devices"][devList[id].name];
        config.release(created);

        sampleRates = devList[id].sampleRates;
        sampleRatesTxt = "";
        char buf[256];
        bool found = false;
        unsigned int defaultId = 0;
        unsigned int defaultSr = devList[id].preferredSampleRate;
        for (int i = 0; i < sampleRates.size(); i++) {
            if (sampleRates[i] == sampleRate) {
                found = true;
                srId = i;
            }
            if (sampleRates[i] == defaultSr) {
                defaultId = i;
            }
            sprintf(buf, "%d", sampleRates[i]);
            sampleRatesTxt += buf;
            sampleRatesTxt += '\0';
        }
        if (!found) {
            sampleRate = defaultSr;
            srId = defaultId;
        }

        _stream->setSampleRate(sampleRate);

        if (running) {
            doStop();
            doStart();
        }
    }

    static int callback(void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames,
                       double streamTime, RtAudioStreamStatus status, void* userData) {
        RNNoiseAudioSink* _this = (RNNoiseAudioSink*)userData;
        int count = _this->stereoPacker.out.read();
        if (count < 0) return 0;

        dsp::stereo_t* audioData = (dsp::stereo_t*)_this->stereoPacker.out.readBuf;
        
        for (unsigned int i = 0; i < nBufferFrames; i++) {
            float leftSample = audioData[i].l;
            float rightSample = audioData[i].r;
            
            // RNNoise processing with VAD smoothing
            if (_this->rnNoiseEnabled && _this->rnNoiseStateL && _this->rnNoiseStateR) {
                _this->accumBufferL[_this->accumPos] = leftSample;
                _this->accumBufferR[_this->accumPos] = rightSample;
                _this->accumPos++;
                
                if (_this->accumPos >= _this->rnnFrameSize) {
                    float tempL[480], tempR[480];
                    float denoisedL[480], denoisedR[480];
                    
                    for (int j = 0; j < _this->rnnFrameSize; j++) {
                        tempL[j] = _this->accumBufferL[j] * 32768.0f;
                        tempR[j] = _this->accumBufferR[j] * 32768.0f;
                    }
                    
                    // Get VAD probabilities from RNNoise
                    float vadProbL = rnnoise_process_frame(_this->rnNoiseStateL, denoisedL, tempL);
                    float vadProbR = rnnoise_process_frame(_this->rnNoiseStateR, denoisedR, tempR);
                    
                    // Apply VAD smoothing if enabled
                    if (_this->smoothTransitions) {
                        // Use different factors for attack and release
                        if (vadProbL > _this->smoothedVadL) {
                            // Attack (voice detected - faster response)
                            _this->smoothedVadL = _this->smoothedVadL * _this->vadAttackFactor + 
                                                 vadProbL * (1.0f - _this->vadAttackFactor);
                        } else {
                            // Release (voice stopped - slower response)
                            _this->smoothedVadL = _this->smoothedVadL * _this->vadReleaseFactor + 
                                                 vadProbL * (1.0f - _this->vadReleaseFactor);
                        }
                        
                        if (vadProbR > _this->smoothedVadR) {
                            // Attack
                            _this->smoothedVadR = _this->smoothedVadR * _this->vadAttackFactor + 
                                                 vadProbR * (1.0f - _this->vadAttackFactor);
                        } else {
                            // Release
                            _this->smoothedVadR = _this->smoothedVadR * _this->vadReleaseFactor + 
                                                 vadProbR * (1.0f - _this->vadReleaseFactor);
                        }
                        
                        // Also apply basic smoothing if factor is set
                        if (_this->vadSmoothingFactor > 0.001f) {
                            _this->smoothedVadL = _this->smoothedVadL * _this->vadSmoothingFactor + 
                                                 vadProbL * (1.0f - _this->vadSmoothingFactor);
                            _this->smoothedVadR = _this->smoothedVadR * _this->vadSmoothingFactor + 
                                                 vadProbR * (1.0f - _this->vadSmoothingFactor);
                        }
                        
                        vadProbL = _this->smoothedVadL;
                        vadProbR = _this->smoothedVadR;
                    }
                    
                    // Update UI indicator
                    _this->lastVadProbability = std::max(vadProbL, vadProbR);
                    
                    for (int j = 0; j < _this->rnnFrameSize; j++) {
                        float procL = denoisedL[j] / 32768.0f;
                        float procR = denoisedR[j] / 32768.0f;
                        
                        // Calculate dynamic mix factors
                        float mixFactorL = _this->attenuationFactor;
                        float mixFactorR = _this->attenuationFactor;
                        
                        if (_this->dynamicMixEnabled && _this->smoothTransitions) {
                            // Reduce denoising strength when voice is detected
                            mixFactorL *= (1.0f - vadProbL * _this->vadReductionFactor);
                            mixFactorR *= (1.0f - vadProbR * _this->vadReductionFactor);
                        }
                        
                        // Mix with delay compensation
                        _this->dryDelayBufferL[_this->delayWritePos] = _this->accumBufferL[j];
                        _this->dryDelayBufferR[_this->delayWritePos] = _this->accumBufferR[j];
                        
                        int readPos = (_this->delayWritePos - _this->delaySamples + _this->maxDelaySamples) % _this->maxDelaySamples;
                        float delayedDryL = _this->dryDelayBufferL[readPos];
                        float delayedDryR = _this->dryDelayBufferR[readPos];
                        
                        _this->delayWritePos = (_this->delayWritePos + 1) % _this->maxDelaySamples;
                        
                        _this->processedBufferL[j] = delayedDryL * (1.0f - mixFactorL) + procL * mixFactorL;
                        _this->processedBufferR[j] = delayedDryR * (1.0f - mixFactorR) + procR * mixFactorR;
                    }
                    
                    _this->accumPos = 0;
                    _this->processedSamples = _this->rnnFrameSize;
                    _this->processedPos = 0;
                }
                
                if (_this->processedSamples > 0) {
                    leftSample = _this->processedBufferL[_this->processedPos];
                    rightSample = _this->processedBufferR[_this->processedPos];
                    _this->processedPos++;
                    _this->processedSamples--;
                }
            }
            
            // High-pass filter
            if (_this->highpassEnabled) {
                leftSample = _this->processHighpass(leftSample, 
                    _this->hp_x1_L, _this->hp_x2_L, _this->hp_y1_L, _this->hp_y2_L);
                rightSample = _this->processHighpass(rightSample, 
                    _this->hp_x1_R, _this->hp_x2_R, _this->hp_y1_R, _this->hp_y2_R);
            }
            
            // Low-pass filter
            if (_this->lowpassEnabled) {
                leftSample = _this->processLowpass(leftSample, 
                    _this->lp_x1_L, _this->lp_x2_L, _this->lp_y1_L, _this->lp_y2_L);
                rightSample = _this->processLowpass(rightSample, 
                    _this->lp_x1_R, _this->lp_x2_R, _this->lp_y1_R, _this->lp_y2_R);
            }
            
            // Gate
            if (_this->gateEnabled) {
                float monoGate = (leftSample + rightSample) * 0.5f;
                _this->processGate(monoGate);
                leftSample *= _this->gateState;
                rightSample *= _this->gateState;
            }
            
            // Output gain
            audioData[i].l = leftSample * _this->outputGain;
            audioData[i].r = rightSample * _this->outputGain;
        }

        memcpy(outputBuffer, audioData, nBufferFrames * sizeof(dsp::stereo_t));
        _this->stereoPacker.out.flush();
        return 0;
    }

#if RTAUDIO_VERSION_MAJOR >= 6
    static void errorCallback(RtAudioErrorType type, const std::string& errorText) {
        switch (type) {
        case RtAudioErrorType::RTAUDIO_NO_ERROR:
            return;
        case RtAudioErrorType::RTAUDIO_WARNING:
        case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND:
        case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT:
            flog::warn("RNNoiseAudioSink Warning: {} ({})", errorText, (int)type);
            break;
        default:
            throw std::runtime_error(errorText);
        }
    }
#endif

    // Core members
    SinkManager::Stream* _stream;
    std::string _streamName;
    dsp::convert::StereoToMono s2m;
    dsp::buffer::Packer<float> monoPacker;
    dsp::buffer::Packer<dsp::stereo_t> stereoPacker;

    // RNNoise
    DenoiseState* rnNoiseStateL = nullptr;
    DenoiseState* rnNoiseStateR = nullptr;
    bool rnNoiseEnabled = false;
    int rnnFrameSize = 480;
    
    // Model management - FIXED TYPE
    RNNModel* customModel = nullptr;
    std::string modelDirectory;
    std::vector<std::string> modelList;
    std::string selectedModel = "Default";
    bool useBuiltinModel = true;
    bool useLittleModel = false;
    std::vector<char> modelData;
    
    // Frame buffers
    float accumBufferL[480] = {0};
    float accumBufferR[480] = {0};
    float processedBufferL[480] = {0};
    float processedBufferR[480] = {0};
    int accumPos = 0;
    int processedPos = 0;
    int processedSamples = 0;
    
    // Dry/Wet with delay compensation
    float attenuationFactor = 1.0f;
    float dryDelayMs = 20.0f;
    std::vector<float> dryDelayBufferL;
    std::vector<float> dryDelayBufferR;
    int delaySamples = 960;
    int maxDelaySamples = 48000;
    int delayWritePos = 0;
    
    // VAD Smoothing with Attack/Release
    bool smoothTransitions = true;
    float vadSmoothingFactor = 0.8f;
    float vadAttackMs = 5.0f;
    float vadReleaseMs = 50.0f;
    float vadAttackFactor = 0.0f;
    float vadReleaseFactor = 0.0f;
    float smoothedVadL = 0.0f;
    float smoothedVadR = 0.0f;
    bool dynamicMixEnabled = false;
    float vadReductionFactor = 0.3f;
    float lastVadProbability = 0.0f;
    
    // Output
    float outputGain = 1.0f;
    
    // Filters
    bool highpassEnabled = false;
    float highpassFreq = 100.0f;
    float hp_b0, hp_b1, hp_b2, hp_a1, hp_a2;
    float hp_x1_L = 0, hp_x2_L = 0, hp_y1_L = 0, hp_y2_L = 0;
    float hp_x1_R = 0, hp_x2_R = 0, hp_y1_R = 0, hp_y2_R = 0;
    
    bool lowpassEnabled = false;
    float lowpassFreq = 3000.0f;
    float lp_b0, lp_b1, lp_b2, lp_a1, lp_a2;
    float lp_x1_L = 0, lp_x2_L = 0, lp_y1_L = 0, lp_y2_L = 0;
    float lp_x1_R = 0, lp_x2_R = 0, lp_y1_R = 0, lp_y2_R = 0;
    
    // Gate
    bool gateEnabled = false;
    float gateThreshold = -40.0f;
    float gateAttackMs = 5.0f;
    float gateReleaseMs = 50.0f;
    float gateAttack = 0.001f;
    float gateRelease = 0.0001f;
    float gateState = 0.0f;

    // Audio device
    RtAudio audio;
    int srId = 0;
    int devId = 0;
    bool running = false;
    unsigned int defaultDevId = 0;
    std::vector<RtAudio::DeviceInfo> devList;
    std::vector<unsigned int> deviceIds;
    std::string txtDevList;
    std::vector<unsigned int> sampleRates;
    std::string sampleRatesTxt;
    unsigned int sampleRate = 48000;
    std::string device = "";
};

class RNNoiseAudioSinkModule : public ModuleManager::Instance {
public:
    RNNoiseAudioSinkModule(std::string name) {
        this->name = name;
        provider.create = create_sink;
        provider.ctx = this;
        
        flog::info("RNNoiseAudioSink: Registering sink provider 'RNNoise Audio'");
        sigpath::sinkManager.registerSinkProvider("RNNoise Audio", provider);
    }

    ~RNNoiseAudioSinkModule() {
        flog::info("RNNoiseAudioSink: Unregistering sink provider 'RNNoise Audio'");
        sigpath::sinkManager.unregisterSinkProvider("RNNoise Audio");
    }

    void postInit() {}
    void enable() { enabled = true; }
    void disable() { enabled = false; }
    bool isEnabled() { return enabled; }

private:
    static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) {
        return (SinkManager::Sink*)(new RNNoiseAudioSink(stream, streamName));
    }

    std::string name;
    bool enabled = true;
    SinkManager::SinkProvider provider;
};

MOD_EXPORT void _INIT_() {
    json def = json({});
    config.setPath(core::args["root"].s() + "/rnnoise_sink_config.json");
    config.load(def);
    config.enableAutoSave();
    flog::info("RNNoiseAudioSink: Module initialized");
}

MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
    flog::info("RNNoiseAudioSink: Creating instance '{}'", name);
    return new RNNoiseAudioSinkModule(name);
}

MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
    flog::info("RNNoiseAudioSink: Deleting instance");
    delete (RNNoiseAudioSinkModule*)instance;
}

MOD_EXPORT void _END_() {
    config.disableAutoSave();
    config.save();
    flog::info("RNNoiseAudioSink: Module terminated");
}