///|
pub(open) trait FromString {
  from_string(String) -> Self
}

///|
pub(open) trait Debug: Show {
  to_debug_string(Self) -> String = _
}

///|
impl Debug with to_debug_string(self) {
  to_debug_string(self)
}

///|
pub impl Debug for String with to_debug_string(self) {
  to_debug_string(self)
}

///|
pub fn[T : Show] to_debug_string(val : T) -> String {
  let logger = StringBuilder::new()
  val.output(logger)
  // self.output(logger)
  logger.to_string()
}

///|
pub(open) trait ToString {
  to_string(Self) -> String
}

///|
pub impl ToString for Char with to_string(self) {
  "\{self}"
}

///|
pub impl ToString for StringBuilder with to_string(self) {
  self.to_string()
}

///|
pub(open) trait JoinToString {
  join_all(Self) -> String
}

///|
pub(open) trait ConcatToString: ToString {
  concat_to_string(Self, Self) -> String
}

///|
pub impl ConcatToString for Char with concat_to_string(a, b) {
  "\{a}\{b}"
}

///|
pub impl ToString for String with to_string(a) {
  a |> String::to_string()
}

///|
pub impl ConcatToString for String with concat_to_string(a, b) {
  a + b
}

///|
pub(open) trait ToInt {
  to_int(Self) -> Int
}

///|
pub(open) trait FromInt {
  from_int(Int) -> Self
}

///|
pub impl ToInt for Int with to_int(self) {
  self
}

///|
pub impl FromInt for Int with from_int(self) {
  self
}

///|
pub(open) trait GetChar {
  op_get(Self, Int) -> Char
}

///|
pub impl GetChar for String with op_get(self, idx) {
  self.charcode_at(idx).unsafe_to_char()
}

///|
pub(open) trait GetUtf16CodePoint: GetChar + Length {
  get_utf16(Self, Int) -> Utf16CodePoint = _
  get_utf16_exn(Self, Int) -> Utf16CodePoint raise GetUtf16CharError = _
}

///|
impl GetUtf16CodePoint with get_utf16(self, idx_word) {
  match (try? self.get_utf16_exn(idx_word)) {
    Ok(a) => a
    Err(e) => {
      if DEBUGGER {
        match e {
          OutOfRange(_) => abort("`idx_word` out of range")
          InvalidStringHighSurrogate =>
            abort("high surrogate appeared at the ending")
          InvalidStringLowSurrogate =>
            abort("low surrogate appeared at the beginning")
          UnreachableCode(_) => abort("unreachable code reached")
        }
      }
      panic()
    }
  }
}

///|
pub(all) suberror GetUtf16CharError {
  OutOfRange(Int, length~ : Int) // `idx_word` out of range
  InvalidStringHighSurrogate // high surrogate appeared at the ending
  InvalidStringLowSurrogate // low surrogate appeared at the beginning
  UnreachableCode(Int) // unreachable code reached
} derive(Show)

///|
impl GetUtf16CodePoint with get_utf16_exn(str, idx_word) {
  guard 0 <= idx_word && idx_word < str.length() else {
    raise OutOfRange(idx_word, length=str.length())
  }
  let c = str.op_get(idx_word)
  let w = c.to_int()
  if (0 <= w && w < 0xD800) || (0xE000 <= w && w <= 0xFFFF) {
    return BMP(c)
  } else if 0xD800 <= w && w <= 0xDBFF {
    if idx_word + 1 < str.length() {
      let wl = str.op_get(idx_word + 1).to_int()
      HighSurrogate(combine_surrogate_pair_int(w, wl))
    } else {
      raise InvalidStringHighSurrogate
    }
  } else if 0xDC00 <= w && w <= 0xDFFF {
    if idx_word - 1 >= 0 {
      let wh = str.op_get(idx_word - 1).to_int()
      LowSurrogate(combine_surrogate_pair_int(wh, w))
    } else {
      raise InvalidStringLowSurrogate
    }
  } else {
    raise UnreachableCode(w)
  }
}

///|
pub(open) trait ToChar {
  to_char(Self) -> Char
}

///|
pub impl ToChar for Char with to_char(self) {
  self
}

///|
// pub impl ToChar for String with to_char(self) { self[0] }

///|
pub impl ToChar for Int with to_char(self) {
  self.unsafe_to_char()
}

// ///|
// pub(open) trait Default {
//   default() -> Self
// }

// ///|
// pub impl[T : Default] Default for Ref[T] with default() {
//   Ref::new(T::default())
// }

///|
pub(open) trait ToByte {
  to_byte(Self) -> Byte
}

///|
pub impl ToByte for Int with to_byte(self) = "%i32_to_byte"