///|
pub(all) enum ElmoreStatus {
  ExactTree
  EmptyNetwork
  NoDriver
  MultipleDrivers
  Disconnected
  Cyclic
} derive(Eq, Debug)

///|
pub(all) struct NodeDelay {
  node_id : Int
  delay_seconds : Double
  downstream_capacitance : Double
} derive(Debug)

///|
pub(all) struct ElmoreResult {
  status : ElmoreStatus
  root : Int?
  node_delays : Array[NodeDelay]
  max_delay_seconds : Double
  coupling_policy : String
} derive(Debug)

///|
fn unavailable(status : ElmoreStatus, root? : Int) -> ElmoreResult {
  {
    status,
    root,
    node_delays: [],
    max_delay_seconds: 0.0,
    coupling_policy: "split coupling capacitance equally between endpoints",
  }
}

///|
fn node_capacitances(net : @model.Net) -> Array[Double] {
  let values = Array::make(net.nodes.length(), 0.0)
  for capacitor in net.capacitors {
    if capacitor.from_node >= 0 && capacitor.from_node < values.length() {
      match capacitor.to_node {
        None => values[capacitor.from_node] += capacitor.capacitance
        Some(other) => {
          values[capacitor.from_node] += capacitor.capacitance * 0.5
          if other >= 0 &&
            other < values.length() &&
            net.nodes[other].kind != @model.External {
            values[other] += capacitor.capacitance * 0.5
          }
        }
      }
    }
  }
  // Pin load is not part of D_NET's declared parasitic capacitance, but it is
  // electrical load seen by the resistance tree and therefore affects delay.
  for connection in net.connections {
    if connection.node_id >= 0 && connection.node_id < values.length() {
      if connection.load_capacitance is Some(load) {
        values[connection.node_id] += load
      }
    }
  }
  values
}

///|
/// Computes Elmore delay only when its mathematical preconditions hold.
///
/// The resistance topology must be a connected, single-driver tree. Coupling
/// capacitance is grounded by assigning half to each endpoint. Unsupported
/// topologies return an explicit status instead of a misleading number.
pub fn elmore_delay(net : @model.Net) -> ElmoreResult {
  if net.nodes.length() == 0 {
    return unavailable(EmptyNetwork)
  }
  let drivers = net.driver_nodes()
  if drivers.length() == 0 {
    return unavailable(NoDriver)
  }
  if drivers.length() > 1 {
    return unavailable(MultipleDrivers)
  }
  let root = drivers[0]
  let topology = @graph.summarize(net)
  if topology.connected_components > 1 {
    return unavailable(Disconnected, root~)
  }
  if topology.has_cycle {
    return unavailable(Cyclic, root~)
  }
  let graph = @graph.RCGraph::from_net(net)
  let parents = Array::make(net.nodes.length(), -2)
  let parent_resistance = Array::make(net.nodes.length(), 0.0)
  let order : Array[Int] = [root]
  let mut local_node_count = 0
  for node in net.nodes {
    if node.kind != @model.External {
      local_node_count += 1
    }
  }
  parents[root] = -1
  let mut cursor = 0
  while cursor < order.length() {
    let node = order[cursor]
    cursor += 1
    for edge in graph.adjacency[node] {
      if parents[edge.to] == -2 {
        parents[edge.to] = node
        parent_resistance[edge.to] = edge.resistance
        order.push(edge.to)
      }
    }
  }
  if order.length() != local_node_count {
    return unavailable(Disconnected, root~)
  }
  let downstream = node_capacitances(net)
  let mut reverse_index = order.length() - 1
  while reverse_index > 0 {
    let node = order[reverse_index]
    let parent = parents[node]
    downstream[parent] += downstream[node]
    reverse_index -= 1
  }
  let delays = Array::make(net.nodes.length(), 0.0)
  let mut max_delay = 0.0
  for index in 1.. max_delay {
      max_delay = delays[node]
    }
  }
  let node_delays : Array[NodeDelay] = []
  for node in 0.. String {
  match self {
    ExactTree => "exact-tree"
    EmptyNetwork => "empty-network"
    NoDriver => "no-driver"
    MultipleDrivers => "multiple-drivers"
    Disconnected => "disconnected"
    Cyclic => "cyclic"
  }
}