// IF (Integrate-and-Fire) neuron — bit-exact port of SNNModels.jl.
//
// Julia reference:
//   src/populations/generized_if/if.jl
//
// MoonBit identifier note: Julia uses Greek letters (τ, Δ) for the
// time-constant / slope-factor field names; MoonBit's lexer does not
// accept non-ASCII identifier characters. We use ASCII names
// (`tm`, `tw`, `dt_slope`, `tabs_const`, `tre`, `tde`, `tri`, `tdi`).
//
// Float32 contract: every arithmetic operation uses `Float` (Float32).
// Update order matches the Julia source exactly:
//
//   v[i] += dt/tm * (-(v[i] - El) + R*(-w[i] + I[i]) - R*syn_curr[i])
//   fire[i] = v[i] > Vt
//   v[i] = ifelse(fire[i], Vr, v[i])
//   tabs[i] = ifelse(fire[i], round(Int, τabs / dt), tabs[i])

///|
/// IFParameter — holds the biophysical constants of an LIF neuron.
///
/// Field values match the Julia defaults in Float32 arithmetic.
pub struct IFParameter {
  c : Float
  gl : Float
  tm : Float
  vt : Float
  vr : Float
  el : Float
  r : Float
  dt_slope : Float
  a : Float
  b : Float
  tw : Float
}

///|
/// Default IFParameter, matching Julia's `IFParameter()`.
pub fn IFParameter::new() -> IFParameter {
  let c : Float = -1.0F
  let gl : Float = -1.0F
  { c, gl, tm: 15.0F, vt: -50.0F, vr: -60.0F, el: -70.0F, r: 0.06F,
    dt_slope: 2.0F, a: 0.0F, b: 0.0F, tw: 0.0F }
}

///|
/// IFParameter with a custom resting potential `el`. Matches
/// `SNN.IFParameter(; El = -49mV)` from IF_net.jl.
pub fn IFParameter::with_el(el : Float) -> IFParameter {
  let c : Float = -1.0F
  let gl : Float = -1.0F
  { c, gl, tm: 15.0F, vt: -50.0F, vr: -60.0F, el, r: 0.06F,
    dt_slope: 2.0F, a: 0.0F, b: 0.0F, tw: 0.0F }
}

///|
/// IFParameter with custom time-constant, threshold, reset, resting
/// potential, and resistance. Matches
/// `SNN.IFParameter(; τm=20ms, R=100MΩ, Vt=-50mV, Vr=-60mV, El=-60mV)`
/// from CUBA.jl. Note: in MoonBit normalised units, `R` is in mho;
/// `100*MΩ = 100*mohm = 0.1F`.
pub fn IFParameter::custom(tm : Float, vt : Float, vr : Float, el : Float, r : Float) -> IFParameter {
  let c : Float = -1.0F
  let gl : Float = -1.0F
  { c, gl, tm, vt, vr, el, r, dt_slope: 2.0F, a: 0.0F, b: 0.0F, tw: 0.0F }
}

///|
/// PostSpike parameters — absolute refractory period.
pub struct PostSpike {
  tabs_const : Float
}

///|
pub fn PostSpike::new() -> PostSpike {
  // Julia: PostSpike{Float32}(; τabs = 2ms)
  { tabs_const: 2.0F }
}

///|
/// IF neuron state — a population of N integrate-and-fire neurons.
pub struct IF {
  param : IFParameter
  spike : PostSpike
  n : Int
  v : Array[Float]
  w : Array[Float]
  fire : Array[Bool]
  tabs : Array[Int]
  i : Array[Float]
  syn_curr : Array[Float]
  // Synapse state
  ge : Array[Float]
  gi : Array[Float]
  he : Array[Float]
  hi : Array[Float]
  glu : Array[Float]
  gaba : Array[Float]
  gsyn_e : Array[Float]
  gsyn_i : Array[Float]
  // Receptor reversal
  e_e : Float
  e_i : Float
  // Synapse time constants (DoubleExpSynapse defaults)
  tre : Float
  tde : Float
  tri : Float
  tdi : Float
}

///|
/// Construct a new IF population with `n` neurons.
pub fn IF::new(n : Int, param : IFParameter, rng : Xoshiro) -> IF {
  let v = Array::make(n, 0.0F)
  let spread = param.vt - param.vr
  for k in 0.. Unit {
  let n = p.n
  for i in 0.. Unit {
  let n = p.n
  for i in 0.. Unit {
  let n = p.n
  let p_ = p.param
  let tm = p_.tm
  let el = p_.el
  let r = p_.r
  let vt = p_.vt
  let vr = p_.vr
  let tabs_const = p.spike.tabs_const
  let tabs_steps : Int = (tabs_const / dt).to_int()

  for i in 0.. 0 {
      p.fire[i] = false
      p.tabs[i] = p.tabs[i] - 1
      continue
    }
    p.v[i] = p.v[i] + dt / tm *
      (-(p.v[i] - el) + r * (-p.w[i] + p.i[i]) - r * p.syn_curr[i])
    p.fire[i] = p.v[i] > vt
    p.v[i] = if p.fire[i] { vr } else { p.v[i] }
    p.tabs[i] = if p.fire[i] { tabs_steps } else { p.tabs[i] }
  }
  ()
}