///|
fn hex_value(c : Char) -> Int {
  match c {
    '0'..='9' => c.to_int() - '0'.to_int()
    'a'..='f' => c.to_int() - 'a'.to_int() + 10
    'A'..='F' => c.to_int() - 'A'.to_int() + 10
    _ => -1
  }
}

///|
fn append_char(out : Array[Byte], c : Char) -> Unit {
  for byte in @utf8.encode(c.to_string()) {
    out.push(byte)
  }
}

///|
fn decode_escapes(text : String) -> (Bytes, Bool) {
  let chars : Array[Char] = text.iter().collect()
  let out : Array[Byte] = []
  let mut stopped = false
  let mut i = 0
  while i < chars.length() && !stopped {
    if chars[i] != '\\' || i + 1 >= chars.length() {
      append_char(out, chars[i])
      i += 1
      continue
    }
    let escaped = chars[i + 1]
    let simple = match escaped {
      'a' => 0x07
      'b' => 0x08
      'e' => 0x1B
      'f' => 0x0C
      'n' => 0x0A
      'r' => 0x0D
      't' => 0x09
      'v' => 0x0B
      '\\' => 0x5C
      _ => -1
    }
    if simple >= 0 {
      out.push(simple.to_byte())
      i += 2
    } else if escaped == 'c' {
      stopped = true
    } else if escaped == 'x' {
      let mut value = 0
      let mut count = 0
      let mut j = i + 2
      while j < chars.length() && count < 2 && hex_value(chars[j]) >= 0 {
        value = value * 16 + hex_value(chars[j])
        count += 1
        j += 1
      }
      if count == 0 {
        out.push(b'\\')
        out.push(b'x')
        i += 2
      } else {
        out.push(value.to_byte())
        i = j
      }
    } else if escaped == '0' {
      let mut value = 0
      let mut count = 0
      let mut j = i + 2
      while j < chars.length() && count < 3 && chars[j] is ('0'..='7') {
        value = value * 8 + chars[j].to_int() - '0'.to_int()
        count += 1
        j += 1
      }
      out.push(value.to_byte())
      i = j
    } else {
      out.push(b'\\')
      append_char(out, escaped)
      i += 2
    }
  }
  (Bytes::from_array(out), stopped)
}

///|
async fn main {
  let args = @env.args()[1:]
  let mut newline = true
  let mut escapes = false
  let mut index = 0
  while index < args.length() {
    let arg = args[index]
    if arg == "--help" {
      @stdio.stdout.write("Usage: echo [-n] [-e|-E] [STRING...]\n")
      return
    }
    if !arg.has_prefix("-") || arg == "-" || arg == "--" {
      break
    }
    let mut valid = true
    for c in arg[1:] {
      if c != 'n' && c != 'e' && c != 'E' {
        valid = false
      }
    }
    if !valid {
      break
    }
    for c in arg[1:] {
      match c {
        'n' => newline = false
        'e' => escapes = true
        'E' => escapes = false
        _ => ()
      }
    }
    index += 1
  }
  let text = args[index:].join(" ")
  if escapes {
    let (output, stopped) = decode_escapes(text)
    @stdio.stdout.write(output)
    if newline && !stopped {
      @stdio.stdout.write("\n")
    }
  } else {
    @stdio.stdout.write(text)
    if newline {
      @stdio.stdout.write("\n")
    }
  }
}