///|
priv struct InlineParser {
  input : String
  mut pos : Int
  lineno : Int
  colno_offset : Int
  line_text : String
}

///|
fn InlineParser::parse(
  input : String,
  lineno : Int,
  colno_offset : Int,
  line_text : String,
) -> Result[Value, NestedTextError] {
  let parser = { input, pos: 0, lineno, colno_offset, line_text }
  match parser.parse_value() {
    Err(err) => Err(err)
    Ok(value) => {
      parser.skip_any_whitespace()
      if parser.pos < parser.input.length() {
        let trailing = parser.input.unsafe_substring(
          start=parser.pos,
          end=parser.input.length(),
        )
        let trimmed = trailing.trim().to_owned()
        let msg = if trimmed.char_length() == 1 {
          "extra character after closing delimiter: '\{trimmed}'."
        } else {
          "extra characters after closing delimiter: '\{trimmed}'."
        }
        Err(parser.error(TrailingContent, msg))
      } else {
        Ok(value)
      }
    }
  }
}

///|
fn InlineParser::parse_value(
  self : InlineParser,
) -> Result[Value, NestedTextError] {
  match self.peek() {
    Some('[') => self.parse_list()
    Some('{') => self.parse_dict()
    _ => Err(self.error(InvalidInlineCharacter, "expected '[' or '{'."))
  }
}

///|
fn InlineParser::parse_list(
  self : InlineParser,
) -> Result[Value, NestedTextError] {
  self.advance_ignore()
  if self.peek() == Some(']') {
    self.advance_ignore()
    return Ok(Value::List([]))
  }
  let items : Array[Value] = []
  for ;; {
    self.skip_whitespace()
    let item_result = match self.peek() {
      Some('[') => self.parse_list()
      Some('{') => self.parse_dict()
      Some('}') => Err(self.error(InvalidInlineCharacter, "expected value."))
      _ =>
        Ok(Value::String(self.parse_inline_string(['[', ']', '{', '}', ','])))
    }
    let item = match item_result {
      Err(err) => return Err(err)
      Ok(v) => v
    }
    items.push(item)
    self.skip_whitespace()
    match self.peek() {
      Some(',') => self.advance_ignore()
      Some(']') => {
        self.advance_ignore()
        return Ok(Value::List(items))
      }
      Some(c) =>
        return Err(
          self.error(
            InvalidInlineCharacter,
            "expected ',' or ']', found '\{c}'.",
          ),
        )
      None =>
        return Err(
          self.error(
            UnterminatedInlineList,
            "line ended without closing delimiter.",
          ),
        )
    }
  }
}

///|
fn InlineParser::parse_dict(
  self : InlineParser,
) -> Result[Value, NestedTextError] {
  self.advance_ignore()
  if self.peek() == Some('}') {
    self.advance_ignore()
    return Ok(Value::Dict([]))
  }
  let pairs : Array[(String, Value)] = []
  let seen_keys : Array[String] = []
  for ;; {
    self.skip_whitespace()
    let key = self.parse_inline_string(['[', ']', '{', '}', ',', ':'])
    match self.peek() {
      Some(':') => self.advance_ignore()
      Some(c) =>
        return Err(
          self.error(InvalidInlineCharacter, "expected ':', found '\{c}'."),
        )
      None =>
        return Err(
          self.error(
            UnterminatedInlineDict,
            "line ended without closing delimiter.",
          ),
        )
    }
    self.skip_whitespace()
    let value_result = match self.peek() {
      Some('[') => self.parse_list()
      Some('{') => self.parse_dict()
      Some('}') | Some(',') => Ok(Value::String(""))
      _ =>
        Ok(Value::String(self.parse_inline_string(['[', ']', '{', '}', ','])))
    }
    let value = match value_result {
      Err(err) => return Err(err)
      Ok(v) => v
    }
    if seen_keys.contains(key) {
      return Err(self.error(DuplicateKey, "duplicate key: \{key}."))
    }
    seen_keys.push(key)
    pairs.push((key, value))
    self.skip_whitespace()
    match self.peek() {
      Some(',') => {
        self.advance_ignore()
        if self.peek() == Some('}') {
          return Err(self.error(InvalidInlineCharacter, "expected value."))
        }
        self.skip_whitespace()
      }
      Some('}') => {
        self.advance_ignore()
        return Ok(Value::Dict(pairs))
      }
      Some(c) =>
        return Err(
          self.error(
            InvalidInlineCharacter,
            "expected ',' or '}', found '\{c}'.",
          ),
        )
      None =>
        return Err(
          self.error(
            UnterminatedInlineDict,
            "line ended without closing delimiter.",
          ),
        )
    }
  }
}

///|
fn InlineParser::parse_inline_string(
  self : InlineParser,
  forbidden : Array[Char],
) -> String {
  let start = self.pos
  for ;; {
    match self.peek() {
      Some(c) =>
        if forbidden.contains(c) {
          break
        } else {
          self.advance_ignore()
        }
      None => break
    }
  }
  self.input.unsafe_substring(start~, end=self.pos).trim().to_owned()
}

///|
fn InlineParser::peek(self : InlineParser) -> Char? {
  self.input.get_char(self.pos)
}

///|
fn InlineParser::advance_ignore(self : InlineParser) -> Unit {
  match self.peek() {
    Some(c) => self.pos += c.utf16_len()
    None => ()
  }
}

///|
fn InlineParser::skip_whitespace(self : InlineParser) -> Unit {
  for ;; {
    match self.peek() {
      Some(' ') | Some('\t') => self.advance_ignore()
      _ => break
    }
  }
}

///|
fn InlineParser::skip_any_whitespace(self : InlineParser) -> Unit {
  for ;; {
    match self.peek() {
      Some(c) =>
        if c.is_ascii_whitespace() {
          self.advance_ignore()
        } else {
          break
        }
      None => break
    }
  }
}

///|
fn InlineParser::error(
  self : InlineParser,
  kind : ErrorKind,
  message : String,
) -> NestedTextError {
  NestedTextError::at(
    kind,
    message,
    self.lineno,
    self.colno_offset + self.pos + 1,
    self.line_text,
  )
}