//! HCA encoder - encodes PCM audio to HCA format.
//!
//! The frame encoder follows the VGAudio/PyCriCodecs flow: MDCT, scale factor
//! selection, bit-budget driven noise/resolution search, quantization, and CRI
//! bit packing.

use super::bitreader::BitWriter;
use super::cipher::cipher_init;
use super::decoder::{
    crc16_checksum, ChannelType, HCA_MAX_CHANNELS, HCA_SAMPLES_PER_FRAME, HCA_SAMPLES_PER_SUBFRAME,
    HCA_SUBFRAMES, HCA_VERSION_200,
};
use super::tables::{DEQUANTIZER_SCALING_TABLE, IMDCT_WINDOW, MAX_BIT_TABLE};
use std::f32::consts::{PI, SQRT_2};
use std::io::{self, Seek, Write};
use std::sync::LazyLock;
use thiserror::Error;

const HEADER_SIZE: usize = 0x60;
const ENCODER_DELAY: u32 = HCA_SAMPLES_PER_SUBFRAME as u32;
const DEFAULT_TRACK_COUNT: u32 = 1;
const MIN_RESOLUTION: u8 = 1;
const MAX_RESOLUTION: u8 = 15;

const DEFAULT_CHANNEL_MAPPING: [u8; 9] = [0, 1, 0, 4, 0, 1, 3, 7, 3];
const VALID_CHANNEL_MAPPINGS: [[u8; 8]; 8] = [
    [0, 1, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 0, 1, 0, 0, 0],
    [1, 0, 0, 1, 0, 1, 0, 0],
    [0, 1, 1, 0, 0, 0, 0, 1],
    [0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 1, 0, 0, 0, 0],
];

const SCALE_TO_RESOLUTION_CURVE: [u8; 59] = [
    0x0F, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0C, 0x0C,
    0x0C, 0x0C, 0x0C, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x06, 0x06, 0x05,
    0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01,
];

const QUANTIZER_INVERSE_STEP_SIZE: [f32; 16] = [
    0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 15.5, 31.5, 63.5, 127.5, 255.5, 511.5, 1023.5, 2047.5,
];

const QUANTIZE_SPECTRUM_BITS: [[u8; 16]; 8] = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 3, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 3, 3, 3, 2, 3, 3, 3, 0, 0, 0, 0],
    [0, 0, 0, 0, 4, 3, 3, 3, 3, 3, 3, 3, 4, 0, 0, 0],
    [0, 0, 0, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0],
    [0, 0, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 4, 4, 4, 0],
    [0, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4],
];

const QUANTIZE_SPECTRUM_VALUE: [[u32; 16]; 8] = [
    [
        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x2, 0x0, 0x1, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x5, 0x3, 0x0, 0x2, 0x4, 0x6, 0x0, 0x0, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0x0, 0x0, 0xF, 0x6, 0x4, 0x2, 0x0, 0x1, 0x3, 0x5, 0xE, 0x0, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0x0, 0xF, 0xD, 0xB, 0x4, 0x2, 0x0, 0x1, 0x3, 0xA, 0xC, 0xE, 0x0, 0x0,
    ],
    [
        0x0, 0x0, 0xF, 0xD, 0xB, 0x9, 0x7, 0x2, 0x0, 0x1, 0x6, 0x8, 0xA, 0xC, 0xE, 0x0,
    ],
    [
        0x0, 0xF, 0xD, 0xB, 0x9, 0x7, 0x5, 0x3, 0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE,
    ],
];

static QUANTIZER_DEAD_ZONE: LazyLock<[f32; 16]> = LazyLock::new(|| {
    let hex: [u32; 16] = [
        0x00000000, 0x3EAAAAAB, 0x3E4CCCCD, 0x3E124925, 0x3DE38E39, 0x3DBA2E8C, 0x3D9D89D9,
        0x3D888889, 0x3D042108, 0x3C820821, 0x3C010204, 0x3B808081, 0x3B004020, 0x3A802008,
        0x3A001002, 0x39800801,
    ];
    let mut result = [0.0f32; 16];
    for i in 0..16 {
        result[i] = f32::from_bits(hex[i]);
    }
    result
});

static INTENSITY_RATIO_BOUNDS: LazyLock<[f32; 14]> = LazyLock::new(|| {
    let hex: [u32; 14] = [
        0x3FF6DB6E, 0x3FE49249, 0x3FD24925, 0x3FC00000, 0x3FADB6DB, 0x3F9B6DB7, 0x3F892492,
        0x3F6DB6DB, 0x3F492492, 0x3F249249, 0x3F000000, 0x3EB6DB6E, 0x3E5B6DB7, 0x3D924925,
    ];
    let mut result = [0.0f32; 14];
    for i in 0..14 {
        result[i] = f32::from_bits(hex[i]);
    }
    result
});

#[derive(Debug, Error)]
pub enum HcaEncoderError {
    #[error("IO error: {0}")]
    Io(#[from] io::Error),
    #[error("Invalid sample rate: {0}")]
    InvalidSampleRate(u32),
    #[error("Invalid channel count: {0}")]
    InvalidChannelCount(u32),
    #[error("Frame too large")]
    FrameTooLarge,
    #[error("No samples provided")]
    NoSamples,
}

/// HCA encoder configuration.
#[derive(Debug, Clone)]
pub struct HcaEncoderConfig {
    pub sample_rate: u32,
    pub channels: u32,
    pub bitrate: u32,
    pub encryption_key: Option<u64>,
    pub loop_start: Option<u32>,
    pub loop_end: Option<u32>,
}

impl Default for HcaEncoderConfig {
    fn default() -> Self {
        Self {
            sample_rate: 44100,
            channels: 2,
            bitrate: 256000,
            encryption_key: None,
            loop_start: None,
            loop_end: None,
        }
    }
}

impl HcaEncoderConfig {
    pub fn new(sample_rate: u32, channels: u32) -> Self {
        Self {
            sample_rate,
            channels,
            ..Default::default()
        }
    }

    pub fn with_bitrate(mut self, bitrate: u32) -> Self {
        self.bitrate = bitrate;
        self
    }

    pub fn with_encryption(mut self, key: u64) -> Self {
        self.encryption_key = Some(key);
        self
    }
}

#[derive(Clone)]
struct ChannelEncodeState {
    channel_type: ChannelType,
    coded_count: usize,
    wave: [[f32; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
    spectra: [[f32; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
    scaled_spectra: [[f32; HCA_SUBFRAMES]; HCA_SAMPLES_PER_SUBFRAME],
    quantized_spectra: [[i32; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
    scale_factors: [u8; HCA_SAMPLES_PER_SUBFRAME],
    resolutions: [u8; HCA_SAMPLES_PER_SUBFRAME],
    intensity: [u8; HCA_SUBFRAMES],
    hfr_group_average_spectra: [f32; HCA_SUBFRAMES],
    hfr_scales: [u8; HCA_SUBFRAMES],
    scale_factor_delta_bits: u8,
    header_length_bits: usize,
    mdct_previous: [f32; HCA_SAMPLES_PER_SUBFRAME],
}

impl Default for ChannelEncodeState {
    fn default() -> Self {
        Self {
            channel_type: ChannelType::Discrete,
            coded_count: 0,
            wave: [[0.0; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
            spectra: [[0.0; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
            scaled_spectra: [[0.0; HCA_SUBFRAMES]; HCA_SAMPLES_PER_SUBFRAME],
            quantized_spectra: [[0; HCA_SAMPLES_PER_SUBFRAME]; HCA_SUBFRAMES],
            scale_factors: [0; HCA_SAMPLES_PER_SUBFRAME],
            resolutions: [0; HCA_SAMPLES_PER_SUBFRAME],
            intensity: [0; HCA_SUBFRAMES],
            hfr_group_average_spectra: [0.0; HCA_SUBFRAMES],
            hfr_scales: [0; HCA_SUBFRAMES],
            scale_factor_delta_bits: 0,
            header_length_bits: 0,
            mdct_previous: [0.0; HCA_SAMPLES_PER_SUBFRAME],
        }
    }
}

/// HCA encoder.
pub struct HcaEncoder {
    config: HcaEncoderConfig,
    frame_size: u32,
    total_band_count: u32,
    base_band_count: u32,
    stereo_band_count: u32,
    hfr_band_count: u32,
    bands_per_hfr_group: u32,
    hfr_group_count: u32,
    track_count: u32,
    channel_config: u32,
    encoder_delay: u32,
    encoder_padding: u32,
    channel: Vec<ChannelEncodeState>,
    encrypt_table: [u8; 256],
}

impl HcaEncoder {
    pub fn new(config: HcaEncoderConfig) -> Result<Self, HcaEncoderError> {
        if config.sample_rate == 0 || config.sample_rate > 0x7FFFFF {
            return Err(HcaEncoderError::InvalidSampleRate(config.sample_rate));
        }
        if config.channels == 0 || config.channels > HCA_MAX_CHANNELS as u32 {
            return Err(HcaEncoderError::InvalidChannelCount(config.channels));
        }

        let frame_size = Self::calculate_frame_size(config.bitrate, config.sample_rate);
        let (total_band_count, base_band_count, stereo_band_count, bands_per_hfr_group) =
            Self::calculate_band_counts(&config, frame_size);
        let hfr_band_count = total_band_count - base_band_count - stereo_band_count;
        let hfr_group_count = ceil_div(hfr_band_count, bands_per_hfr_group);
        let track_count = DEFAULT_TRACK_COUNT;
        let channel_config = Self::default_channel_config(config.channels, track_count)?;

        let mut cipher_table = [0u8; 256];
        if let Some(key) = config.encryption_key {
            cipher_init(&mut cipher_table, 56, key);
        } else {
            cipher_init(&mut cipher_table, 0, 0);
        }

        let mut encrypt_table = [0u8; 256];
        for (plain, &encrypted) in cipher_table.iter().enumerate() {
            encrypt_table[encrypted as usize] = plain as u8;
        }

        let mut encoder = Self {
            config,
            frame_size,
            total_band_count,
            base_band_count,
            stereo_band_count,
            hfr_band_count,
            bands_per_hfr_group,
            hfr_group_count,
            track_count,
            channel_config,
            encoder_delay: ENCODER_DELAY,
            encoder_padding: 0,
            channel: vec![ChannelEncodeState::default(); HCA_MAX_CHANNELS],
            encrypt_table,
        };
        encoder.set_channel_types();
        Ok(encoder)
    }

    fn calculate_frame_size(bitrate: u32, sample_rate: u32) -> u32 {
        let size = (bitrate as u64 * HCA_SAMPLES_PER_FRAME as u64) / (sample_rate as u64 * 8);
        (size as u32).clamp(0x8, 0xFFFF)
    }

    fn calculate_band_counts(config: &HcaEncoderConfig, _frame_size: u32) -> (u32, u32, u32, u32) {
        let mut cutoff_frequency = config.sample_rate / 2;
        let bitrate = config.bitrate.max(1);
        let pcm_bitrate = config.sample_rate * config.channels * 16;
        let (hfr_ratio, cutoff_ratio) = if config.channels <= 1 || pcm_bitrate / bitrate <= 6 {
            (6, 12)
        } else {
            (8, 16)
        };

        if bitrate < pcm_bitrate / cutoff_ratio {
            cutoff_frequency =
                cutoff_frequency.min(cutoff_ratio * bitrate / (32 * config.channels));
        }

        let total_band_count = ((cutoff_frequency as f64 * 256.0 / config.sample_rate as f64)
            .round() as u32)
            .clamp(1, HCA_SAMPLES_PER_SUBFRAME as u32);
        let hfr_start_band = total_band_count
            .min(((hfr_ratio as f64 * bitrate as f64 * 128.0) / pcm_bitrate as f64).round() as u32);
        let stereo_start_band = if hfr_ratio == 6 {
            hfr_start_band
        } else {
            hfr_start_band.div_ceil(2)
        };

        let hfr_band_count = total_band_count - hfr_start_band;
        let bands_per_hfr_group = hfr_band_count.div_ceil(8);

        (
            total_band_count,
            stereo_start_band,
            hfr_start_band - stereo_start_band,
            bands_per_hfr_group,
        )
    }

    fn default_channel_config(channels: u32, track_count: u32) -> Result<u32, HcaEncoderError> {
        let channels_per_track = channels / track_count;
        let channel_config = DEFAULT_CHANNEL_MAPPING
            .get(channels_per_track as usize)
            .copied()
            .unwrap_or(0) as u32;
        let valid = VALID_CHANNEL_MAPPINGS
            .get((channels_per_track - 1) as usize)
            .and_then(|row| row.get(channel_config as usize))
            .copied()
            .unwrap_or(0);
        if valid == 0 {
            Err(HcaEncoderError::InvalidChannelCount(channels))
        } else {
            Ok(channel_config)
        }
    }

    fn set_channel_types(&mut self) {
        let mut types = vec![ChannelType::Discrete; self.config.channels as usize];
        let channels_per_track = self.config.channels / self.track_count;

        if self.stereo_band_count > 0 && channels_per_track > 1 {
            for track in 0..self.track_count as usize {
                let base = track * channels_per_track as usize;
                match channels_per_track {
                    2 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                    }
                    3 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                    }
                    4 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                        if self.channel_config == 0 {
                            types[base + 2] = ChannelType::StereoPrimary;
                            types[base + 3] = ChannelType::StereoSecondary;
                        }
                    }
                    5 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                        if self.channel_config <= 2 {
                            types[base + 3] = ChannelType::StereoPrimary;
                            types[base + 4] = ChannelType::StereoSecondary;
                        }
                    }
                    6 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                        types[base + 4] = ChannelType::StereoPrimary;
                        types[base + 5] = ChannelType::StereoSecondary;
                    }
                    7 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                        types[base + 4] = ChannelType::StereoPrimary;
                        types[base + 5] = ChannelType::StereoSecondary;
                    }
                    8 => {
                        types[base] = ChannelType::StereoPrimary;
                        types[base + 1] = ChannelType::StereoSecondary;
                        types[base + 4] = ChannelType::StereoPrimary;
                        types[base + 5] = ChannelType::StereoSecondary;
                        types[base + 6] = ChannelType::StereoPrimary;
                        types[base + 7] = ChannelType::StereoSecondary;
                    }
                    _ => {}
                }
            }
        }

        for (i, channel_type) in types.iter().enumerate() {
            self.channel[i].channel_type = *channel_type;
            self.channel[i].coded_count = if *channel_type == ChannelType::StereoSecondary {
                self.base_band_count as usize
            } else {
                (self.base_band_count + self.stereo_band_count) as usize
            };
        }
    }

    /// Encode interleaved f32 PCM samples to HCA.
    pub fn encode<W: Write + Seek>(
        &mut self,
        samples: &[f32],
        writer: &mut W,
    ) -> Result<(), HcaEncoderError> {
        if samples.is_empty() {
            return Err(HcaEncoderError::NoSamples);
        }

        for ch in self.channel.iter_mut().take(self.config.channels as usize) {
            ch.mdct_previous.fill(0.0);
        }

        let channels = self.config.channels as usize;
        let sample_frames = samples.len().div_ceil(channels);
        let frame_count =
            (sample_frames as u32 + self.encoder_delay).div_ceil(HCA_SAMPLES_PER_FRAME as u32);
        self.encoder_padding =
            frame_count * HCA_SAMPLES_PER_FRAME as u32 - self.encoder_delay - sample_frames as u32;

        self.write_header(writer, frame_count)?;

        let frame_samples = HCA_SAMPLES_PER_FRAME * channels;
        for frame_idx in 0..frame_count as usize {
            let start = frame_idx * frame_samples;
            let end = (start + frame_samples).min(samples.len());

            let mut frame_data = vec![0.0f32; frame_samples];
            if start < samples.len() {
                frame_data[..end - start].copy_from_slice(&samples[start..end]);
            }

            self.encode_frame(writer, &frame_data)?;
        }

        Ok(())
    }

    fn write_header<W: Write + Seek>(
        &self,
        writer: &mut W,
        frame_count: u32,
    ) -> Result<(), HcaEncoderError> {
        let mut header = vec![0u8; HEADER_SIZE];

        header[0..4].copy_from_slice(b"HCA\0");
        write_be_u16(&mut header, 4, HCA_VERSION_200 as u16);
        write_be_u16(&mut header, 6, HEADER_SIZE as u16);

        header[8..12].copy_from_slice(b"fmt\0");
        header[12] = self.config.channels as u8;
        header[13..16].copy_from_slice(&(self.config.sample_rate & 0x00FF_FFFF).to_be_bytes()[1..]);
        write_be_u32(&mut header, 16, frame_count);
        write_be_u16(&mut header, 20, self.encoder_delay as u16);
        write_be_u16(&mut header, 22, self.encoder_padding as u16);

        header[24..28].copy_from_slice(b"comp");
        write_be_u16(&mut header, 28, self.frame_size as u16);
        header[30] = MIN_RESOLUTION;
        header[31] = MAX_RESOLUTION;
        header[32] = self.track_count as u8;
        header[33] = self.channel_config as u8;
        header[34] = self.total_band_count as u8;
        header[35] = self.base_band_count as u8;
        header[36] = self.stereo_band_count as u8;
        header[37] = self.bands_per_hfr_group as u8;

        let mut position = 40usize;
        if let (Some(loop_start), Some(loop_end)) = (self.config.loop_start, self.config.loop_end) {
            let loop_start = loop_start + self.encoder_delay;
            let loop_end = loop_end + self.encoder_delay;
            let loop_start_frame = loop_start / HCA_SAMPLES_PER_FRAME as u32;
            let loop_start_delay = loop_start % HCA_SAMPLES_PER_FRAME as u32;
            let mut loop_end_frame = loop_end / HCA_SAMPLES_PER_FRAME as u32;
            let mut loop_end_padding =
                HCA_SAMPLES_PER_FRAME as u32 - (loop_end % HCA_SAMPLES_PER_FRAME as u32);
            if loop_end_padding == HCA_SAMPLES_PER_FRAME as u32 {
                loop_end_frame = loop_end_frame.saturating_sub(1);
                loop_end_padding = 0;
            }

            header[position..position + 4].copy_from_slice(b"loop");
            write_be_u32(&mut header, position + 4, loop_start_frame);
            write_be_u32(&mut header, position + 8, loop_end_frame);
            write_be_u16(&mut header, position + 12, loop_start_delay as u16);
            write_be_u16(&mut header, position + 14, loop_end_padding as u16);
            position += 16;
        }

        header[position..position + 4].copy_from_slice(b"ciph");
        write_be_u16(
            &mut header,
            position + 4,
            if self.config.encryption_key.is_some() {
                56
            } else {
                0
            },
        );
        position += 6;

        header[position..position + 4].copy_from_slice(b"pad\0");

        if self.config.encryption_key.is_some() {
            mask_header(&mut header);
        }

        let crc = crc16_checksum(&header[..HEADER_SIZE - 2]);
        write_be_u16(&mut header, HEADER_SIZE - 2, crc);

        writer.write_all(&header)?;
        Ok(())
    }

    fn encode_frame<W: Write>(
        &mut self,
        writer: &mut W,
        samples: &[f32],
    ) -> Result<(), HcaEncoderError> {
        self.pcm_to_float(samples);
        self.run_mdct();
        self.encode_intensity_stereo();
        self.calculate_scale_factors();
        self.scale_spectra();
        self.calculate_hfr_group_averages();
        self.calculate_hfr_scale();
        self.calculate_frame_header_length();
        let acceptable_noise_level = self.calculate_noise_level()?;
        let evaluation_boundary = self.calculate_evaluation_boundary(acceptable_noise_level)?;
        self.calculate_frame_resolutions(acceptable_noise_level, evaluation_boundary);
        self.quantize_spectra();

        let mut frame = self.pack_frame(acceptable_noise_level, evaluation_boundary)?;
        if self.config.encryption_key.is_some() {
            for byte in &mut frame {
                *byte = self.encrypt_table[*byte as usize];
            }
        }

        let checksum = crc16_checksum(&frame[..self.frame_size as usize - 2]);
        frame[self.frame_size as usize - 2] = (checksum >> 8) as u8;
        frame[self.frame_size as usize - 1] = (checksum & 0xFF) as u8;

        writer.write_all(&frame)?;
        Ok(())
    }

    fn pcm_to_float(&mut self, samples: &[f32]) {
        let channels = self.config.channels as usize;
        for c in 0..channels {
            for sf in 0..HCA_SUBFRAMES {
                for i in 0..HCA_SAMPLES_PER_SUBFRAME {
                    let frame_index = sf * HCA_SAMPLES_PER_SUBFRAME + i;
                    let sample_index = frame_index * channels + c;
                    self.channel[c].wave[sf][i] = samples
                        .get(sample_index)
                        .copied()
                        .unwrap_or(0.0)
                        .clamp(-1.0, 1.0);
                }
            }
        }
    }

    fn run_mdct(&mut self) {
        for c in 0..self.config.channels as usize {
            for sf in 0..HCA_SUBFRAMES {
                mdct_transform(&mut self.channel[c], sf);
            }
        }
    }

    fn encode_intensity_stereo(&mut self) {
        if self.stereo_band_count == 0 {
            return;
        }

        for c in 0..self.config.channels as usize {
            if self.channel[c].channel_type != ChannelType::StereoPrimary
                || c + 1 >= self.config.channels as usize
            {
                continue;
            }

            for sf in 0..HCA_SUBFRAMES {
                let mut energy_l = 0.0f32;
                let mut energy_r = 0.0f32;
                let mut energy_total = 0.0f32;

                for b in self.base_band_count as usize..self.total_band_count as usize {
                    let l = self.channel[c].spectra[sf][b];
                    let r = self.channel[c + 1].spectra[sf][b];
                    energy_l += l.abs();
                    energy_r += r.abs();
                    energy_total += (l + r).abs();
                }
                energy_total *= 2.0;

                let energy_lr = energy_l + energy_r;
                let mut energy_ratio = 1.0;
                let quantized = if energy_lr > 0.0 {
                    let stored_value = 2.0 * energy_l / energy_lr;
                    if energy_total > 0.0 {
                        energy_ratio = (energy_lr / energy_total).clamp(0.5, SQRT_2 / 2.0);
                    }

                    let mut value = 1usize;
                    while value < 13 && INTENSITY_RATIO_BOUNDS[value] >= stored_value {
                        value += 1;
                    }
                    value as u8
                } else {
                    0
                };

                self.channel[c + 1].intensity[sf] = quantized;

                for b in self.base_band_count as usize..self.total_band_count as usize {
                    let mixed = (self.channel[c].spectra[sf][b]
                        + self.channel[c + 1].spectra[sf][b])
                        * energy_ratio;
                    self.channel[c].spectra[sf][b] = mixed;
                    self.channel[c + 1].spectra[sf][b] = 0.0;
                }
            }
        }
    }

    fn calculate_scale_factors(&mut self) {
        for c in 0..self.config.channels as usize {
            let coded_count = self.channel[c].coded_count;
            for b in 0..coded_count {
                let mut max_value = 0.0f32;
                for sf in 0..HCA_SUBFRAMES {
                    max_value = max_value.max(self.channel[c].spectra[sf][b].abs());
                }
                self.channel[c].scale_factors[b] = find_scale_factor(max_value);
            }
            self.channel[c].scale_factors[coded_count..HCA_SAMPLES_PER_SUBFRAME].fill(0);
        }
    }

    fn scale_spectra(&mut self) {
        for c in 0..self.config.channels as usize {
            let coded_count = self.channel[c].coded_count;
            for b in 0..coded_count {
                let scale_factor = self.channel[c].scale_factors[b] as usize;
                let scale = quantizer_scaling(scale_factor);
                for sf in 0..HCA_SUBFRAMES {
                    let value = self.channel[c].spectra[sf][b] * scale;
                    self.channel[c].scaled_spectra[b][sf] = if scale_factor == 0 {
                        0.0
                    } else {
                        value.clamp(-0.999_999_9, 0.999_999_9)
                    };
                }
            }
        }
    }

    fn calculate_hfr_group_averages(&mut self) {
        if self.hfr_group_count == 0 {
            return;
        }

        let hfr_start_band = (self.stereo_band_count + self.base_band_count) as usize;
        for c in 0..self.config.channels as usize {
            if self.channel[c].channel_type == ChannelType::StereoSecondary {
                continue;
            }

            let mut band = hfr_start_band;
            for group in 0..self.hfr_group_count as usize {
                let mut sum = 0.0f32;
                let mut count = 0usize;
                for _ in 0..self.bands_per_hfr_group as usize {
                    if band >= HCA_SAMPLES_PER_SUBFRAME {
                        break;
                    }
                    for sf in 0..HCA_SUBFRAMES {
                        sum += self.channel[c].spectra[sf][band].abs();
                    }
                    count += HCA_SUBFRAMES;
                    band += 1;
                }
                self.channel[c].hfr_group_average_spectra[group] =
                    if count > 0 { sum / count as f32 } else { 0.0 };
            }
        }
    }

    fn calculate_hfr_scale(&mut self) {
        if self.hfr_group_count == 0 {
            return;
        }

        let hfr_start_band = (self.stereo_band_count + self.base_band_count) as usize;
        let hfr_band_count = self
            .hfr_band_count
            .min(self.total_band_count - self.hfr_band_count) as usize;

        for c in 0..self.config.channels as usize {
            if self.channel[c].channel_type == ChannelType::StereoSecondary {
                continue;
            }

            let mut band = 0usize;
            for group in 0..self.hfr_group_count as usize {
                let mut sum = 0.0f32;
                let mut count = 0usize;

                for _ in 0..self.bands_per_hfr_group as usize {
                    if band >= hfr_band_count || hfr_start_band <= band {
                        break;
                    }
                    let source_band = hfr_start_band - band - 1;
                    for sf in 0..HCA_SUBFRAMES {
                        sum += self.channel[c].scaled_spectra[source_band][sf].abs();
                    }
                    count += HCA_SUBFRAMES;
                    band += 1;
                }

                let average = if count > 0 { sum / count as f32 } else { 0.0 };
                let mut group_spectra = self.channel[c].hfr_group_average_spectra[group];
                if average > 0.0 {
                    group_spectra *= (1.0 / average).min(SQRT_2);
                }
                self.channel[c].hfr_scales[group] = find_scale_factor(group_spectra);
            }
        }
    }

    fn calculate_frame_header_length(&mut self) {
        for c in 0..self.config.channels as usize {
            self.calculate_optimal_delta_length(c);
            if self.channel[c].channel_type == ChannelType::StereoSecondary {
                self.channel[c].header_length_bits += 4 * HCA_SUBFRAMES;
            } else if self.hfr_group_count > 0 {
                self.channel[c].header_length_bits += 6 * self.hfr_group_count as usize;
            }
        }
    }

    fn calculate_optimal_delta_length(&mut self, channel_index: usize) {
        let channel = &mut self.channel[channel_index];
        let coded_count = channel.coded_count;
        let empty_channel = channel.scale_factors[..coded_count]
            .iter()
            .all(|&scale| scale == 0);

        if empty_channel {
            channel.header_length_bits = 3;
            channel.scale_factor_delta_bits = 0;
            return;
        }

        let mut min_delta_bits = 6u8;
        let mut min_length = 3 + 6 * coded_count;

        for delta_bits in 1..6usize {
            let max_delta = (1 << (delta_bits - 1)) - 1;
            let mut length = 3 + 6;
            for band in 1..coded_count {
                let delta =
                    channel.scale_factors[band] as i32 - channel.scale_factors[band - 1] as i32;
                length += if delta.unsigned_abs() as usize > max_delta {
                    delta_bits + 6
                } else {
                    delta_bits
                };
            }

            if length < min_length {
                min_length = length;
                min_delta_bits = delta_bits as u8;
            }
        }

        channel.header_length_bits = min_length;
        channel.scale_factor_delta_bits = min_delta_bits;
    }

    fn calculate_noise_level(&mut self) -> Result<u32, HcaEncoderError> {
        let mut highest_band = (self.base_band_count + self.stereo_band_count) as i32 - 1;
        let available_bits = self.frame_size as usize * 8;
        let mut level = self.binary_search_level(available_bits, 0, 255);

        while level < 0 {
            highest_band -= 2;
            if highest_band < 0 {
                return Err(HcaEncoderError::FrameTooLarge);
            }

            for c in 0..self.config.channels as usize {
                for band in [highest_band + 1, highest_band + 2] {
                    if band >= 0 && (band as usize) < HCA_SAMPLES_PER_SUBFRAME {
                        self.channel[c].scale_factors[band as usize] = 0;
                    }
                }
            }

            self.calculate_frame_header_length();
            level = self.binary_search_level(available_bits, 0, 255);
        }

        Ok(level as u32)
    }

    fn binary_search_level(&self, available_bits: usize, mut low: i32, mut high: i32) -> i32 {
        let max = high;
        let mut mid_value = 0usize;

        while low != high {
            let mid = (low + high) / 2;
            mid_value = self.calculate_used_bits(mid, 0);

            if mid_value > available_bits {
                low = mid + 1;
            } else {
                high = mid;
            }
        }

        if low == max && mid_value > available_bits {
            -1
        } else {
            low
        }
    }

    fn calculate_evaluation_boundary(
        &self,
        acceptable_noise_level: u32,
    ) -> Result<u32, HcaEncoderError> {
        if acceptable_noise_level == 0 {
            return Ok(0);
        }

        let available_bits = self.frame_size as usize * 8;
        let level =
            self.binary_search_boundary(available_bits, acceptable_noise_level as i32, 0, 127);

        if level < 0 {
            Err(HcaEncoderError::FrameTooLarge)
        } else {
            Ok(level as u32)
        }
    }

    fn binary_search_boundary(
        &self,
        available_bits: usize,
        noise_level: i32,
        mut low: i32,
        mut high: i32,
    ) -> i32 {
        let max = high;

        while (high - low).abs() > 1 {
            let mid = (low + high) / 2;
            let mid_value = self.calculate_used_bits(noise_level, mid);
            if available_bits < mid_value {
                high = mid - 1;
            } else {
                low = mid;
            }
        }

        if low == high {
            return if low < max { low } else { -1 };
        }

        let high_value = self.calculate_used_bits(noise_level, high);
        if high_value > available_bits {
            low
        } else {
            high
        }
    }

    fn calculate_used_bits(&self, noise_level: i32, eval_boundary: i32) -> usize {
        let mut length = 16 + 16 + 16;

        for c in 0..self.config.channels as usize {
            let channel = &self.channel[c];
            length += channel.header_length_bits;

            for band in 0..channel.coded_count {
                let noise = if (band as i32) < eval_boundary {
                    noise_level - 1
                } else {
                    noise_level
                };
                let resolution = calculate_resolution(channel.scale_factors[band], noise) as usize;

                if resolution >= 8 {
                    let bits = MAX_BIT_TABLE[resolution] as usize - 1;
                    let dead_zone = QUANTIZER_DEAD_ZONE[resolution];
                    for sf in 0..HCA_SUBFRAMES {
                        length += bits;
                        if channel.scaled_spectra[band][sf].abs() >= dead_zone {
                            length += 1;
                        }
                    }
                } else {
                    let step_size = QUANTIZER_INVERSE_STEP_SIZE[resolution];
                    let shift_up = step_size + 1.0;
                    let shift_down = (step_size + 0.5 - 8.0) as i32;
                    for sf in 0..HCA_SUBFRAMES {
                        let quantized = (channel.scaled_spectra[band][sf] * step_size + shift_up)
                            as i32
                            - shift_down;
                        let index = quantized.clamp(0, 15) as usize;
                        length += QUANTIZE_SPECTRUM_BITS[resolution][index] as usize;
                    }
                }
            }
        }

        length
    }

    fn calculate_frame_resolutions(&mut self, noise_level: u32, evaluation_boundary: u32) {
        for c in 0..self.config.channels as usize {
            let coded_count = self.channel[c].coded_count;
            for band in 0..coded_count {
                let noise = if band < evaluation_boundary as usize {
                    noise_level.saturating_sub(1)
                } else {
                    noise_level
                };
                self.channel[c].resolutions[band] =
                    calculate_resolution(self.channel[c].scale_factors[band], noise as i32);
            }
            self.channel[c].resolutions[coded_count..HCA_SAMPLES_PER_SUBFRAME].fill(0);
        }
    }

    fn quantize_spectra(&mut self) {
        for c in 0..self.config.channels as usize {
            let coded_count = self.channel[c].coded_count;
            for band in 0..coded_count {
                let resolution = self.channel[c].resolutions[band] as usize;
                let step_size = QUANTIZER_INVERSE_STEP_SIZE[resolution];
                let shift_up = step_size + 1.0;
                let shift_down = (step_size + 0.5) as i32;

                for sf in 0..HCA_SUBFRAMES {
                    let quantized = (self.channel[c].scaled_spectra[band][sf] * step_size
                        + shift_up) as i32
                        - shift_down;
                    self.channel[c].quantized_spectra[sf][band] = if resolution < 8 {
                        quantized.clamp(-8, 7)
                    } else {
                        let bits = MAX_BIT_TABLE[resolution] as i32 - 1;
                        let max_value = (1 << bits) - 1;
                        quantized.clamp(-max_value, max_value)
                    };
                }
            }
        }
    }

    fn pack_frame(
        &self,
        acceptable_noise_level: u32,
        evaluation_boundary: u32,
    ) -> Result<Vec<u8>, HcaEncoderError> {
        let mut bits = BitWriter::new(self.frame_size as usize);
        bits.write(0xFFFF, 16);
        bits.write(acceptable_noise_level, 9);
        bits.write(evaluation_boundary, 7);

        for c in 0..self.config.channels as usize {
            self.write_scale_factors(&mut bits, c);
            if self.channel[c].channel_type == ChannelType::StereoSecondary {
                for sf in 0..HCA_SUBFRAMES {
                    bits.write(self.channel[c].intensity[sf] as u32, 4);
                }
            } else if self.hfr_group_count > 0 {
                for group in 0..self.hfr_group_count as usize {
                    bits.write(self.channel[c].hfr_scales[group] as u32, 6);
                }
            }
        }

        for sf in 0..HCA_SUBFRAMES {
            for c in 0..self.config.channels as usize {
                self.write_spectra(&mut bits, sf, c);
            }
        }

        if bits.position() > (self.frame_size as usize - 2) * 8 {
            return Err(HcaEncoderError::FrameTooLarge);
        }

        let mut frame = bits.into_vec();
        let checksum = crc16_checksum(&frame[..self.frame_size as usize - 2]);
        frame[self.frame_size as usize - 2] = (checksum >> 8) as u8;
        frame[self.frame_size as usize - 1] = (checksum & 0xFF) as u8;
        Ok(frame)
    }

    fn write_scale_factors(&self, writer: &mut BitWriter, channel_index: usize) {
        let channel = &self.channel[channel_index];
        let delta_bits = channel.scale_factor_delta_bits;
        writer.write(delta_bits as u32, 3);

        if delta_bits == 0 {
            return;
        }

        if delta_bits == 6 {
            for band in 0..channel.coded_count {
                writer.write(channel.scale_factors[band] as u32, 6);
            }
            return;
        }

        writer.write(channel.scale_factors[0] as u32, 6);
        let max_delta = (1 << (delta_bits - 1)) - 1;
        let escape_value = (1 << delta_bits) - 1;

        for band in 1..channel.coded_count {
            let delta = channel.scale_factors[band] as i32 - channel.scale_factors[band - 1] as i32;
            if delta.abs() > max_delta {
                writer.write(escape_value as u32, delta_bits as usize);
                writer.write(channel.scale_factors[band] as u32, 6);
            } else {
                writer.write((max_delta + delta) as u32, delta_bits as usize);
            }
        }
    }

    fn write_spectra(&self, writer: &mut BitWriter, subframe: usize, channel_index: usize) {
        let channel = &self.channel[channel_index];
        for band in 0..channel.coded_count {
            let resolution = channel.resolutions[band] as usize;
            if resolution == 0 {
                continue;
            }

            let quantized = channel.quantized_spectra[subframe][band];
            if resolution < 8 {
                let index = (quantized + 8).clamp(0, 15) as usize;
                let bits = QUANTIZE_SPECTRUM_BITS[resolution][index] as usize;
                let value = QUANTIZE_SPECTRUM_VALUE[resolution][index];
                writer.write(value, bits);
            } else {
                let bits = MAX_BIT_TABLE[resolution] as usize - 1;
                writer.write(quantized.unsigned_abs(), bits);
                if quantized != 0 {
                    writer.write(if quantized > 0 { 0 } else { 1 }, 1);
                }
            }
        }
    }
}

fn mdct_transform(channel: &mut ChannelEncodeState, subframe: usize) {
    let mut scratch = [0.0f32; HCA_SAMPLES_PER_SUBFRAME];
    let half = HCA_SAMPLES_PER_SUBFRAME / 2;

    for i in 0..half {
        let a = IMDCT_WINDOW[half - i - 1] * -channel.wave[subframe][half + i];
        let b = -IMDCT_WINDOW[half + i] * channel.wave[subframe][half - i - 1];
        let c = IMDCT_WINDOW[i] * channel.mdct_previous[i];
        let d = -IMDCT_WINDOW[HCA_SAMPLES_PER_SUBFRAME - i - 1]
            * channel.mdct_previous[HCA_SAMPLES_PER_SUBFRAME - i - 1];

        scratch[i] = a - b;
        scratch[half + i] = c - d;
    }

    for k in 0..HCA_SAMPLES_PER_SUBFRAME {
        let mut sum = 0.0f32;
        for (n, &sample) in scratch.iter().enumerate() {
            let angle = PI / HCA_SAMPLES_PER_SUBFRAME as f32 * (n as f32 + 0.5) * (k as f32 + 0.5);
            sum += sample * angle.cos();
        }
        channel.spectra[subframe][k] = sum * 0.125;
    }

    channel
        .mdct_previous
        .copy_from_slice(&channel.wave[subframe]);
}

fn find_scale_factor(value: f32) -> u8 {
    let mut low = 0usize;
    let mut high = 63usize;
    while low < high {
        let mid = (low + high) / 2;
        if DEQUANTIZER_SCALING_TABLE[mid] <= value {
            low = mid + 1;
        } else {
            high = mid;
        }
    }
    low as u8
}

fn quantizer_scaling(scale_factor: usize) -> f32 {
    let dequant = DEQUANTIZER_SCALING_TABLE[scale_factor];
    if dequant > 0.0 {
        1.0 / dequant
    } else {
        0.0
    }
}

fn calculate_resolution(scale_factor: u8, noise_level: i32) -> u8 {
    if scale_factor == 0 {
        return 0;
    }

    let curve_position = (noise_level - 5 * scale_factor as i32 / 2 + 2).clamp(0, 58);
    SCALE_TO_RESOLUTION_CURVE[curve_position as usize].clamp(MIN_RESOLUTION, MAX_RESOLUTION)
}

fn ceil_div(a: u32, b: u32) -> u32 {
    if b == 0 {
        0
    } else {
        a.div_ceil(b)
    }
}

fn write_be_u16(data: &mut [u8], offset: usize, value: u16) {
    data[offset..offset + 2].copy_from_slice(&value.to_be_bytes());
}

fn write_be_u32(data: &mut [u8], offset: usize, value: u32) {
    data[offset..offset + 4].copy_from_slice(&value.to_be_bytes());
}

fn mask_header(header: &mut [u8]) {
    xor_chunk_id(header, 0, 3);
    xor_chunk_id(header, 8, 3);
    xor_chunk_id(header, 24, 4);

    let mut position = 40usize;
    if unmasked_chunk(header, position) == *b"loop" {
        xor_chunk_id(header, position, 4);
        position += 16;
    }
    if unmasked_chunk(header, position) == *b"ciph" {
        xor_chunk_id(header, position, 4);
        position += 6;
    }
    if unmasked_chunk(header, position) == *b"pad\0" {
        xor_chunk_id(header, position, 3);
    }
}

fn unmasked_chunk(header: &[u8], offset: usize) -> [u8; 4] {
    [
        header[offset] & 0x7F,
        header[offset + 1] & 0x7F,
        header[offset + 2] & 0x7F,
        header[offset + 3] & 0x7F,
    ]
}

fn xor_chunk_id(header: &mut [u8], offset: usize, count: usize) {
    for i in 0..count {
        header[offset + i] ^= 0x80;
    }
}

/// Convenience function to encode WAV to HCA.
pub fn encode_wav_to_hca<W: Write + Seek>(
    wav_data: &[u8],
    writer: &mut W,
    config: Option<HcaEncoderConfig>,
) -> Result<(), HcaEncoderError> {
    if wav_data.len() < 44 || &wav_data[0..4] != b"RIFF" || &wav_data[8..12] != b"WAVE" {
        return Err(HcaEncoderError::NoSamples);
    }

    let mut pos = 12;
    let mut channels = 2u32;
    let mut sample_rate = 44100u32;
    let mut bits_per_sample = 16u32;
    let mut data_start = 0;
    let mut data_len = 0;

    while pos + 8 <= wav_data.len() {
        let chunk_id = &wav_data[pos..pos + 4];
        let chunk_size = u32::from_le_bytes([
            wav_data[pos + 4],
            wav_data[pos + 5],
            wav_data[pos + 6],
            wav_data[pos + 7],
        ]) as usize;

        if chunk_id == b"fmt " && chunk_size >= 16 {
            channels = u16::from_le_bytes([wav_data[pos + 10], wav_data[pos + 11]]) as u32;
            sample_rate = u32::from_le_bytes([
                wav_data[pos + 12],
                wav_data[pos + 13],
                wav_data[pos + 14],
                wav_data[pos + 15],
            ]);
            bits_per_sample = u16::from_le_bytes([wav_data[pos + 22], wav_data[pos + 23]]) as u32;
        } else if chunk_id == b"data" {
            data_start = pos + 8;
            data_len = chunk_size;
            break;
        }

        pos += 8 + chunk_size + (chunk_size & 1);
    }

    if data_start == 0 || data_len == 0 {
        return Err(HcaEncoderError::NoSamples);
    }

    let mut samples = Vec::new();
    let bytes_per_sample = (bits_per_sample / 8) as usize;
    let sample_count = data_len / bytes_per_sample;

    for i in 0..sample_count {
        let offset = data_start + i * bytes_per_sample;
        if offset + bytes_per_sample > wav_data.len() {
            break;
        }

        let sample = match bits_per_sample {
            16 => {
                let raw = i16::from_le_bytes([wav_data[offset], wav_data[offset + 1]]);
                raw as f32 / 32768.0
            }
            24 => {
                let raw = ((wav_data[offset] as i32)
                    | ((wav_data[offset + 1] as i32) << 8)
                    | ((wav_data[offset + 2] as i32) << 16))
                    << 8
                    >> 8;
                raw as f32 / 8_388_608.0
            }
            32 => f32::from_le_bytes([
                wav_data[offset],
                wav_data[offset + 1],
                wav_data[offset + 2],
                wav_data[offset + 3],
            ]),
            _ => 0.0,
        };
        samples.push(sample);
    }

    let cfg = config.unwrap_or_else(|| HcaEncoderConfig::new(sample_rate, channels));
    let mut encoder = HcaEncoder::new(cfg)?;
    encoder.encode(&samples, writer)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_encoder_config() {
        let config = HcaEncoderConfig::new(44100, 2).with_bitrate(256000);
        assert_eq!(config.sample_rate, 44100);
        assert_eq!(config.channels, 2);
        assert_eq!(config.bitrate, 256000);
    }

    #[test]
    fn test_encoder_creation() {
        let config = HcaEncoderConfig::new(44100, 2);
        let encoder = HcaEncoder::new(config);
        assert!(encoder.is_ok());
    }

    #[test]
    fn test_invalid_config() {
        let config = HcaEncoderConfig::new(0, 2);
        let encoder = HcaEncoder::new(config);
        assert!(encoder.is_err());

        let config = HcaEncoderConfig::new(44100, 0);
        let encoder = HcaEncoder::new(config);
        assert!(encoder.is_err());
    }

    #[test]
    fn test_crc16() {
        let data = [0x48, 0x43, 0x41, 0x00];
        let crc = crc16_checksum(&data);
        assert!(crc != 0);
    }

    #[test]
    fn test_encode_simple() {
        let config = HcaEncoderConfig::new(44100, 1);
        let mut encoder = HcaEncoder::new(config).unwrap();

        let samples: Vec<f32> = (0..HCA_SAMPLES_PER_FRAME)
            .map(|i| (2.0 * PI * 440.0 * i as f32 / 44100.0).sin() * 0.5)
            .collect();

        let mut output = std::io::Cursor::new(Vec::new());
        let result = encoder.encode(&samples, &mut output);
        assert!(result.is_ok());

        let data = output.into_inner();
        assert!(!data.is_empty());
        assert!(data[0] == 0x48 || data[0] == 0xC8);
    }

    #[test]
    fn test_encode_encrypted_roundtrip() {
        let key = 0x1234_5678_90AB_CDEF;
        let config = HcaEncoderConfig::new(44100, 1).with_encryption(key);
        let mut encoder = HcaEncoder::new(config).unwrap();

        let samples: Vec<f32> = (0..HCA_SAMPLES_PER_FRAME * 2)
            .map(|i| (2.0 * PI * 330.0 * i as f32 / 44100.0).sin() * 0.4)
            .collect();

        let mut output = std::io::Cursor::new(Vec::new());
        encoder.encode(&samples, &mut output).unwrap();
        let data = output.into_inner();
        assert_eq!(data[0], 0xC8);

        let mut decoder = crate::hca::HcaDecoder::from_reader(std::io::Cursor::new(data)).unwrap();
        decoder.set_encryption_key(key, 0);
        let decoded = decoder.decode_all().unwrap();
        assert!(!decoded.is_empty());
    }
}
