/**
 * WavFormatDecoder - WAV/PCM implementation of IFormatDecoder.
 *
 * All WAV-specific stream logic lives here: header parsing, frame-aligned segment
 * sizing, segment wrapping with a synthesised 44-byte PCM header, and block-aligned
 * seek byte-offset calculation. StreamDecoder delegates to this via IFormatDecoder
 * and holds no WAV knowledge of its own.
 *
 * The low-level byte routines (parseHeader, createHeader, getSampleAlignedChunkSize)
 * are reused from WavUtils rather than duplicated — this is the same proven logic the
 * decoder used before the format-agnostic refactor, kept in one place.
 */

import { WavUtils } from '../wavutils.js';
import { FormatInfo, IFormatDecoder } from './IFormatDecoder.js';

export class WavFormatDecoder implements IFormatDecoder {
    tryParseHeader(chunks: Uint8Array[], totalSize: number): FormatInfo | null {
        const header = WavUtils.parseHeader(chunks, totalSize);
        if (!header) return null;

        return {
            sampleRate: header.sampleRate,
            channels: header.channels,
            bitsPerSample: header.bitsPerSample,
            byteRate: header.byteRate,
            blockAlign: header.blockAlign,
            // dataSize / byteRate is the exact PCM duration; guard against a zero
            // byteRate (malformed fmt chunk) rather than producing Infinity/NaN.
            totalDuration: header.byteRate > 0 ? header.dataSize / header.byteRate : null,
            audioDataOffset: header.headerSize,
            seekData: null
        };
    }

    getAlignedSegmentSize(
        info: FormatInfo,
        availableBytes: number,
        requestedSize: number,
        streamComplete: boolean
    ): number {
        // blockAlign is the PCM frame size; reuse the original frame-alignment routine
        // verbatim so streaming/tail-drain behavior is unchanged.
        return WavUtils.getSampleAlignedChunkSize(
            { blockAlign: info.blockAlign },
            requestedSize,
            availableBytes,
            streamComplete
        );
    }

    wrapSegment(info: FormatInfo, rawBytes: Uint8Array): Uint8Array {
        const header = WavUtils.createHeader(
            {
                channels: info.channels,
                sampleRate: info.sampleRate,
                byteRate: info.byteRate,
                blockAlign: info.blockAlign,
                bitsPerSample: info.bitsPerSample
            },
            rawBytes.length
        );
        const wavFile = new Uint8Array(header.length + rawBytes.length);
        wavFile.set(header, 0);
        wavFile.set(rawBytes, header.length);
        return wavFile;
    }

    calculateByteOffset(info: FormatInfo, positionSeconds: number): number {
        // No header / malformed fmt chunk: cannot compute. Return the audio start so the
        // caller still has a valid (file-absolute) Range target rather than a negative.
        if (info.byteRate <= 0 || info.blockAlign <= 0) return info.audioDataOffset;

        const rawOffset = Math.floor(positionSeconds * info.byteRate);
        // Align to a PCM frame boundary for clean audio, then make it file-absolute by
        // adding the header size — the Range request is a byte position in the file on disk.
        const alignedAudioOffset = Math.floor(rawOffset / info.blockAlign) * info.blockAlign;
        return info.audioDataOffset + alignedAudioOffset;
    }
}
