/**
 * FormatInfo: parsed header data needed to stream and seek an audio file.
 * Populated by IFormatDecoder.tryParseHeader; used by StreamDecoder throughout playback.
 */
export interface FormatInfo {
    /** Samples per second (e.g. 44100). */
    sampleRate: number;
    /** Number of audio channels. */
    channels: number;
    /**
     * Nominal bit depth — 16 for MP3 (conventional), 16/24/32 for WAV/FLAC.
     * Used for display; decoders handle the actual sample format internally.
     */
    bitsPerSample: number;
    /**
     * Average bytes per second. Used for CBR byte-offset estimation.
     * For WAV: exact (sampleRate * blockAlign).
     * For MP3 CBR: bitrate_kbps * 125.
     * For FLAC: approximate (fileSize / duration).
     */
    byteRate: number;
    /**
     * For WAV: PCM frame size in bytes (channels * bitsPerSample / 8).
     * For MP3: frame size in bytes (constant for CBR, 0 for VBR with TOC).
     * For FLAC: 0 (frame sizes vary; use sync scan instead).
     * Used by getAlignedSegmentSize to round to clean frame boundaries.
     */
    blockAlign: number;
    /** Total duration in seconds, from the header (null if unavailable). */
    totalDuration: number | null;
    /** Byte offset where audio frames begin in the original file. */
    audioDataOffset: number;
    /**
     * Format-specific accelerator for seek-beyond-buffer byte calculation.
     * WAV: null (uses byteRate/blockAlign directly).
     * MP3 VBR: Xing/VBRI TOC (100-entry Uint8Array, values are file-percentage * 255).
     * FLAC: SeekTable (array of {sampleNumber: number, streamOffset: number} — stream_offset
     *        is bytes from the start of audio frames, i.e. after all metadata blocks).
     */
    seekData?: Mp3VbrSeekData | FlacSeekData | null;
}

export interface Mp3VbrSeekData {
    kind: 'mp3-vbr';
    toc: Uint8Array;           // 100 entries; toc[i] = file-byte-fraction at i% of duration
    totalBytes: number;        // total audio bytes (from Xing header)
}

export interface FlacSeekData {
    kind: 'flac-seektable';
    points: Array<{ sampleNumber: number; streamOffset: number }>;
    streamInfoBytes: Uint8Array; // raw STREAMINFO metadata block for segment wrapping
    metadataBlocksSize: number;  // total bytes of all metadata blocks (for stream_offset relative math)
}

/**
 * IFormatDecoder: per-format strategy for header parsing, segment boundary detection,
 * segment wrapping, and seek offset calculation.
 *
 * Implementations: WavFormatDecoder (Wave 1), Mp3FormatDecoder (Wave 2), FlacFormatDecoder (Wave 2).
 */
export interface IFormatDecoder {
    /**
     * Attempt to parse the header from accumulated bytes. Returns null if more bytes are needed.
     * Called with growing chunks array until it succeeds or exceeds MAX_HEADER_SEARCH_BYTES.
     */
    tryParseHeader(chunks: Uint8Array[], totalSize: number): FormatInfo | null;

    /**
     * Return the largest decodable byte count ≤ requestedSize that ends on a clean frame/block
     * boundary in the audio data (post-header). Returns 0 if not enough data yet.
     * @param info - the parsed FormatInfo
     * @param availableBytes - bytes available starting at the current processedBytes position
     * @param requestedSize - maximum desired segment size
     * @param streamComplete - true when the stream has ended (allows draining the tail)
     * @param rawData - optional; the first `Math.min(requestedSize, availableBytes)` raw audio
     *   bytes (starting at the current processedBytes position in the stream), made available
     *   for format-specific frame-sync scanning. WAV and MP3 decoders ignore this parameter;
     *   FLAC and similar variable-frame formats use it to find the last clean frame boundary
     *   within the candidate window.
     */
    getAlignedSegmentSize(
        info: FormatInfo,
        availableBytes: number,
        requestedSize: number,
        streamComplete: boolean,
        rawData?: Uint8Array
    ): number;

    /**
     * Wrap raw audio bytes in the minimal decodable container for decodeAudioData.
     * WAV: prepend a 44-byte standard PCM header.
     * MP3: pass through unchanged (raw frames are self-contained).
     * FLAC: prepend fLaC marker + STREAMINFO metadata block.
     */
    wrapSegment(info: FormatInfo, rawBytes: Uint8Array): Uint8Array;

    /**
     * Calculate the file-absolute byte offset for a seek-beyond-buffer Range request.
     * The returned value includes the header offset (result ≥ info.audioDataOffset).
     * WAV: exact PCM frame alignment.
     * MP3 CBR: frame-aligned estimate; MP3 VBR: TOC interpolation.
     * FLAC: SEEKTABLE lookup when available.
     */
    calculateByteOffset(info: FormatInfo, positionSeconds: number): number;
}
