// RNNoise Web Audio Worklet Processor
// Uses the RNNoise WASM binary to perform real-time noise suppression

class RNNoiseProcessor extends AudioWorkletProcessor {
    constructor() {
        super();
        this.alive = true;
        this.wasmInstance = null;
        this.rnnoise = null;
        this.initialized = false;
        this.inputBuffer = new Float32Array(480); // RNNoise expects 480 samples (10ms at 48kHz)
        this.outputBuffer = new Float32Array(480);
        this.bufferPtr = 0;
        this.heapInputPtr = null;
        this.heapOutputPtr = null;
        this.statePtr = null;
        
        this.vadThreshold = 0.85; // Default strict threshold
        this.enabled = true;

        this.port.onmessage = (event) => {
            if (event.data.type === 'INIT') {
                this.initWasm(event.data.wasmBinary);
            } else if (event.data.type === 'SET_ENABLED') {
                this.enabled = event.data.enabled;
            } else if (event.data.type === 'SET_THRESHOLD') {
                this.vadThreshold = event.data.value;
            }
        };
    }

    async initWasm(wasmBinary) {
        try {
            const wasmModule = await WebAssembly.instantiate(wasmBinary, {
                env: {
                    memory: new WebAssembly.Memory({ initial: 256, maximum: 256 }),
                    abort: () => { console.error("WASM Abort"); }
                }
            });
            this.wasmInstance = wasmModule.instance;
            this.rnnoise = this.wasmInstance.exports;
            
            // Allocate memory on the WASM heap
            // rnnoise_create returns a pointer to the state
            this.statePtr = this.rnnoise.rnnoise_create(0);
            
            // Buffer size is 480 floats (4 bytes each)
            this.heapInputPtr = this.rnnoise.malloc(480 * 4);
            this.heapOutputPtr = this.rnnoise.malloc(480 * 4);
            
            this.initialized = true;
            console.log("RNNoise Processor Initialized with WASM");
        } catch (e) {
            console.error("Failed to initialize RNNoise WASM:", e);
        }
    }

    process(inputs, outputs, parameters) {
        const input = inputs[0];
        const output = outputs[0];

        if (!input || !input[0] || !this.initialized || !this.enabled) {
            // Pas d'entrée ou pas initialisé, on bypass
            if (input && input[0] && output && output[0]) {
                output[0].set(input[0]);
            }
            return true;
        }

        const inputChannel = input[0];
        const outputChannel = output[0];

        // Fill our internal buffer until we have 480 samples
        for (let i = 0; i < inputChannel.length; i++) {
            this.inputBuffer[this.bufferPtr] = inputChannel[i] * 32768.0; // RNNoise expects 16-bit PCM range
            this.bufferPtr++;

            if (this.bufferPtr >= 480) {
                // Process 480 samples
                this.processRNNoise();
                this.bufferPtr = 0;
            }

            // Output from our buffer (with latency of 480 samples)
            // We use a simple circular buffer approach here for the output too
            outputChannel[i] = (this.outputBuffer[this.bufferPtr] / 32768.0);
        }

        return true;
    }

    processRNNoise() {
        // Copy input to WASM heap
        const heapInput = new Float32Array(this.rnnoise.memory.buffer, this.heapInputPtr, 480);
        heapInput.set(this.inputBuffer);

        // Process audio: rnnoise_process_frame(state, output, input)
        // Returns the probability of speech (0.0 to 1.0)
        const vadProbability = this.rnnoise.rnnoise_process_frame(this.statePtr, this.heapOutputPtr, this.heapInputPtr);

        // Copy output from WASM heap
        const heapOutput = new Float32Array(this.rnnoise.memory.buffer, this.heapOutputPtr, 480);
        
        // Aggressive Voice Gate based on RNNoise VAD
        if (vadProbability < this.vadThreshold) {
            // Not voice -> Mute completely
            this.outputBuffer.fill(0);
        } else {
            // Voice detected -> Copy denoised output
            this.outputBuffer.set(heapOutput);
        }
    }
}

registerProcessor('rnnoise-processor', RNNoiseProcessor);