///|
/// Parse one complete strict JSON document. Object keys are checked while they
/// are still textual so duplicate-key diagnostics do not depend on a map's
/// overwrite behaviour.
pub fn parse(input : String) -> Result[JsonValue, ParseError] {
  let parser = Parser::new(input)
  parser.skip_whitespace()
  match parser.value() {
    Err(error) => Err(error)
    Ok(value) => {
      parser.skip_whitespace()
      if parser.position == parser.chars.length() {
        Ok(value)
      } else {
        Err(TrailingContent(parser.position))
      }
    }
  }
}

///|
fn Parser::value(self : Parser) -> Result[JsonValue, ParseError] {
  match self.peek() {
    None => Err(UnexpectedEnd(self.position))
    Some('n') => self.literal("null", Null)
    Some('t') => self.literal("true", Bool(true))
    Some('f') => self.literal("false", Bool(false))
    Some('"') => self.string().map(value => String(value))
    Some('[') => self.array()
    Some('{') => self.object()
    Some('-')
    | Some('0')
    | Some('1')
    | Some('2')
    | Some('3')
    | Some('4')
    | Some('5')
    | Some('6')
    | Some('7')
    | Some('8')
    | Some('9') => self.number().map(value => Number(value))
    Some(char) => Err(UnexpectedCharacter(self.position, char))
  }
}

///|
fn Parser::array(self : Parser) -> Result[JsonValue, ParseError] {
  self.position = self.position + 1
  self.skip_whitespace()
  let values : Array[JsonValue] = []
  if self.take_if(']') {
    return Ok(Array(values))
  }
  match self.value() {
    Err(error) => return Err(error)
    Ok(value) => values.push(value)
  }
  self.skip_whitespace()
  if self.take_if(']') {
    return Ok(Array(values))
  }
  if !self.take_if(',') {
    return Err(Expected(self.position, "',' or ']"))
  }
  self.skip_whitespace()
  self.array_more(values)
}

///|
fn Parser::array_more(
  self : Parser,
  values : Array[JsonValue],
) -> Result[JsonValue, ParseError] {
  if self.peek() is Some(']') {
    return Err(Expected(self.position, "a value after ','"))
  }
  match self.value() {
    Err(error) => return Err(error)
    Ok(value) => values.push(value)
  }
  self.skip_whitespace()
  if self.take_if(']') {
    Ok(Array(values))
  } else if self.take_if(',') {
    self.skip_whitespace()
    self.array_more(values)
  } else {
    Err(Expected(self.position, "',' or ']"))
  }
}

///|
fn Parser::object(self : Parser) -> Result[JsonValue, ParseError] {
  self.position = self.position + 1
  self.skip_whitespace()
  let entries : Array[(String, JsonValue)] = []
  if self.take_if('}') {
    return Ok(Object(entries))
  }
  self.object_more(entries)
}

///|
fn Parser::object_more(
  self : Parser,
  entries : Array[(String, JsonValue)],
) -> Result[JsonValue, ParseError] {
  let key_position = self.position
  let key = match self.string() {
    Ok(value) => value
    Err(_) => return Err(Expected(self.position, "an object key string"))
  }
  for entry in entries {
    let (existing, _) = entry
    if existing == key {
      return Err(DuplicateKey(key_position, key))
    }
  }
  self.skip_whitespace()
  if !self.take_if(':') {
    return Err(Expected(self.position, "':'"))
  }
  self.skip_whitespace()
  let value = match self.value() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  entries.push((key, value))
  self.skip_whitespace()
  if self.take_if('}') {
    Ok(Object(entries))
  } else if self.take_if(',') {
    self.skip_whitespace()
    if self.peek() is Some('}') {
      Err(Expected(self.position, "an object key after ','"))
    } else {
      self.object_more(entries)
    }
  } else {
    Err(Expected(self.position, "',' or '}'"))
  }
}

///|
fn Parser::literal(
  self : Parser,
  text : String,
  value : JsonValue,
) -> Result[JsonValue, ParseError] {
  for char in text {
    if self.peek() != Some(char) {
      return Err(Expected(self.position, text))
    }
    self.position = self.position + 1
  }
  Ok(value)
}

///|
fn Parser::number(self : Parser) -> Result[String, ParseError] {
  let start = self.position
  let mut text = ""
  if self.take_if('-') {
    text = "-"
  }
  match self.peek() {
    Some('0') => {
      text = text + "0"
      self.position = self.position + 1
      if self.peek().map(char => char >= '0' && char <= '9').unwrap_or(false) {
        return Err(InvalidNumber(start, "leading zero"))
      }
    }
    Some(char) if char >= '1' && char <= '9' =>
      while self.peek().map(is_digit).unwrap_or(false) {
        match self.next() {
          Some(digit) => text = text + digit.to_string()
          None => ()
        }
      }
    _ => return Err(InvalidNumber(start, "integer part required"))
  }
  if self.take_if('.') {
    text = text + "."
    let fraction_start = self.position
    while self.peek().map(is_digit).unwrap_or(false) {
      match self.next() {
        Some(digit) => text = text + digit.to_string()
        None => ()
      }
    }
    if self.position == fraction_start {
      return Err(InvalidNumber(start, "fraction digits required"))
    }
  }
  if self.take_if('e') || self.take_if('E') {
    text = text + "e"
    if self.take_if('+') {
      text = text + "+"
    } else if self.take_if('-') {
      text = text + "-"
    }
    let exponent_start = self.position
    while self.peek().map(is_digit).unwrap_or(false) {
      match self.next() {
        Some(digit) => text = text + digit.to_string()
        None => ()
      }
    }
    if self.position == exponent_start {
      return Err(InvalidNumber(start, "exponent digits required"))
    }
  }
  Ok(text)
}

///|
fn Parser::string(self : Parser) -> Result[String, ParseError] {
  if !self.take_if('"') {
    return Err(Expected(self.position, "a string"))
  }
  let mut output = ""
  while true {
    match self.next() {
      None => return Err(UnexpectedEnd(self.position))
      Some('"') => return Ok(output)
      Some('\\') =>
        match self.escape() {
          Ok(char) => output = output + char.to_string()
          Err(error) => return Err(error)
        }
      Some(char) if char < ' ' =>
        return Err(InvalidEscape(self.position - 1, "control character"))
      Some(char) => output = output + char.to_string()
    }
  } nobreak {
    Err(UnexpectedEnd(self.position))
  }
}

///|
fn Parser::escape(self : Parser) -> Result[Char, ParseError] {
  let position = self.position
  match self.next() {
    Some('"') => Ok('"')
    Some('\\') => Ok('\\')
    Some('/') => Ok('/')
    Some('b') => Ok('\b')
    Some('f') => Ok('\f')
    Some('n') => Ok('\n')
    Some('r') => Ok('\r')
    Some('t') => Ok('\t')
    Some('u') => self.unicode_escape(position)
    Some(char) =>
      Err(InvalidEscape(position, "unknown escape '" + char.to_string() + "'"))
    None => Err(UnexpectedEnd(position))
  }
}

///|
fn Parser::unicode_escape(
  self : Parser,
  position : Int,
) -> Result[Char, ParseError] {
  let first = match self.hex_code_unit(position) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if first >= 0xd800 && first <= 0xdbff {
    if !self.take_if('\\') || !self.take_if('u') {
      return Err(
        InvalidEscape(position, "high surrogate without low surrogate"),
      )
    }
    let second = match self.hex_code_unit(position) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    if second < 0xdc00 || second > 0xdfff {
      return Err(
        InvalidEscape(position, "high surrogate without low surrogate"),
      )
    }
    Ok((0x10000 + (first - 0xd800) * 0x400 + second - 0xdc00).unsafe_to_char())
  } else if first >= 0xdc00 && first <= 0xdfff {
    Err(InvalidEscape(position, "lone low surrogate"))
  } else {
    Ok(first.unsafe_to_char())
  }
}

///|
fn Parser::hex_code_unit(
  self : Parser,
  position : Int,
) -> Result[Int, ParseError] {
  let mut value = 0
  let mut count = 0
  while count < 4 {
    match self.next() {
      Some(char) if char >= '0' && char <= '9' =>
        value = value * 16 + char.to_int() - '0'.to_int()
      Some(char) if char >= 'a' && char <= 'f' =>
        value = value * 16 + char.to_int() - 'a'.to_int() + 10
      Some(char) if char >= 'A' && char <= 'F' =>
        value = value * 16 + char.to_int() - 'A'.to_int() + 10
      _ =>
        return Err(InvalidEscape(position, "four hexadecimal digits required"))
    }
    count = count + 1
  }
  Ok(value)
}

///|
fn Parser::peek(self : Parser) -> Char? {
  if self.position < self.chars.length() {
    Some(self.chars[self.position])
  } else {
    None
  }
}

///|
fn Parser::next(self : Parser) -> Char? {
  match self.peek() {
    None => None
    Some(char) => {
      self.position = self.position + 1
      Some(char)
    }
  }
}

///|
fn Parser::take_if(self : Parser, expected : Char) -> Bool {
  if self.peek() == Some(expected) {
    self.position = self.position + 1
    true
  } else {
    false
  }
}

///|
fn Parser::skip_whitespace(self : Parser) -> Unit {
  while self.peek().map(is_json_whitespace).unwrap_or(false) {
    self.position = self.position + 1
  }
}

///|
fn is_digit(char : Char) -> Bool {
  char >= '0' && char <= '9'
}

///|
fn is_json_whitespace(char : Char) -> Bool {
  char == ' ' || char == '\n' || char == '\r' || char == '\t'
}