///|
pub(all) struct SimpleValue {
  name : String
  args : @hashmap.HashMap[String, Array[String]]
  flags : @hashmap.HashMap[String, Bool]
  positional_args : Array[String]
  mut subcmd : SimpleValue?
} derive(Show, Eq)

///|
/// Create a simple value to hold the parsed result
pub fn SimpleValue::new(name : String) -> SimpleValue {
  SimpleValue::{
    name,
    args: @hashmap.new(),
    flags: @hashmap.new(),
    positional_args: [],
    subcmd: None,
  }
}

///|
pub fn SimpleValue::get_flag(self : SimpleValue, name : String) -> Bool? {
  self.flags.get(name)
}

///|
/// Get the positional argument values
pub fn[T : BasicValue] SimpleValue::get_positional(
  self : SimpleValue,
) -> Array[T] raise ParserError {
  let out = []
  for value in self.positional_args {
    out.push(T::parse(value)) catch {
      err => raise InvalidArgumentValue(err.to_string())
    }
  }
  out
}

///|
/// Get exactly one value by the argument name
pub fn[T : BasicValue] SimpleValue::get_one(
  self : SimpleValue,
  name : String,
) -> T raise ParserError {
  guard self.args.get(name) is Some(values) else {
    raise InvalidArgumentValue("Value not found for: \{name}")
  }
  guard values.length() == 1 else {
    raise InvalidArgumentValueLength(
      "Invalid argument length: \{values.length()}, expected=1, name=\{name}",
    )
  }
  T::parse(values[0]) catch {
    err => raise InvalidArgumentValue(err.to_string())
  }
}

///|
/// Get one/zero value by the argument name
pub fn[T : BasicValue] SimpleValue::get_option(
  self : SimpleValue,
  name : String,
) -> T? raise ParserError {
  guard self.args.get(name) is Some(values) else { return None }
  let length = values.length()
  guard length <= 1 else {
    raise InvalidArgumentValueLength(
      "Invalid argument length: \{length}, expected<=1, name=\{name}",
    )
  }
  guard values.get(0) is Some(value) else { return None }
  Some(T::parse(value)) catch {
    err => raise InvalidArgumentValue(err.to_string())
  }
}

///|
/// Get multiple values by the argument name
pub fn[T : BasicValue] SimpleValue::get_array(
  self : SimpleValue,
  name : String,
) -> Array[T] raise ParserError {
  guard self.args.get(name) is Some(values) else { return [] }
  let out = []
  for value in values {
    out.push(T::parse(value)) catch {
      err => raise InvalidArgumentValue(err.to_string())
    }
  }
  out
}

///|
pub(open) trait BasicValue {
  parse(input : String) -> Self raise @strconv.StrConvError
}

///|
pub impl BasicValue for String with parse(input : String) -> String raise @strconv.StrConvError {
  input
}

///|
pub impl BasicValue for Double with parse(input : String) -> Double raise @strconv.StrConvError {
  @strconv.parse_double(input)
}

///|
pub impl BasicValue for Int with parse(input : String) -> Int raise @strconv.StrConvError {
  @strconv.parse_int(input)
}

///|
pub impl BasicValue for Int64 with parse(input : String) -> Int64 raise @strconv.StrConvError {
  @strconv.parse_int64(input)
}

///|
pub impl BasicValue for UInt with parse(input : String) -> UInt raise @strconv.StrConvError {
  @strconv.parse_uint(input)
}

///|
pub impl BasicValue for UInt64 with parse(input : String) -> UInt64 raise @strconv.StrConvError {
  @strconv.parse_uint64(input)
}

///|
pub(open) trait Value {
  set_flag(Self, name : String, value : Bool) -> Unit raise ParserError = _
  add_value(Self, name : String, value : String, positional : Bool) -> Unit raise ParserError = _
  select_subcmd(Self, subcmd : String) -> Self raise ParserError = _
}

///|
impl Value with set_flag(_self, name : String, _value : Bool) -> Unit raise ParserError {
  raise ParserError::InvalidArgumentName(
    "Invalid argument name \{name}, unimplemented",
  )
}

///|
impl Value with add_value(
  _self,
  name : String,
  value : String,
  _positional : Bool,
) -> Unit raise ParserError {
  raise ParserError::InvalidArgumentValue(
    "Invalid argument name=\{name}, value=\{value}, unimplemented",
  )
}

///|
impl Value with select_subcmd(_self, subcmd : String) -> Self raise ParserError {
  raise ParserError::InvalidSubCommandName(
    "Invalid sub-command \{subcmd}, unimplemented",
  )
}

///|
pub impl Value for SimpleValue with set_flag(self, name : String, value : Bool) -> Unit raise ParserError {
  self.flags.set(name, value)
}

///|
pub impl Value for SimpleValue with add_value(
  self,
  name : String,
  value : String,
  positional : Bool,
) -> Unit raise ParserError {
  if positional {
    self.positional_args.push(value)
  } else if self.args.get(name) is Some(values) {
    values.push(value)
  } else {
    self.args.set(name, [value])
  }
}

///|
pub impl Value for SimpleValue with select_subcmd(self, subcmd : String) -> SimpleValue raise ParserError {
  match self.subcmd {
    Some(subcmd_value) => subcmd_value
    None => {
      let subcmd_value = SimpleValue::new(subcmd)
      self.subcmd = Some(subcmd_value)
      subcmd_value
    }
  }
}