///|
/// JSON value types
pub(all) enum JsonValue {
  Null
  Bool(Bool)
  Number(Double)
  String(String)
  Array(Array[JsonValue])
  Object(Map[String, JsonValue])
} derive(Show, Eq)

///|
/// Position in the input for error reporting
struct Position {
  line : Int
  column : Int
} derive(Show, Eq)

///|
/// Parser errors
pub(all) suberror ParseError {
  UnexpectedChar(Position, Char)
  UnexpectedEnd
  InvalidNumber(Position, String)
  InvalidEscape(Position, Char)
  InvalidUnicode(Position, String)
  Expected(Position, String)
} derive(Show, Eq)

///|
/// Token types for the lexer
priv enum Token {
  LeftBrace
  RightBrace
  LeftBracket
  RightBracket
  Colon
  Comma
  Null
  True
  False
  Number(Double)
  TString(String)
  Eof
} derive(Eq)

///|
/// Lexer state
priv struct Lexer {
  input : String
  mut pos : Int
  mut line : Int
  mut column : Int
}

///|
/// Create a new lexer
fn Lexer::new(input : String) -> Lexer {
  { input, pos: 0, line: 1, column: 1 }
}

///|
/// Get current position
fn Lexer::current_position(self : Lexer) -> Position {
  { line: self.line, column: self.column }
}

///|
/// Peek at current character
fn Lexer::peek(self : Lexer) -> Char? {
  if self.pos >= self.input.length() {
    None
  } else {
    self.input.get_char(self.pos)
  }
}

///|
/// Advance to next character
fn Lexer::advance(self : Lexer) -> Unit {
  if self.pos < self.input.length() {
    match self.input.get_char(self.pos) {
      Some('\n') => {
        self.line += 1
        self.column = 1
      }
      Some(_) => self.column += 1
      None => ()
    }
    self.pos += 1
  }
}

///|
/// Skip whitespace
fn Lexer::skip_whitespace(self : Lexer) -> Unit {
  while true {
    match self.peek() {
      Some(' ') | Some('\t') | Some('\n') | Some('\r') => self.advance()
      _ => break
    }
  }
}

///|
/// Parse a number
fn Lexer::parse_number(self : Lexer) -> Token raise ParseError {
  let start = self.pos
  let pos = self.current_position()

  // Handle optional minus
  if self.peek() is Some('-') {
    self.advance()
  }

  // Parse integer part
  match self.peek() {
    Some('0') => self.advance()
    Some(c) if c >= '1' && c <= '9' => {
      self.advance()
      while self.peek() is Some(c) && c >= '0' && c <= '9' {
        self.advance()
      }
    }
    _ => raise ParseError::InvalidNumber(pos, "Expected digit")
  }

  // Parse optional fraction
  if self.peek() is Some('.') {
    self.advance()
    let mut has_digit = false
    while self.peek() is Some(c) && c >= '0' && c <= '9' {
      has_digit = true
      self.advance()
    }
    if not(has_digit) {
      raise ParseError::InvalidNumber(pos, "Expected digit after decimal point")
    }
  }

  // Parse optional exponent
  if self.peek() is Some('e') || self.peek() is Some('E') {
    self.advance()
    if self.peek() is Some('+') || self.peek() is Some('-') {
      self.advance()
    }
    let mut has_digit = false
    while self.peek() is Some(c) && c >= '0' && c <= '9' {
      has_digit = true
      self.advance()
    }
    if not(has_digit) {
      raise ParseError::InvalidNumber(pos, "Expected digit in exponent")
    }
  }
  let num_str = try! self.input[start:self.pos]
  let n = @strconv.parse_double(num_str) catch {
    _ => raise ParseError::InvalidNumber(pos, "Invalid number format")
  }
  Number(n)
}

///|
/// Parse a string
fn Lexer::parse_string(self : Lexer) -> Token raise ParseError {
  let pos = self.current_position()
  self.advance() // Skip opening quote
  let result = @buffer.new()
  let ret : Token = while true {
    match self.peek() {
      None => raise ParseError::UnexpectedEnd
      Some('"') => {
        self.advance()
        break TString(result.to_string())
      }
      Some('\\') => {
        self.advance()
        match self.peek() {
          Some('"') => {
            result.write_char('"')
            self.advance()
          }
          Some('\\') => {
            result.write_char('\\')
            self.advance()
          }
          Some('/') => {
            result.write_char('/')
            self.advance()
          }
          Some('b') => {
            result.write_char('\b')
            self.advance()
          }
          Some('f') => {
            result.write_char('\u000C')
            self.advance()
          }
          Some('n') => {
            result.write_char('\n')
            self.advance()
          }
          Some('r') => {
            result.write_char('\r')
            self.advance()
          }
          Some('t') => {
            result.write_char('\t')
            self.advance()
          }
          Some('u') => {
            self.advance()
            let hex = @buffer.new()
            for i = 0; i < 4; i = i + 1 {
              match self.peek() {
                Some(c) if (c >= '0' && c <= '9') ||
                  (c >= 'a' && c <= 'f') ||
                  (c >= 'A' && c <= 'F') => {
                  hex.write_char(c)
                  self.advance()
                }
                _ =>
                  raise ParseError::InvalidUnicode(
                    pos, "Invalid unicode escape",
                  )
              }
            }
            let hex_str = hex.to_string()
            let code = @strconv.parse_int(hex_str, base=16) catch {
              _ =>
                raise ParseError::InvalidUnicode(pos, "Invalid unicode value")
            }
            result.write_char(code.unsafe_to_char())
          }
          Some(c) => raise ParseError::InvalidEscape(pos, c)
          None => raise ParseError::UnexpectedEnd
        }
      }
      Some(c) if c.to_int() < 0x20 => raise ParseError::UnexpectedChar(pos, c)
      Some(c) => {
        result.write_char(c)
        self.advance()
      }
    }
  } else {
    TString(result.to_string())
  }
  ret
}

///|
/// Parse a keyword
fn Lexer::parse_keyword(
  self : Lexer,
  keyword : String,
  token : Token,
) -> Token raise ParseError {
  let pos = self.current_position()
  for i = 0; i < keyword.length(); i = i + 1 {
    match (self.peek(), keyword.get_char(i)) {
      (Some(c1), Some(c2)) if c1 == c2 => self.advance()
      _ => raise ParseError::Expected(pos, keyword)
    }
  }
  token
}

///|
/// Get next token
fn Lexer::next_token(self : Lexer) -> Token raise ParseError {
  self.skip_whitespace()
  match self.peek() {
    None => Eof
    Some('{') => {
      self.advance()
      LeftBrace
    }
    Some('}') => {
      self.advance()
      RightBrace
    }
    Some('[') => {
      self.advance()
      LeftBracket
    }
    Some(']') => {
      self.advance()
      RightBracket
    }
    Some(':') => {
      self.advance()
      Colon
    }
    Some(',') => {
      self.advance()
      Comma
    }
    Some('"') => self.parse_string()
    Some('-') => self.parse_number()
    Some(c) if c >= '0' && c <= '9' => self.parse_number()
    Some('t') => self.parse_keyword("true", Token::True)
    Some('f') => self.parse_keyword("false", Token::False)
    Some('n') => self.parse_keyword("null", Token::Null)
    Some(c) => raise ParseError::UnexpectedChar(self.current_position(), c)
  }
}

///|
/// Parser state
priv struct Parser {
  lexer : Lexer
  mut current : Token
}

///|
/// Create a new parser
fn Parser::new(input : String) -> Parser raise ParseError {
  let lexer = Lexer::new(input)
  let current = lexer.next_token()
  { lexer, current }
}

///|
/// Advance to next token
fn Parser::advance(self : Parser) -> Unit raise ParseError {
  self.current = self.lexer.next_token()
}

///|
/// Parse a JSON value
fn Parser::parse_value(self : Parser) -> JsonValue raise ParseError {
  match self.current {
    Token::Null => {
      self.advance()
      JsonValue::Null
    }
    Token::True => {
      self.advance()
      JsonValue::Bool(true)
    }
    Token::False => {
      self.advance()
      JsonValue::Bool(false)
    }
    Number(n) => {
      self.advance()
      JsonValue::Number(n)
    }
    TString(s) => {
      self.advance()
      JsonValue::String(s)
    }
    LeftBracket => self.parse_array()
    LeftBrace => self.parse_object()
    _ => raise ParseError::Expected(self.lexer.current_position(), "value")
  }
}

///|
/// Parse a JSON array
fn Parser::parse_array(self : Parser) -> JsonValue raise ParseError {
  self.advance() // Skip '['
  let elements : Array[JsonValue] = []
  if self.current == RightBracket {
    self.advance()
    return JsonValue::Array(elements)
  }
  let result : JsonValue = while true {
    elements.push(self.parse_value())
    match self.current {
      Comma => {
        self.advance()
        continue
      }
      RightBracket => {
        self.advance()
        break JsonValue::Array(elements)
      }
      _ =>
        raise ParseError::Expected(self.lexer.current_position(), "',' or ']'")
    }
  } else {
    JsonValue::Array(elements)
  }
  result
}

///|
/// Parse a JSON object
fn Parser::parse_object(self : Parser) -> JsonValue raise ParseError {
  self.advance() // Skip '{'
  let members : Map[String, JsonValue] = {}
  if self.current == RightBrace {
    self.advance()
    return JsonValue::Object(members)
  }
  let result : JsonValue = while true {
    // Parse key
    let key = match self.current {
      TString(s) => {
        self.advance()
        s
      }
      _ =>
        raise ParseError::Expected(self.lexer.current_position(), "string key")
    }

    // Expect colon
    if self.current != Colon {
      raise ParseError::Expected(self.lexer.current_position(), "':'")
    }
    self.advance()

    // Parse value
    let value = self.parse_value()
    members[key] = value
    match self.current {
      Comma => {
        self.advance()
        continue
      }
      RightBrace => {
        self.advance()
        break JsonValue::Object(members)
      }
      _ =>
        raise ParseError::Expected(self.lexer.current_position(), "',' or '}'")
    }
  } else {
    JsonValue::Object(members)
  }
  result
}

///|
/// Parse JSON from a string
pub fn parse(input : String) -> JsonValue raise ParseError {
  let parser = Parser::new(input)
  let value = parser.parse_value()

  // Ensure we've consumed all input
  if parser.current != Eof {
    raise ParseError::Expected(parser.lexer.current_position(), "end of input")
  }
  value
}

///|
/// Convert JsonValue to a formatted string
pub fn JsonValue::to_json_string(self : JsonValue, indent : Int) -> String {
  let spaces = " ".repeat(indent)
  let inner_spaces = " ".repeat(indent + 2)
  match self {
    Null => "null"
    Bool(true) => "true"
    Bool(false) => "false"
    Number(n) => n.to_string()
    String(s) => {
      let escaped = @buffer.new()
      escaped.write_char('"')
      for ch in s {
        match ch {
          '"' => escaped.write_string("\\\"")
          '\\' => escaped.write_string("\\\\")
          '\b' => escaped.write_string("\\b")
          '\u000C' => escaped.write_string("\\f")
          '\n' => escaped.write_string("\\n")
          '\r' => escaped.write_string("\\r")
          '\t' => escaped.write_string("\\t")
          c if c.to_int() < 0x20 => {
            escaped.write_string("\\u")
            let code = c.to_int()
            // Format as 4-digit hex
            let digits = "0123456789abcdef"
            escaped.write_char(digits.get_char((code >> 12) & 0xF).unwrap())
            escaped.write_char(digits.get_char((code >> 8) & 0xF).unwrap())
            escaped.write_char(digits.get_char((code >> 4) & 0xF).unwrap())
            escaped.write_char(digits.get_char(code & 0xF).unwrap())
          }
          c => escaped.write_char(c)
        }
      }
      escaped.write_char('"')
      escaped.to_string()
    }
    Array(arr) =>
      if arr.is_empty() {
        "[]"
      } else {
        let result = @buffer.new()
        result.write_string("[\n")
        for i = 0; i < arr.length(); i = i + 1 {
          result.write_string(inner_spaces)
          result.write_string(arr[i].to_json_string(indent + 2))
          if i < arr.length() - 1 {
            result.write_char(',')
          }
          result.write_char('\n')
        }
        result.write_string(spaces)
        result.write_char(']')
        result.to_string()
      }
    Object(obj) =>
      if obj.is_empty() {
        "{}"
      } else {
        let result = @buffer.new()
        result.write_string("{\n")
        let mut first = true
        for key, value in obj {
          if not(first) {
            result.write_string(",\n")
          }
          first = false
          result.write_string(inner_spaces)
          result.write_string(JsonValue::String(key).to_json_string(0))
          result.write_string(": ")
          result.write_string(value.to_json_string(indent + 2))
        }
        result.write_char('\n')
        result.write_string(spaces)
        result.write_char('}')
        result.to_string()
      }
  }
}