///|
/// A typed value produced by a transformer function.
/// Built-in types have concrete variants for compile-time pattern matching.
pub(all) enum ParamValue {
  IntVal(Int)
  FloatVal(Double)
  DoubleVal(Double)
  LongVal(Int64)
  ByteVal(Byte)
  ShortVal(Int)
  StringVal(String)
  WordVal(String)
  AnonymousVal(String)
  BigDecimalVal(@decimal.Decimal)
  BigIntegerVal(BigInt)
  /// The capture group did not match. Only a `RegularExpression` with an
  /// optional group gives this value.
  NullVal
  /// The value of a typed parameter type. Read it with the
  /// `ParameterType[T]` handle of the type. Two `TypedVal` values are equal
  /// only when they are the same value (reference equality), so two matches
  /// of the same text are not `==`.
  TypedVal(TypedValue)
}

///|
pub impl Show for ParamValue with fn output(self, logger) {
  match self {
    IntVal(v) => logger.write_string("IntVal(\{v})")
    FloatVal(v) => logger.write_string("FloatVal(\{v})")
    DoubleVal(v) => logger.write_string("DoubleVal(\{v})")
    LongVal(v) => logger.write_string("LongVal(\{v})")
    ByteVal(v) => logger.write_string("ByteVal(\{v})")
    ShortVal(v) => logger.write_string("ShortVal(\{v})")
    StringVal(v) => logger.write_string("StringVal(\{v})")
    WordVal(v) => logger.write_string("WordVal(\{v})")
    AnonymousVal(v) => logger.write_string("AnonymousVal(\{v})")
    BigDecimalVal(v) => logger.write_string("BigDecimalVal(\{v})")
    BigIntegerVal(v) => logger.write_string("BigIntegerVal(\{v})")
    NullVal => logger.write_string("NullVal")
    TypedVal(v) => logger.write_string("TypedVal(<\{v.type_name()}>)")
  }
}

///|
pub impl @debug.Debug for ParamValue with fn to_repr(self) {
  match self {
    IntVal(v) => @debug.Repr::ctor("IntVal", [(None, @debug.Repr(v))])
    FloatVal(v) => @debug.Repr::ctor("FloatVal", [(None, @debug.Repr(v))])
    DoubleVal(v) => @debug.Repr::ctor("DoubleVal", [(None, @debug.Repr(v))])
    LongVal(v) => @debug.Repr::ctor("LongVal", [(None, @debug.Repr(v))])
    ByteVal(v) => @debug.Repr::ctor("ByteVal", [(None, @debug.Repr(v))])
    ShortVal(v) => @debug.Repr::ctor("ShortVal", [(None, @debug.Repr(v))])
    StringVal(v) => @debug.Repr::ctor("StringVal", [(None, @debug.Repr(v))])
    WordVal(v) => @debug.Repr::ctor("WordVal", [(None, @debug.Repr(v))])
    AnonymousVal(v) =>
      @debug.Repr::ctor("AnonymousVal", [(None, @debug.Repr(v))])
    BigDecimalVal(v) =>
      @debug.Repr::ctor("BigDecimalVal", [
        (None, @debug.Repr::literal(v.to_string())),
      ])
    BigIntegerVal(v) =>
      @debug.Repr::ctor("BigIntegerVal", [(None, @debug.Repr(v))])
    NullVal => @debug.Repr::literal("NullVal")
    TypedVal(v) =>
      @debug.Repr::ctor("TypedVal", [
        (None, @debug.Repr::literal("<\{v.type_name()}>")),
      ])
  }
}

///|
pub impl Eq for ParamValue with fn equal(self, other) -> Bool {
  match (self, other) {
    (IntVal(a), IntVal(b)) => a == b
    (FloatVal(a), FloatVal(b)) => a == b
    (DoubleVal(a), DoubleVal(b)) => a == b
    (LongVal(a), LongVal(b)) => a == b
    (ByteVal(a), ByteVal(b)) => a == b
    (ShortVal(a), ShortVal(b)) => a == b
    (StringVal(a), StringVal(b)) => a == b
    (WordVal(a), WordVal(b)) => a == b
    (AnonymousVal(a), AnonymousVal(b)) => a == b
    (BigDecimalVal(a), BigDecimalVal(b)) => a == b
    (BigIntegerVal(a), BigIntegerVal(b)) => a == b
    (NullVal, NullVal) => true
    (TypedVal(a), TypedVal(b)) => physical_equal(a, b)
    _ => false
  }
}

///|
/// A transformer function that converts captured regex group strings into a typed ParamValue.
/// Receives an array of captured group strings (arity matches capture groups in the regex).
pub(all) struct Transformer {
  priv f : (Array[String]) -> ParamValue raise Error
  /// Set for typed parameter types, which must see groups that did not
  /// match as `None`.
  priv with_missing : ((Array[String?]) -> ParamValue raise Error)?
}

///|
pub fn Transformer::new(
  f : (Array[String]) -> ParamValue raise Error,
) -> Transformer {
  { f, with_missing: None, }
}

///|
/// A transformer that gets `None` for a capture group that did not match.
fn Transformer::with_missing(
  f : (Array[String?]) -> ParamValue raise Error,
) -> Transformer {
  { f: values => f(values.map(v => Some(v))), with_missing: Some(f), }
}

///|
/// Call the transformer with the values of the capture groups. A group that
/// did not match is `None`; a plain transformer gets `""` for it.
fn Transformer::call_with_missing(
  self : Transformer,
  values : Array[String?],
) -> ParamValue raise Error {
  match self.with_missing {
    Some(f) => f(values)
    None => (self.f)(values.map(v => v.unwrap_or("")))
  }
}

///|
pub fn Transformer::call(
  self : Transformer,
  groups : Array[String],
) -> ParamValue raise Error {
  (self.f)(groups)
}

///|
pub impl @debug.Debug for Transformer with fn to_repr(_self) {
  @debug.Repr::literal("")
}

///|
pub extend ParamValue with Show::{to_string, output}

///|
#deprecated
pub extend ParamValue with Eq::{not_equal, equal}

///|
#deprecated
pub extend ParamValue with @debug.Debug::{to_repr}

///|
#deprecated
pub extend Transformer with @debug.Debug::{to_repr}