// Audio Worklet for processing microphone audio data
// This runs in a separate audio thread for better performance

class AudioProcessorWorklet extends AudioWorkletProcessor {
    constructor() {
        super();
        this.targetSampleRate = 16000;  // Target sample rate for Doubao
        this.frameCount = 0;
        this.chunkCount = 0;
        this.totalBytes = 0;
        this.flushCount = 0;

        // Accumulate samples (like playground)
        this.accumulatedSamples = [];
        this.samplesPerChunk = 8000;  // 500ms @ 16kHz = 8000 samples

        // Handle messages from main thread (e.g., flush command)
        this.port.onmessage = (event) => {
            // Flush: send remaining accumulated samples
            if (event.data?.type === 'flush') {
                this.flushCount += 1;
                if (this.accumulatedSamples.length > 0) {
                    this.sendAccumulatedSamples(true);
                } else {
                    this.port.postMessage({
                        type: 'audioStats',
                        chunkCount: this.chunkCount,
                        byteCount: this.totalBytes,
                        flushCount: this.flushCount,
                    });
                }
            }
        };
    }

    // Send accumulated samples to main thread
    sendAccumulatedSamples(isFlush = false) {
        if (this.accumulatedSamples.length === 0) return;

        // Convert to Float32Array
        const samples = new Float32Array(this.accumulatedSamples);

        // Convert to 16-bit PCM
        const pcmData = this.floatToPcm16(samples);
        const byteCount = pcmData.byteLength;
        this.chunkCount += 1;
        this.totalBytes += byteCount;

        // Calculate RMS
        let sum = 0;
        for (let i = 0; i < samples.length; i++) {
            sum += samples[i] * samples[i];
        }
        const rms = Math.sqrt(sum / samples.length);

        // Send to main thread
        this.port.postMessage({
            type: 'audioData',
            data: pcmData.buffer,
            samples: samples.length,
            sampleRate: this.targetSampleRate,
            rms: rms,
            sequence: this.chunkCount,
            chunkCount: this.chunkCount,
            byteCount: this.totalBytes,
            flushCount: this.flushCount,
            isFlush: isFlush,
        }, [pcmData.buffer]);

        // Clear accumulator
        this.accumulatedSamples = [];
    }

    // Message handling from main thread
    process(inputs, outputs, parameters) {
        const input = inputs[0];
        const inputChannel = input[0];

        this.frameCount++;

        if (!inputChannel) {
            return true;  // No input, continue processing
        }

        // Get the current sample rate (typically 48000 for microphone)
        const currentSampleRate = this.port.context?.sampleRate || 48000;

        // Resample to target rate (16000) if needed
        let samplesToSend;
        if (currentSampleRate !== this.targetSampleRate) {
            samplesToSend = this.resampleAudio(
                inputChannel,
                currentSampleRate,
                this.targetSampleRate
            );
        } else {
            // Convert to Float32Array
            samplesToSend = new Float32Array(inputChannel);
        }

        // Accumulate samples
        for (let i = 0; i < samplesToSend.length; i++) {
            this.accumulatedSamples.push(samplesToSend[i]);
        }

        // When we have enough samples, send to main thread
        if (this.accumulatedSamples.length >= this.samplesPerChunk) {
            this.sendAccumulatedSamples();
        }

        return true;  // Keep the processor alive
    }

    // Resample audio from one rate to another
    resampleAudio(samples, fromRate, toRate) {
        if (fromRate === toRate) {
            return samples;
        }

        const ratio = fromRate / toRate;
        const outputLength = Math.floor(samples.length / ratio);
        const result = new Float32Array(outputLength);

        for (let i = 0; i < outputLength; i++) {
            const sourceIndex = Math.floor(i * ratio);
            result[i] = samples[sourceIndex];
        }

        return result;
    }

    // Convert float samples to 16-bit PCM
    floatToPcm16(samples) {
        const pcm = new Int16Array(samples.length);
        for (let i = 0; i < samples.length; i++) {
            // Clamp to -1 to 1 range
            const clamped = Math.max(-1, Math.min(1, samples[i]));
            // Convert to 16-bit PCM
            pcm[i] = Math.round(clamped * 32767);
        }
        return pcm;
    }
}

registerProcessor('audio-processor', AudioProcessorWorklet);
