///|
/// Parse a TOML value
fn Parser::parse_value(self : Parser) -> TomlValue raise ParseError {
  self.skip_whitespace()
  match self.peek() {
    Some('"' | '\'') => TomlValue::String(self.parse_string())
    Some('[') => self.parse_array()
    Some('{') => self.parse_inline_table()
    Some('t' | 'f') => TomlValue::Boolean(self.parse_boolean())
    Some('+' | '-' | '0'..='9') => self.parse_number()
    Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
    None => raise ParseError::UnexpectedEof
  }
}

///|
/// Parse an array
fn Parser::parse_array(self : Parser) -> TomlValue raise ParseError {
  self.pos += 1 // Skip '['
  let arr : Array[TomlValue] = []
  self.skip_whitespace()

  // Empty array
  if self.peek() == Some(']') {
    self.pos += 1
    return TomlValue::Array(arr)
  }
  while not(self.is_eof()) {
    self.skip_whitespace()

    // Allow newlines in arrays
    while self.peek() == Some('\n') {
      self.pos += 1
      self.skip_whitespace()
    }
    arr.push(self.parse_value())
    self.skip_whitespace()

    // Allow newlines after value
    while self.peek() == Some('\n') {
      self.pos += 1
      self.skip_whitespace()
    }
    match self.peek() {
      Some(',') => {
        self.pos += 1
        self.skip_whitespace()
        // Allow trailing comma
        if self.peek() == Some(']') {
          self.pos += 1
          return TomlValue::Array(arr)
        }
      }
      Some(']') => {
        self.pos += 1
        return TomlValue::Array(arr)
      }
      Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
      None => raise ParseError::UnexpectedEof
    }
  }
  raise ParseError::UnexpectedEof
}

///|
/// Parse an inline table
fn Parser::parse_inline_table(self : Parser) -> TomlValue raise ParseError {
  self.pos += 1 // Skip '{'
  let table : Map[String, TomlValue] = {}
  self.skip_whitespace()

  // Empty table
  if self.peek() == Some('}') {
    self.pos += 1
    return TomlValue::Table(table)
  }
  while not(self.is_eof()) {
    self.skip_whitespace()
    let key = self.parse_key()
    self.skip_whitespace()
    match self.next() {
      Some('=') => ()
      Some(ch) => raise ParseError::UnexpectedChar(self.pos - 1, ch)
      None => raise ParseError::UnexpectedEof
    }
    self.skip_whitespace()
    let value = self.parse_value()
    if table.contains(key) {
      raise ParseError::DuplicateKey(key)
    }
    table[key] = value
    self.skip_whitespace()
    match self.peek() {
      Some(',') => self.pos += 1
      Some('}') => {
        self.pos += 1
        return TomlValue::Table(table)
      }
      Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
      None => raise ParseError::UnexpectedEof
    }
  }
  raise ParseError::UnexpectedEof
}

///|
/// Parse a key-value pair
fn Parser::parse_keyval(
  self : Parser,
  table : Map[String, TomlValue],
) -> Unit raise ParseError {
  let key = self.parse_key()
  self.skip_whitespace()
  match self.next() {
    Some('=') => ()
    Some(ch) => raise ParseError::UnexpectedChar(self.pos - 1, ch)
    None => raise ParseError::UnexpectedEof
  }
  self.skip_whitespace()
  let value = self.parse_value()
  if table.contains(key) {
    raise ParseError::DuplicateKey(key)
  }
  table[key] = value

  // Skip any trailing whitespace and comments on the same line
  // But do NOT consume the newline - leave it for the main parse loop
  while not(self.is_eof()) && self.peek() != Some('\n') {
    match self.peek() {
      Some(' ' | '\t' | '\r') => self.pos += 1
      Some('#') => {
        // Skip comment until end of line (but don't consume the newline)
        while not(self.is_eof()) {
          self.pos += 1
          if self.peek() == Some('\n') {
            break
          }
        }
        break
      }
      _ => break
    }
  }
}

///|
/// Parse a table header [section]
fn Parser::parse_table_header(self : Parser) -> String raise ParseError {
  self.pos += 1 // Skip '['
  let key = self.parse_key()
  self.skip_whitespace()
  match self.next() {
    Some(']') => key
    Some(ch) => raise ParseError::UnexpectedChar(self.pos - 1, ch)
    None => raise ParseError::UnexpectedEof
  }
}

///|
/// Parse a TOML document
pub fn parse(input : String) -> TomlValue raise ParseError {
  let parser = Parser::new(input)
  let root : Map[String, TomlValue] = {}
  let mut current_table = root
  while not(parser.is_eof()) {
    parser.skip_whitespace()
    if parser.is_eof() {
      break
    }

    // Skip newlines
    if parser.peek() == Some('\n') {
      parser.pos += 1
      continue
    }
    match parser.peek() {
      Some('[') => {
        // Table header
        let table_name = parser.parse_table_header()

        // Create or get the table
        if root.contains(table_name) {
          match root[table_name] {
            Table(t) => current_table = t
            _ => raise ParseError::DuplicateKey(table_name)
          }
        } else {
          let new_table : Map[String, TomlValue] = {}
          root[table_name] = TomlValue::Table(new_table)
          current_table = new_table
        }
      }
      Some(_) =>
        // Key-value pair
        parser.parse_keyval(current_table)
      None => break
    }
    parser.skip_whitespace()

    // Expect newline or EOF after each item
    if not(parser.is_eof()) && parser.peek() != Some('\n') {
      match parser.peek() {
        Some(ch) => raise ParseError::UnexpectedChar(parser.pos, ch)
        None => ()
      }
    }
    if parser.peek() == Some('\n') {
      parser.pos += 1
    }
  }
  TomlValue::Table(root)
}