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

///|
/// JSON parsing errors
pub(all) suberror ParseError {
  UnexpectedChar(Int, Char)
  UnexpectedEof
  InvalidNumber(String)
  InvalidEscape(Char)
  InvalidUnicodeEscape(String)
} derive(Eq, Show)

///|
/// Parser state
priv struct Parser {
  mut pos : Int
  input : String
}

///|
/// Create a new parser
fn Parser::new(input : String) -> Parser {
  { pos: 0, input }
}

///|
/// Check if we're at the end of input
fn Parser::is_eof(self : Parser) -> Bool {
  self.pos >= self.input.length()
}

///|
/// Peek current character without consuming
fn Parser::peek(self : Parser) -> Char? {
  if self.is_eof() {
    None
  } else {
    self.input.get_char(self.pos)
  }
}

///|
/// Consume and return current character
fn Parser::next(self : Parser) -> Char raise ParseError {
  match self.peek() {
    Some(ch) => {
      self.pos += 1
      ch
    }
    None => raise ParseError::UnexpectedEof
  }
}

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

///|
/// Expect and consume a specific character
fn Parser::expect(self : Parser, expected : Char) -> Unit raise ParseError {
  let ch = self.next()
  if ch != expected {
    raise ParseError::UnexpectedChar(self.pos - 1, ch)
  }
}

///|
/// Parse a JSON value
fn Parser::parse_value(self : Parser) -> JsonValue raise ParseError {
  self.skip_whitespace()
  match self.peek() {
    Some('n') => self.parse_null()
    Some('t') | Some('f') => self.parse_boolean()
    Some('"') => JsonValue::String(self.parse_string())
    Some('[') => self.parse_array()
    Some('{') => self.parse_object()
    Some(ch) if ch is ('0'..='9') || ch == '-' => self.parse_number()
    Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
    None => raise ParseError::UnexpectedEof
  }
}

///|
/// Parse null
fn Parser::parse_null(self : Parser) -> JsonValue raise ParseError {
  self.expect('n')
  self.expect('u')
  self.expect('l')
  self.expect('l')
  JsonValue::Null
}

///|
/// Parse boolean
fn Parser::parse_boolean(self : Parser) -> JsonValue raise ParseError {
  match self.peek() {
    Some('t') => {
      self.expect('t')
      self.expect('r')
      self.expect('u')
      self.expect('e')
      JsonValue::Boolean(true)
    }
    Some('f') => {
      self.expect('f')
      self.expect('a')
      self.expect('l')
      self.expect('s')
      self.expect('e')
      JsonValue::Boolean(false)
    }
    Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
    None => raise ParseError::UnexpectedEof
  }
}

///|
/// Parse number
fn Parser::parse_number(self : Parser) -> JsonValue raise ParseError {
  let start = self.pos

  // Handle negative sign
  if self.peek() is Some('-') {
    ignore(self.next())
  }

  // Parse integer part
  if self.peek() is Some('0') {
    ignore(self.next())
    // Parse digits
  } else if self.peek() is Some(ch) && ch is ('1'..='9') {
    ignore(self.next())
    while self.peek() is Some(ch) && ch is ('0'..='9') {
      ignore(self.next())
    }
  } else {
    raise ParseError::InvalidNumber("Expected digit")
  }

  // Parse fractional part
  if self.peek() is Some('.') {
    ignore(self.next())
    if self.peek() is Some(ch) && ch is ('0'..='9') {
      while self.peek() is Some(ch) && ch is ('0'..='9') {
        ignore(self.next())
      }
    } else {
      raise ParseError::InvalidNumber("Expected digit after decimal point")
    }
  }

  // Parse exponent
  if self.peek() is Some('e') || self.peek() is Some('E') {
    ignore(self.next())
    if self.peek() is Some('+') || self.peek() is Some('-') {
      ignore(self.next())
    }
    if self.peek() is Some(ch) && ch is ('0'..='9') {
      while self.peek() is Some(ch) && ch is ('0'..='9') {
        ignore(self.next())
      }
    } else {
      raise ParseError::InvalidNumber("Expected digit in exponent")
    }
  }
  let num_str = try! self.input[start:self.pos]
  let n = @strconv.parse_double(num_str) catch {
    _ => raise ParseError::InvalidNumber(num_str.to_string())
  }
  JsonValue::Number(n)
}

///|
/// Parse string
fn Parser::parse_string(self : Parser) -> String raise ParseError {
  self.expect('"')
  let buf = @buffer.new()
  while true {
    match self.peek() {
      Some('"') => {
        ignore(self.next())
        break
      }
      Some('\\') => {
        ignore(self.next())
        let escaped = self.next()
        match escaped {
          '"' => buf.write_char('"')
          '\\' => buf.write_char('\\')
          '/' => buf.write_char('/')
          'b' => buf.write_char('\b')
          'f' => buf.write_char('\u{000C}')
          'n' => buf.write_char('\n')
          'r' => buf.write_char('\r')
          't' => buf.write_char('\t')
          'u' => {
            // Parse unicode escape \uXXXX
            let hex = @buffer.new()
            for i = 0; i < 4; i = i + 1 {
              let ch = self.next()
              if ch is ('0'..='9') || ch is ('a'..='f') || ch is ('A'..='F') {
                hex.write_char(ch)
              } else {
                raise ParseError::InvalidUnicodeEscape(hex.to_string())
              }
            }
            // Convert hex to character
            let hex_str = hex.to_string()
            let code = @strconv.parse_int(hex_str, base=16) catch {
              _ => raise ParseError::InvalidUnicodeEscape(hex_str)
            }
            buf.write_char(code.unsafe_to_char())
          }
          ch => raise ParseError::InvalidEscape(ch)
        }
      }
      Some(ch) => {
        buf.write_char(ch)
        ignore(self.next())
      }
      None => raise ParseError::UnexpectedEof
    }
  }
  buf.to_string()
}

///|
/// Parse array
fn Parser::parse_array(self : Parser) -> JsonValue raise ParseError {
  self.expect('[')
  self.skip_whitespace()
  let arr : Array[JsonValue] = []
  if self.peek() is Some(']') {
    ignore(self.next())
    return JsonValue::Array(arr)
  }
  while true {
    arr.push(self.parse_value())
    self.skip_whitespace()
    match self.peek() {
      Some(',') => {
        ignore(self.next())
        self.skip_whitespace()
      }
      Some(']') => {
        ignore(self.next())
        break
      }
      Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
      None => raise ParseError::UnexpectedEof
    }
  }
  JsonValue::Array(arr)
}

///|
/// Parse object
fn Parser::parse_object(self : Parser) -> JsonValue raise ParseError {
  self.expect('{')
  self.skip_whitespace()
  let obj : Map[String, JsonValue] = {}
  if self.peek() is Some('}') {
    ignore(self.next())
    return JsonValue::Object(obj)
  }
  while true {
    self.skip_whitespace()
    let key = self.parse_string()
    self.skip_whitespace()
    self.expect(':')
    let value = self.parse_value()
    obj[key] = value
    self.skip_whitespace()
    match self.peek() {
      Some(',') => {
        ignore(self.next())
        self.skip_whitespace()
      }
      Some('}') => {
        ignore(self.next())
        break
      }
      Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
      None => raise ParseError::UnexpectedEof
    }
  }
  JsonValue::Object(obj)
}

///|
/// Parse a JSON string into a JsonValue
pub fn parse(input : String) -> JsonValue raise ParseError {
  let parser = Parser::new(input)
  let value = parser.parse_value()
  parser.skip_whitespace()
  if not(parser.is_eof()) {
    match parser.peek() {
      Some(ch) => raise ParseError::UnexpectedChar(parser.pos, ch)
      None => ()
    }
  }
  value
}

///|
/// Convert JsonValue to string representation
pub fn JsonValue::to_json_string(self : JsonValue) -> String {
  let buf = @buffer.new()
  self.write_to(buf)
  buf.to_string()
}

///|
/// Write JsonValue to buffer
fn JsonValue::write_to(self : JsonValue, buf : @buffer.Buffer) -> Unit {
  match self {
    Null => buf.write_string("null")
    Boolean(b) => buf.write_string(if b { "true" } else { "false" })
    Number(n) => buf.write_string(n.to_string())
    String(s) => {
      buf.write_char('"')
      for ch in s {
        match ch {
          '"' => buf.write_string("\\\"")
          '\\' => buf.write_string("\\\\")
          '\b' => buf.write_string("\\b")
          '\u{000C}' => buf.write_string("\\f")
          '\n' => buf.write_string("\\n")
          '\r' => buf.write_string("\\r")
          '\t' => buf.write_string("\\t")
          ch if ch.to_int() < 32 => {
            buf.write_string("\\u")
            let code = ch.to_int()
            let hex = [
              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
              'e', 'f',
            ]
            buf.write_char(hex[code / 4096 % 16])
            buf.write_char(hex[code / 256 % 16])
            buf.write_char(hex[code / 16 % 16])
            buf.write_char(hex[code % 16])
          }
          ch => buf.write_char(ch)
        }
      }
      buf.write_char('"')
    }
    Array(arr) => {
      buf.write_char('[')
      for i, item in arr {
        if i > 0 {
          buf.write_char(',')
        }
        item.write_to(buf)
      }
      buf.write_char(']')
    }
    Object(obj) => {
      buf.write_char('{')
      let mut first = true
      for key, value in obj {
        if not(first) {
          buf.write_char(',')
        }
        first = false
        JsonValue::String(key).write_to(buf)
        buf.write_char(':')
        value.write_to(buf)
      }
      buf.write_char('}')
    }
  }
}