///|
priv suberror CommandRegistryError {
  CommandNameConflict(String)
} derive(Debug)

///|
priv struct CommandSpec {
  name : String
  aliases : Array[String]
  usage : String
  summary : String
  parser : (StringView, StringView) -> CommandParseResult
}

///|
fn CommandSpec::new(
  name : String,
  aliases : Array[String],
  usage : String,
  summary : String,
  parser : (StringView, StringView) -> CommandParseResult,
) -> CommandSpec {
  { name, aliases, usage, summary, parser, }
}

///|
fn CommandSpec::parse(
  self : CommandSpec,
  invoked_name : StringView,
  arguments : StringView,
) -> CommandParseResult {
  (self.parser)(invoked_name, arguments)
}

///|
fn CommandSpec::help_entry(self : CommandSpec) -> CommandHelpEntry {
  {
    name: self.name,
    aliases: self.aliases.copy(),
    usage: self.usage,
    summary: self.summary,
  }
}

///|
priv struct CommandRegistry {
  specs : Array[CommandSpec]
  names : Map[String, Int]
}

///|
fn CommandRegistry::new(
  specs : Array[CommandSpec],
) -> CommandRegistry raise CommandRegistryError {
  let names : Map[String, Int] = Map([])
  for index, spec in specs.iter2() {
    guard names.get(spec.name) is None else {
      raise CommandNameConflict(spec.name)
    }
    names.set(spec.name, index)
    for name in spec.aliases {
      guard names.get(name) is None else { raise CommandNameConflict(name) }
      names.set(name, index)
    }
  }
  { specs, names, }
}

///|
fn CommandRegistry::lookup(
  self : CommandRegistry,
  name : StringView,
) -> CommandSpec? {
  guard self.names.get(name.to_owned()) is Some(index) else { return None }
  Some(self.specs[index])
}

///|
fn CommandRegistry::help_entries(
  self : CommandRegistry,
) -> Array[CommandHelpEntry] {
  self.specs.map(spec => spec.help_entry())
}

///|
priv enum RegistryParseResult {
  RegistryCommand(&ReplCommand)
  RegistryInvalid(String)
  RegistryUnknown
}

///|
fn CommandRegistry::parse_line(
  self : CommandRegistry,
  line : StringView,
) -> RegistryParseResult {
  guard parse_command_invocation(line) is Some(invocation) else {
    return RegistryUnknown
  }
  guard self.lookup(invocation.name) is Some(spec) else {
    return RegistryUnknown
  }
  match spec.parse(invocation.name, invocation.arguments) {
    ParsedCommand(command) => RegistryCommand(command)
    InvalidCommand(message) => RegistryInvalid(message)
  }
}

///|
fn default_command_registry() -> CommandRegistry raise CommandRegistryError {
  CommandRegistry::new([
    break_command_spec(),
    breakpoints_command_spec(),
    delete_command_spec(),
    disable_command_spec(),
    enable_command_spec(),
    run_command_spec(),
    continue_command_spec(),
    next_command_spec(),
    step_command_spec(),
    finish_command_spec(),
    print_command_spec(),
    list_command_spec(),
    backtrace_command_spec(),
    frame_command_spec(),
    up_command_spec(),
    down_command_spec(),
    locals_command_spec(),
    help_command_spec(),
    quit_command_spec(),
  ])
}