// Morris-Lecar neuron — bit-exact port of
// SNNModels.jl/src/populations/morrislecar.jl.
//
// Julia reference:
//   v = -52.14, w = 0.2 (constant init)
//   m_ss = 0.5 * (1 + tanh((v - V1) / V2))
//   n_ss = 0.5 * (1 + tanh((v - V3) / V4))
//   τ   = 1 / (ϕ * cosh((v - V3) / (2 * V4)))
//
//   integrate! (per step):
//     v[i] += dt / Cm * MorrisLecar_dv(v, w, I, param)
//     w[i] += dt * MorrisLecar_dw(v, w, param)
//     v[i] += dt/Cm * (ge * (Ee - v) + gi * (Ei - v))
//     ge[i] += dt * -ge[i] / τe
//     gi[i] += dt * -gi[i] / τi
//     fire[i] = v > 20
//
// Float32 contract: every arithmetic uses `Float`; `tanh` uses `tanhf`.
// Unit normalisation (matching Julia):
//   Cm  = 6.69 pF = 6.69 (pF=1)
//   gl  = 0.5 nS = 0.5
//   gK  = 2 nS   = 2
//   gCa = 1.1 nS = 1.1
//   ϕ   = 25 Hz = 0.025 (Hz=0.001)

///|
/// MorrisLecarParameter — biophysical constants.
pub struct MorrisLecarParameter {
  cm : Float
  el : Float
  ek : Float
  eca : Float
  gl : Float
  gk : Float
  gca : Float
  tau_e : Float
  tau_i : Float
  v1 : Float
  v2 : Float
  v3 : Float
  v4 : Float
  phi : Float
  e_e : Float
  e_i : Float
}

///|
/// Default MorrisLecarParameter, matching Julia's `MorrisLecarParameter()`.
pub fn MorrisLecarParameter::new() -> MorrisLecarParameter {
  { cm: 6.69F, el: -50.0F, ek: -70.0F, eca: 100.0F,
    gl: 0.5F, gk: 2.0F, gca: 1.1F,
    tau_e: 5.0F, tau_i: 10.0F,
    v1: 30.0F, v2: 15.0F, v3: 0.0F, v4: 30.0F,
    phi: 25.0F * 0.001F,  // 25 Hz = 0.025
    e_e: 0.0F, e_i: -75.0F }
}

///|
/// Morris-Lecar neuron state — a population of N ML neurons.
pub struct MorrisLecar {
  param : MorrisLecarParameter
  n : Int
  v : Array[Float]
  w : Array[Float]
  fire : Array[Bool]
  i : Array[Float]
  ge : Array[Float]
  gi : Array[Float]
}

///|
/// Construct a new MorrisLecar population with `n` neurons.
pub fn MorrisLecar::new(n : Int, param : MorrisLecarParameter, _rng : Xoshiro) -> MorrisLecar {
  let v = Array::make(n, -52.14F)
  let w = Array::make(n, 0.2F)
  let fire : Array[Bool] = Array::make(n, false)
  let i : Array[Float] = Array::make(n, 0.0F)
  let ge = Array::make(n, 0.0F)
  let gi = Array::make(n, 0.0F)
  { param, n, v, w, fire, i, ge, gi }
}

///|
/// Update the MorrisLecar neuron state for one step.
/// Bit-exact port of `integrate!(p::MorrisLecar, param, dt)`.
pub fn step_ml(p : MorrisLecar, dt : Float) -> Unit {
  let n = p.n
  let p_ = p.param
  let cm = p_.cm
  let el = p_.el
  let ek = p_.ek
  let eca = p_.eca
  let gl = p_.gl
  let gk = p_.gk
  let gca = p_.gca
  let tau_e = p_.tau_e
  let tau_i = p_.tau_i
  let v1 = p_.v1
  let v2 = p_.v2
  let v3 = p_.v3
  let v4 = p_.v4
  let phi = p_.phi
  let e_e = p_.e_e
  let e_i = p_.e_i
  for i in 0.. 20.0F
  }
  ()
}