///|
/// Return the referenced name when an expression is a plain name.
pub fn Expression::name_text(self : Expression) -> String? {
  match self {
    Name(name, _) => Some(name)
    _ => None
  }
}

///|
/// Return whether an expression contains a reference to a name.
pub fn Expression::contains_name(self : Expression, wanted : String) -> Bool {
  match self {
    Name(name, _) => name == wanted
    Unary(_, operand, _) => operand.contains_name(wanted)
    Binary(left, _, right, _) =>
      left.contains_name(wanted) || right.contains_name(wanted)
    Index(base, index, _) =>
      base.contains_name(wanted) || index.contains_name(wanted)
    Number(_, _) | StringLiteral(_, _) | Unknown(_) => false
  }
}

///|
/// Count expression nodes in source order.
pub fn Expression::node_count(self : Expression) -> Int {
  match self {
    Name(_, _) | Number(_, _) | StringLiteral(_, _) | Unknown(_) => 1
    Unary(_, operand, _) => 1 + operand.node_count()
    Binary(left, _, right, _) => 1 + left.node_count() + right.node_count()
    Index(base, index, _) => 1 + base.node_count() + index.node_count()
  }
}

///|
/// Count statements recursively, including block and branch nodes.
pub fn Statement::node_count(self : Statement) -> Int {
  match self {
    Empty(_) | Unknown(_) => 1
    Assignment(target, value, _, _) =>
      1 + target.node_count() + value.node_count()
    Block(statements, _) => {
      let mut count = 1
      for statement in statements {
        count += statement.node_count()
      }
      count
    }
    If(condition, then_branch, else_branch, _) => {
      let mut count = 1 + condition.node_count() + then_branch.node_count()
      match else_branch {
        Some(branch) => count += branch.node_count()
        None => ()
      }
      count
    }
    Case(expression, arms, _) => {
      let mut count = 1 + expression.node_count()
      for arm in arms {
        count += arm.body.node_count()
        for choice in arm.choices {
          count += choice.node_count()
        }
      }
      count
    }
  }
}

///|
/// Find a port by name without exposing the representation to callers.
pub fn Module::find_port(self : Module, name : String) -> Port? {
  for port in self.ports {
    if port.name == name {
      return Some(port)
    }
  }
  None
}

///|
/// Find a declared signal by name.
pub fn Module::find_signal(self : Module, name : String) -> Signal? {
  for signal in self.signals {
    if signal.name == name {
      return Some(signal)
    }
  }
  None
}

///|
/// Return instance names in declaration order.
pub fn Module::instance_names(self : Module) -> Array[String] {
  let names : Array[String] = []
  for item in self.items {
    match item {
      Instance(instance) => names.push(instance.instance_name)
      _ => ()
    }
  }
  names
}

///|
/// Return module names in declaration order.
pub fn Design::module_names(self : Design) -> Array[String] {
  let names : Array[String] = []
  for mod in self.modules {
    names.push(mod.name)
  }
  names
}

///|
/// Find a module in a design by name.
pub fn Design::find_module(self : Design, name : String) -> Module? {
  for mod in self.modules {
    if mod.name == name {
      return Some(mod)
    }
  }
  None
}

///|
/// Count module items across a design.
pub fn Design::item_count(self : Design) -> Int {
  let mut count = 0
  for mod in self.modules {
    count += mod.items.length()
  }
  count
}