pub mod biquad;
pub mod crossover;
pub mod compressor;
pub mod clipper;
pub mod limiter;
pub mod ui;
pub mod presets;

use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU32, Ordering};

// ─── Metros globales de display ───────────────────────────────────────────────
// Escritos por el pad probe del pipeline activo, leídos por la ventana de config.
// Independientes del Arc del DspProcessor: no requieren lock.
static DISP_LEVELS: [AtomicU32; 6] = [
    AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0),
    AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0),
];
static DISP_GRS: [AtomicU32; 6] = [
    AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0),
    AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0),
];

pub fn display_level(b: usize) -> f32 { f32::from_bits(DISP_LEVELS[b].load(Ordering::Relaxed)) }
pub fn display_gr(b: usize)    -> f32 { f32::from_bits(DISP_GRS[b].load(Ordering::Relaxed)) }
use serde::{Deserialize, Serialize};
use crossover::Crossover;
use compressor::Compressor;
use clipper::Clipper;
use limiter::Limiter;

// ─── AtomicF32 ────────────────────────────────────────────────────────────────

pub struct AtomicF32(AtomicU32);
impl AtomicF32 {
    pub fn new(v: f32) -> Self { Self(AtomicU32::new(v.to_bits())) }
    pub fn load(&self) -> f32 { f32::from_bits(self.0.load(Ordering::Relaxed)) }
    pub fn store(&self, v: f32) { self.0.store(v.to_bits(), Ordering::Relaxed); }
}
impl Default for AtomicF32 { fn default() -> Self { Self::new(0.0) } }

// ─── BandMeters ───────────────────────────────────────────────────────────────

pub struct BandMeters {
    pub level: Arc<AtomicF32>,
    pub gr:    Arc<AtomicF32>,
}
impl BandMeters {
    pub fn new() -> Self {
        Self {
            level: Arc::new(AtomicF32::new(0.0)),
            gr:    Arc::new(AtomicF32::new(0.0)),
        }
    }
}

// ─── Configuración de banda ───────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BandConfig {
    pub enabled:       bool,
    pub threshold_db:  f32,
    pub ratio:         f32,
    pub attack_ms:     f32,
    pub release_ms:    f32,
    pub makeup_db:     f32,
    pub clip_enable:   bool,
    pub clip_level_db: f32,
    pub exp_enable:    bool,
    pub exp_threshold: f32,
    pub exp_ratio:     f32,
}

impl Default for BandConfig {
    fn default() -> Self {
        Self {
            enabled:       true,
            threshold_db:  -18.0,
            ratio:         3.0,
            attack_ms:     10.0,
            release_ms:    150.0,
            makeup_db:     3.0,
            clip_enable:   true,
            clip_level_db: -0.5,
            exp_enable:    false,
            exp_threshold: -60.0,
            exp_ratio:     2.0,
        }
    }
}

// ─── Configuración del procesador ────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessorConfig {
    pub enabled:            bool,
    pub num_bands:          usize,
    pub crossover_freqs:    [f32; 5],
    pub input_gain_db:      f32,
    pub output_gain_db:     f32,
    pub limiter_threshold:  f32,
    pub bands:              [BandConfig; 6],
    pub sample_rate:        u32,
    #[serde(default)]
    pub last_preset:        String,
}

impl Default for ProcessorConfig {
    fn default() -> Self {
        Self {
            enabled:           false,
            num_bands:         5,
            crossover_freqs:   [120.0, 400.0, 2000.0, 6000.0, 12000.0],
            input_gain_db:     0.0,
            output_gain_db:    0.0,
            limiter_threshold: -0.3,
            bands:             std::array::from_fn(|_| BandConfig::default()),
            sample_rate:       48000,
            last_preset:       String::new(),
        }
    }
}

impl ProcessorConfig {
    fn config_path() -> std::path::PathBuf {
        dirs::home_dir()
            .unwrap_or_else(|| std::path::PathBuf::from("."))
            .join(".gradio/data/processor.json")
    }

    pub fn cargar() -> Self {
        let path = Self::config_path();
        if let Ok(data) = std::fs::read_to_string(&path) {
            if let Ok(cfg) = serde_json::from_str(&data) {
                return cfg;
            }
        }
        Self::default()
    }

    pub fn guardar(&self) {
        let path = Self::config_path();
        if let Some(parent) = path.parent() {
            let _ = std::fs::create_dir_all(parent);
        }
        if let Ok(json) = serde_json::to_string_pretty(self) {
            let _ = std::fs::write(&path, json);
        }
    }

    pub fn db_to_linear(db: f32) -> f32 {
        10f32.powf(db / 20.0)
    }
}

// ─── Procesador DSP (instancia por pipeline) ─────────────────────────────────

pub struct DspProcessor {
    pub cfg:         ProcessorConfig,
    pub meters:      [BandMeters; 6],
    crossover:       Crossover,
    compressors:     [Compressor; 6],
    limiter:         Limiter,
}

impl DspProcessor {
    pub fn new(cfg: ProcessorConfig) -> Self {
        let sr = cfg.sample_rate as f32;
        let nb = cfg.num_bands.clamp(2, 6);
        let crossover = Crossover::new(&cfg.crossover_freqs, nb, sr);
        let compressors = std::array::from_fn(|i| Compressor::new(&cfg.bands[i], sr));
        let limiter = Limiter::new(cfg.limiter_threshold, sr);
        let meters = std::array::from_fn(|_| BandMeters::new());
        Self { cfg, meters, crossover, compressors, limiter }
    }

    /// Procesa un bloque de muestras F32LE intercaladas (L,R,L,R,...).
    /// Modifica los samples in-place.
    pub fn process_block(&mut self, samples: &mut [f32]) {
        if !self.cfg.enabled { return; }

        let sr = self.cfg.sample_rate as f32;
        let nb = self.cfg.num_bands.clamp(2, 6);
        let in_g  = ProcessorConfig::db_to_linear(self.cfg.input_gain_db);
        let out_g = ProcessorConfig::db_to_linear(self.cfg.output_gain_db);

        let mut bl = [0.0f32; 6];
        let mut br = [0.0f32; 6];

        let frames = samples.len() / 2;
        let mut band_levels_sum = [0.0f32; 6];
        let mut band_gr_sum     = [0.0f32; 6];

        for f in 0..frames {
            let l = samples[f * 2]     * in_g;
            let r = samples[f * 2 + 1] * in_g;

            self.crossover.process_sample(l, r, &mut bl, &mut br);

            let mut ol = 0.0f32;
            let mut or_ = 0.0f32;
            for b in 0..nb {
                let bc = self.cfg.bands[b].clone();
                let gr = self.compressors[b].process_sample(&mut bl[b], &mut br[b], &bc);
                Clipper::process(&mut bl[b], &mut br[b], &bc);
                band_levels_sum[b] += (bl[b].abs() + br[b].abs()) * 0.5;
                band_gr_sum[b]     += gr.abs();
                ol  += bl[b];
                or_ += br[b];
            }
            self.limiter.process(&mut ol, &mut or_);

            samples[f * 2]     = ol * out_g;
            samples[f * 2 + 1] = or_ * out_g;
        }

        // Actualizar medidores locales Y globales (display de la ventana de config)
        let inv = if frames > 0 { 1.0 / frames as f32 } else { 1.0 };
        for b in 0..nb {
            let lv = (band_levels_sum[b] * inv).clamp(0.0, 2.0);
            let gr = (band_gr_sum[b]     * inv / 40.0).clamp(0.0, 1.0);
            self.meters[b].level.store(lv);
            self.meters[b].gr   .store(gr);
            // Metros globales: leídos por la UI sin necesitar lock del DspProcessor
            DISP_LEVELS[b].store(lv.to_bits(), Ordering::Relaxed);
            DISP_GRS[b]   .store(gr.to_bits(), Ordering::Relaxed);
        }

        // Suppress unused variable warning for sr
        let _ = sr;
    }

    pub fn update_config(&mut self, cfg: ProcessorConfig) {
        let sr = cfg.sample_rate as f32;
        let nb = cfg.num_bands.clamp(2, 6);
        self.crossover.recalculate(&cfg.crossover_freqs, nb);
        for i in 0..6 {
            self.compressors[i].update_params(&cfg.bands[i], sr);
        }
        self.limiter.update_threshold(cfg.limiter_threshold);
        self.cfg = cfg;
    }
}

pub type SharedDsp = Arc<Mutex<DspProcessor>>;

pub fn nuevo_shared_dsp(cfg: ProcessorConfig) -> SharedDsp {
    Arc::new(Mutex::new(DspProcessor::new(cfg)))
}
