// voice_bridge.sy -- Hz -> Hormone Transduction via NCA Conductance Cascade
// Takes raw voice features and transduces vocal frequency characteristics
// into neurochemical state through the 34-head cascading hierarchy.
//
// Signal flow:
//   8D voice feature vector (extracted externally)
//   -> Hz-to-stimulus mapping (learned, H-H gated)
//   -> inject as phasic stimulus into T2 neurotransmitter heads
//   -> cascade propagates through T2->T3 gating
//   -> output: auditable neurochemical state vector
//
// Voice features map to neurochemistry through established psychoacoustics:
//   - F0 (fundamental frequency) -> arousal axis (cortisol/serotonin)
//   - Vocal jitter/shimmer -> stress markers (cortisol/adrenaline)
//   - Harmonic-to-noise ratio -> warmth/bonding (oxytocin/endorphin)
//   - Speech rate -> engagement/arousal (norepinephrine/dopamine)
//   - Pause dynamics -> inhibition/cognition (GABA/acetylcholine)
//   - Spectral energy distribution -> emotional intensity
//
// All gates use H-H rate functions: alpha = exp(clamp(w*x+b)),
// gate = alpha / (alpha + beta). NO sigmoid. NO tanh*0.5+0.5.
//
// Ported from Marjorie McCubbins' Python NCA (voice_bridge.py)
// Aislinn McCubbins, April 2026
//
// Patent Pending: U.S. Provisional Application Nos. 63/939,190, 63/962,385 & 63/988,485
// Copyright 2025-2026 Nexus Concordat Inc.

use cascade::{Cascade, ConductanceHead}

// ============================================================
// VOICE FEATURE SPECIFICATION
// ============================================================
// Canonical ordering for the 8D voice feature vector.
// Feature extraction happens outside Scyla (signal processing).
// This module consumes the resulting normalized [0,1] vector.

struct VoiceFeatureSpec {
    name: String,
    desc: String,
    range_lo: Float,
    range_hi: Float,
}

fn voice_feature_specs() -> Vec<VoiceFeatureSpec> {
    [
        VoiceFeatureSpec { name: "f0_mean",        desc: "Mean fundamental frequency (Hz)",      range_lo: 50.0,  range_hi: 500.0 },
        VoiceFeatureSpec { name: "f0_variability", desc: "F0 standard deviation / mean (ratio)", range_lo: 0.0,   range_hi: 1.0   },
        VoiceFeatureSpec { name: "jitter",         desc: "Cycle-to-cycle F0 perturbation",       range_lo: 0.0,   range_hi: 0.1   },
        VoiceFeatureSpec { name: "shimmer",        desc: "Cycle-to-cycle amplitude perturbation", range_lo: 0.0,   range_hi: 0.3   },
        VoiceFeatureSpec { name: "hnr",            desc: "Harmonic-to-noise ratio (dB)",          range_lo: 0.0,   range_hi: 40.0  },
        VoiceFeatureSpec { name: "speech_rate",    desc: "Voiced frames per second",              range_lo: 0.0,   range_hi: 15.0  },
        VoiceFeatureSpec { name: "pause_ratio",    desc: "Fraction of unvoiced frames",           range_lo: 0.0,   range_hi: 1.0   },
        VoiceFeatureSpec { name: "spectral_tilt",  desc: "High-freq vs low-freq energy ratio",    range_lo: 0.0,   range_hi: 1.0   },
    ]
}

// ============================================================
// 8D VOICE FEATURE VECTOR
// ============================================================
// Normalized [0,1] values from external feature extraction.

struct VoiceFeatures {
    f0_mean: Float,
    f0_variability: Float,
    jitter: Float,
    shimmer: Float,
    hnr: Float,
    speech_rate: Float,
    pause_ratio: Float,
    spectral_tilt: Float,
}

impl VoiceFeatures {
    fn silent() -> VoiceFeatures {
        VoiceFeatures {
            f0_mean: 0.0,
            f0_variability: 0.0,
            jitter: 0.0,
            shimmer: 0.0,
            hnr: 0.0,
            speech_rate: 0.0,
            pause_ratio: 1.0,
            spectral_tilt: 0.0,
        }
    }

    fn to_vec(&self) -> Vec<Float> {
        [self.f0_mean, self.f0_variability, self.jitter, self.shimmer,
         self.hnr, self.speech_rate, self.pause_ratio, self.spectral_tilt]
    }

    fn from_raw(raw: Vec<Float>) -> VoiceFeatures {
        // Normalize raw feature values into [0,1] using known ranges
        let specs = voice_feature_specs()
        let mut normed = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        let mut i = 0
        while i < 8 {
            let lo = specs[i].range_lo
            let hi = specs[i].range_hi
            let val = (raw[i] - lo) / (hi - lo + 0.00000001)
            if val < 0.0 { normed[i] = 0.0 }
            else if val > 1.0 { normed[i] = 1.0 }
            else { normed[i] = val }
            i = i + 1
        }
        VoiceFeatures {
            f0_mean: normed[0],
            f0_variability: normed[1],
            jitter: normed[2],
            shimmer: normed[3],
            hnr: normed[4],
            speech_rate: normed[5],
            pause_ratio: normed[6],
            spectral_tilt: normed[7],
        }
    }
}

// ============================================================
// Hz -> NEUROTRANSMITTER MAPPING (biological grounding)
// ============================================================
// Psychoacoustic prior matrix: [8 voice features x 8 neurotransmitters]
// T2 head order: dopamine, norepinephrine, serotonin, gaba,
//                oxytocin, cortisol, endorphin, acetylcholine
//
// Grounded in psychoacoustic and psychophysiological research:
//   Scherer (1986): Vocal affect expression
//   Juslin & Laukka (2003): Communication of emotions in vocal expression
//   Bachorowski & Owren (2008): Vocal acoustics in emotional intelligence

struct HzToNtPrior {
    weights: Vec<Vec<Float>>,
}

impl HzToNtPrior {
    fn biological() -> HzToNtPrior {
        HzToNtPrior {
            weights: [
                // f0_mean: high pitch -> stress/arousal, low pitch -> calm
                //  DA    NE    5HT   GABA  OXY   CORT  END   ACh
                [ 0.0,  0.3, -0.3, -0.3, -0.1,  0.4,  0.0,  0.1],

                // f0_variability: prosodic range -> engagement, reward
                [ 0.5,  0.2,  0.0, -0.1,  0.2,  0.0,  0.2,  0.3],

                // jitter: vocal instability -> stress, fight/flight
                [ 0.0,  0.2, -0.2, -0.3, -0.3,  0.5,  0.0,  0.0],

                // shimmer: amplitude instability -> distress
                [ 0.0,  0.1, -0.2, -0.2, -0.2,  0.4,  0.0,  0.0],

                // hnr: harmonic richness -> warmth, bonding, pleasure
                [ 0.2,  0.0,  0.3,  0.2,  0.5, -0.3,  0.4,  0.0],

                // speech_rate: fast -> arousal/engagement, slow -> calm
                [ 0.3,  0.4, -0.2, -0.3,  0.0,  0.2, -0.1,  0.3],

                // pause_ratio: more pauses -> inhibition, cognitive load
                [-0.1, -0.2,  0.1,  0.4, -0.1, -0.1,  0.0,  0.4],

                // spectral_tilt: bright -> assertion/alert, dark -> vulnerability
                [ 0.1,  0.3, -0.1, -0.2,  0.0,  0.2,  0.0,  0.2],
            ],
        }
    }

    // Matrix-vector multiply: [8x8]^T * [8] -> [8]
    // Transpose because linear projection is W^T * x
    fn project(&self, features: Vec<Float>) -> Vec<Float> {
        let mut result = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        let mut nt = 0
        while nt < 8 {
            let mut sum = 0.0
            let mut f = 0
            while f < 8 {
                sum = sum + self.weights[f][nt] * features[f]
                f = f + 1
            }
            result[nt] = sum
            nt = nt + 1
        }
        result
    }
}

// ============================================================
// Hz -> HORMONE CONDUCTANCE GATE (H-H, NO SIGMOID)
// ============================================================
// Learned transduction gate from voice features to neurotransmitter stimulus.
// Uses H-H exponential rate functions for all gating.
// Initialized from biological priors but learned during training.
//
// Input: [8] normalized voice features
// Output: [8] phasic stimulus values for T2 neurotransmitter heads

temporal struct HzToHormoneGate {
    // Learned projection (initialized from biological prior)
    state proj_weights: Vec<Vec<Float>>,
    state proj_bias: Vec<Float>,

    // H-H gate parameters for each NT (controls transduction sensitivity)
    // gate = alpha / (alpha + beta) where alpha, beta = exp(w*x + b)
    state w_alpha: Vec<Float>,
    state b_alpha: Vec<Float>,
    state w_beta: Vec<Float>,
    state b_beta: Vec<Float>,

    // Running baseline for phasic computation (novelty detection)
    state baseline: Vec<Float>,
    const ema_alpha: Float = 0.15,
}

impl HzToHormoneGate {
    fn new() -> HzToHormoneGate {
        // Initialize weights from biological prior (transposed for projection)
        let prior = HzToNtPrior::biological()

        // Transpose: proj_weights[nt][feature] = prior.weights[feature][nt]
        let mut weights = []
        let mut nt = 0
        while nt < 8 {
            let mut row = []
            let mut f = 0
            while f < 8 {
                row = row + [prior.weights[f][nt]]
                f = f + 1
            }
            weights = weights + [row]
            nt = nt + 1
        }

        HzToHormoneGate {
            proj_weights: weights,
            proj_bias: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            w_alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            b_alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            w_beta:  [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            b_beta:  [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            baseline: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            ema_alpha: 0.15,
        }
    }

    // Linear projection: W * x + b
    fn project(&self, features: Vec<Float>) -> Vec<Float> {
        let mut result = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        let mut nt = 0
        while nt < 8 {
            let mut sum = self.proj_bias[nt]
            let mut f = 0
            while f < 8 {
                sum = sum + self.proj_weights[nt][f] * features[f]
                f = f + 1
            }
            result[nt] = sum
            nt = nt + 1
        }
        result
    }

    // Transduce voice features to NT stimulus through H-H gate
    fn transduce(&mut self, features: Vec<Float>) -> Vec<Float> {
        // Linear projection from Hz features to NT space
        let raw = self.project(features)

        let mut gated = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        let mut phasic = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

        let mut i = 0
        while i < 8 {
            // H-H conductance gate (sensitivity control)
            let a_arg = self.w_alpha[i] * raw[i] + self.b_alpha[i]
            let b_arg = self.w_beta[i] * raw[i] + self.b_beta[i]

            // Clamp to [-3, 3] for numerical stability
            let a_clamped = a_arg
            if a_clamped < -3.0 { a_clamped = -3.0 }
            if a_clamped > 3.0 { a_clamped = 3.0 }
            let b_clamped = b_arg
            if b_clamped < -3.0 { b_clamped = -3.0 }
            if b_clamped > 3.0 { b_clamped = 3.0 }

            let alpha = exp(a_clamped)
            let beta = exp(b_clamped)
            let gate = alpha / (alpha + beta + 0.00000001)

            // Gated signal
            gated[i] = raw[i] * gate

            // Phasic stimulus = deviation from running baseline (novelty)
            phasic[i] = gated[i] - self.baseline[i]

            // Update baseline via EMA
            self.baseline[i] = self.baseline[i] + self.ema_alpha * (gated[i] - self.baseline[i])

            i = i + 1
        }

        phasic
    }
}

impl Evolve for HzToHormoneGate {
    fn evolve(&mut self, dt: Duration) {
        // Gate parameters are learned, not temporally evolved.
        // Baseline drifts toward zero during silence (slow decay).
        let dt_s = dt.seconds()
        let decay = 0.01 * dt_s
        let mut i = 0
        while i < 8 {
            self.baseline[i] = self.baseline[i] * (1.0 - decay)
            i = i + 1
        }
    }
}

// ============================================================
// VOICE BRIDGE (integrates with Cascade)
// ============================================================
// Hz -> Hormone transduction through H-H conductance gating.
// Injects voice-derived phasic stimulus into T2 neurotransmitter head states.
// The cascade's own T2->T3 gating propagates effects to brain regions.

temporal struct VoiceBridge {
    state hz_to_hormone: HzToHormoneGate,

    // Gain control per NT (learnable amplitude scaling)
    state stimulus_gain: Vec<Float>,

    // H-H master gate: controls overall voice influence on cascade.
    // Prevents voice signal from overwhelming endogenous cascade dynamics.
    state w_master_alpha: Float = 0.5,
    state b_master_alpha: Float = 0.0,
    state w_master_beta: Float = 0.5,
    state b_master_beta: Float = 0.0,

    // Telemetry
    state last_stimulus: Vec<Float>,
    state last_master_gate: Float = 0.0,
    state last_features: Vec<Float>,
}

impl VoiceBridge {
    fn new() -> VoiceBridge {
        VoiceBridge {
            hz_to_hormone: HzToHormoneGate::new(),
            stimulus_gain: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
            w_master_alpha: 0.5,
            b_master_alpha: 0.0,
            w_master_beta: 0.5,
            b_master_beta: 0.0,
            last_stimulus: [],
            last_master_gate: 0.0,
            last_features: [],
        }
    }

    // H-H master gate controlling overall voice influence.
    // Stronger signals get partially attenuated (autoreceptor-like).
    fn compute_master_gate(&self, stimulus_magnitude: Float) -> Float {
        let a_arg = self.w_master_alpha * stimulus_magnitude + self.b_master_alpha
        let b_arg = self.w_master_beta * stimulus_magnitude + self.b_master_beta

        // Clamp to [-3, 3]
        let a_clamped = a_arg
        if a_clamped < -3.0 { a_clamped = -3.0 }
        if a_clamped > 3.0 { a_clamped = 3.0 }
        let b_clamped = b_arg
        if b_clamped < -3.0 { b_clamped = -3.0 }
        if b_clamped > 3.0 { b_clamped = 3.0 }

        let alpha = exp(a_clamped)
        let beta = exp(b_clamped)
        alpha / (alpha + beta + 0.00000001)
    }

    // Transduce voice features into cascade state.
    // Returns the stimulus values applied per NT.
    fn forward(&mut self, features: VoiceFeatures, cascade: &mut Cascade) -> Vec<Float> {
        let feat_vec = features.to_vec()

        // Get phasic stimulus from Hz->Hormone gate
        let phasic = self.hz_to_hormone.transduce(feat_vec)

        // Apply learned gain
        let mut scaled = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        let mut i = 0
        while i < 8 {
            scaled[i] = phasic[i] * self.stimulus_gain[i]
            i = i + 1
        }

        // Compute mean absolute magnitude for master gate
        let mut mag_sum = 0.0
        i = 0
        while i < 8 {
            let abs_val = scaled[i]
            if abs_val < 0.0 { abs_val = 0.0 - abs_val }
            mag_sum = mag_sum + abs_val
            i = i + 1
        }
        let magnitude = mag_sum / 8.0

        // Master gate (prevents overwhelming the cascade)
        let master = self.compute_master_gate(magnitude)

        // Final stimulus: scaled by master gate
        let mut final_stimulus = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        i = 0
        while i < 8 {
            final_stimulus[i] = scaled[i] * master
            i = i + 1
        }

        // Store telemetry
        self.last_stimulus = final_stimulus
        self.last_master_gate = master
        self.last_features = feat_vec

        // Inject into T2 neurotransmitter heads
        // Additive phasic stimulus through stimulate(), which respects
        // all 5 biological feedback mechanisms: mean-reverting decay,
        // autoreceptor, desensitization, vesicle depletion,
        // concentration-dependent clearance
        i = 0
        while i < 8 {
            cascade.tier_2.heads[i].stimulate(final_stimulus[i])
            i = i + 1
        }

        final_stimulus
    }
}

impl Evolve for VoiceBridge {
    fn evolve(&mut self, dt: Duration) {
        self.hz_to_hormone.evolve(dt)
    }
}

// ============================================================
// TELEMETRY
// ============================================================

struct VoiceTelemetry {
    last_stimulus: Vec<Float>,
    master_gate: Float,
    last_features: Vec<Float>,
    feature_names: Vec<String>,
    nt_order: Vec<String>,
}

impl VoiceBridge {
    fn telemetry(&self) -> VoiceTelemetry {
        VoiceTelemetry {
            last_stimulus: self.last_stimulus,
            master_gate: self.last_master_gate,
            last_features: self.last_features,
            feature_names: ["f0_mean", "f0_variability", "jitter", "shimmer",
                            "hnr", "speech_rate", "pause_ratio", "spectral_tilt"],
            nt_order: ["dopamine", "norepinephrine", "serotonin", "gaba",
                       "oxytocin", "cortisol", "endorphin", "acetylcholine"],
        }
    }
}

// ============================================================
// CONVENIENCE: ONE-SHOT FEATURES -> CASCADE INJECTION
// ============================================================

fn hz_features_to_hormones(features: VoiceFeatures, cascade: &mut Cascade) -> Vec<Float> {
    let mut bridge = VoiceBridge::new()
    bridge.forward(features, cascade)
}
