///|
pub(all) enum Width {
  Known(Int)
  Unknown
} derive(Eq, Debug)

///|
pub(all) enum DriveKind {
  Continuous
  Procedural
  InstanceOutput
} derive(Eq, Debug)

///|
pub(all) struct Drive {
  kind : DriveKind
  span : @source.Span
} derive(Eq, Debug)

///|
pub(all) struct SignalInfo {
  name : String
  net_type : @syntax.NetType
  width : Width
  declaration_span : @source.Span
  from_port : Bool
  drivers : Array[Drive]
} derive(Debug)

///|
pub fn SignalInfo::has_driver(self : SignalInfo) -> Bool {
  self.drivers.length() > 0
}

///|
pub(all) struct InstanceInfo {
  module_name : String
  instance_name : String
  span : @source.Span
} derive(Eq, Debug)

///|
pub(all) struct ModuleInfo {
  name : String
  ports : Array[@syntax.Port]
  signals : Array[SignalInfo]
  instances : Array[InstanceInfo]
  span : @source.Span
} derive(Debug)

///|
pub fn ModuleInfo::signal(self : ModuleInfo, name : String) -> SignalInfo? {
  for signal in self.signals {
    if signal.name == name {
      return Some(signal)
    }
  }
  None
}

///|
pub(all) struct SemanticIssue {
  code : String
  message : String
  span : @source.Span
} derive(Eq, Debug)

///|
pub(all) struct DependencyGraph {
  edge_list : Array[(String, String)]
} derive(Eq, Debug)

///|
pub fn DependencyGraph::edges(
  self : DependencyGraph,
) -> Array[(String, String)] {
  self.edge_list
}

///|
pub(all) struct SemanticIndex {
  modules : Array[ModuleInfo]
  graph : DependencyGraph
  issues : Array[SemanticIssue]
  syntax_modules : Array[@syntax.Module]
} derive(Debug)

///|
pub fn SemanticIndex::find_module(
  self : SemanticIndex,
  name : String,
) -> ModuleInfo? {
  for mod in self.modules {
    if mod.name == name {
      return Some(mod)
    }
  }
  None
}

///|
pub fn SemanticIndex::drivers(
  self : SemanticIndex,
  module_name : String,
  signal_name : String,
) -> Array[Drive] {
  match self.find_module(module_name) {
    Some(mod) =>
      match mod.signal(signal_name) {
        Some(signal) => signal.drivers
        None => []
      }
    None => []
  }
}

///|
pub fn SemanticIndex::width_of(
  self : SemanticIndex,
  module_name : String,
  signal_name : String,
) -> Width {
  match self.find_module(module_name) {
    Some(mod) =>
      match mod.signal(signal_name) {
        Some(signal) => signal.width
        None => Unknown
      }
    None => Unknown
  }
}