// JSON Parser - Stress tests recursion, ADTs, and string manipulation
//
// This demonstrates:
// - Recursive descent parsing
// - Complex ADTs for AST representation
// - Character handling with Char type
// - Error handling with Result type

// JSON Value representation
type JsonValue =
    | JsonNull
    | JsonBool(Bool)
    | JsonNumber(Int)
    | JsonString(String)
    | JsonArray(List<JsonValue>)

// Parser state: input chars and position
type ParseState =
    | ParseState(List<Char>, Int)

// Parser result
type ParseResult<T> =
    | ParseOk(T, ParseState)
    | ParseErr(String)

// Get current character
fn peek(state: ParseState): Option<Char> =
    match state {
        ParseState(chars, pos) => List.get(chars, pos)
    }

// Advance position
fn advance(state: ParseState): ParseState =
    match state {
        ParseState(chars, pos) => ParseState(chars, pos + 1)
    }

// Skip whitespace
fn skipWhitespace(state: ParseState): ParseState =
    match peek(state) {
        None => state,
        Some(c) =>
            if c == ' ' || c == '\t' || c == '\n' then
                skipWhitespace(advance(state))
            else state
    }

// Check if character is a digit
fn isDigit(c: Char): Bool =
    c == '0' || c == '1' || c == '2' || c == '3' || c == '4' ||
    c == '5' || c == '6' || c == '7' || c == '8' || c == '9'

fn digitValue(c: Char): Int =
    if c == '0' then 0
    else if c == '1' then 1
    else if c == '2' then 2
    else if c == '3' then 3
    else if c == '4' then 4
    else if c == '5' then 5
    else if c == '6' then 6
    else if c == '7' then 7
    else if c == '8' then 8
    else if c == '9' then 9
    else 0

// Parse a number
fn parseNumber(state: ParseState): ParseResult<Int> =
    parseDigits(state, 0, false)

fn parseDigits(state: ParseState, acc: Int, hasDigits: Bool): ParseResult<Int> =
    match peek(state) {
        None =>
            if hasDigits then ParseOk(acc, state)
            else ParseErr("Expected digit"),
        Some(c) =>
            if isDigit(c) then
                parseDigits(advance(state), acc * 10 + digitValue(c), true)
            else if hasDigits then ParseOk(acc, state)
            else ParseErr("Expected digit")
    }

// Parse a string (simple version - no escapes)
fn parseString(state: ParseState): ParseResult<String> =
    match peek(state) {
        None => ParseErr("Unexpected end"),
        Some(c) =>
            if c != '"' then ParseErr("Expected quote")
            else parseStringContent(advance(state), "")
    }

fn charToString(c: Char): String =
    String.fromChar(c)

fn parseStringContent(state: ParseState, acc: String): ParseResult<String> =
    match peek(state) {
        None => ParseErr("Unterminated string"),
        Some(c) =>
            if c == '"' then ParseOk(acc, advance(state))
            else parseStringContent(advance(state), acc + charToString(c))
    }

// Check if next chars match a keyword
fn matchKeyword(state: ParseState, keyword: String): Bool =
    matchKeywordChars(state, String.chars(keyword), 0)

fn matchKeywordChars(state: ParseState, keywordChars: List<Char>, idx: Int): Bool =
    match List.get(keywordChars, idx) {
        None => true,
        Some(kc) =>
            match state {
                ParseState(chars, pos) =>
                    match List.get(chars, pos + idx) {
                        None => false,
                        Some(ic) => if ic == kc then matchKeywordChars(state, keywordChars, idx + 1) else false
                    }
            }
    }

fn advanceBy(state: ParseState, n: Int): ParseState =
    if n <= 0 then state
    else advanceBy(advance(state), n - 1)

// Main value parser
fn parseValue(state: ParseState): ParseResult<JsonValue> = {
    let s = skipWhitespace(state)
    match peek(s) {
        None => ParseErr("Unexpected end of input"),
        Some(c) =>
            if matchKeyword(s, "null") then
                ParseOk(JsonNull, advanceBy(s, 4))
            else if matchKeyword(s, "true") then
                ParseOk(JsonBool(true), advanceBy(s, 4))
            else if matchKeyword(s, "false") then
                ParseOk(JsonBool(false), advanceBy(s, 5))
            else if c == '"' then
                match parseString(s) {
                    ParseErr(e) => ParseErr(e),
                    ParseOk(str, newState) => ParseOk(JsonString(str), newState)
                }
            else if c == '[' then parseArray(s)
            else if isDigit(c) then
                match parseNumber(s) {
                    ParseErr(e) => ParseErr(e),
                    ParseOk(num, newState) => ParseOk(JsonNumber(num), newState)
                }
            else ParseErr("Unexpected character")
    }
}

// Parse array
fn parseArray(state: ParseState): ParseResult<JsonValue> =
    match peek(state) {
        None => ParseErr("Unexpected end"),
        Some(c) =>
            if c != '[' then ParseErr("Expected [")
            else parseArrayElements(skipWhitespace(advance(state)), [])
    }

fn parseArrayElements(state: ParseState, acc: List<JsonValue>): ParseResult<JsonValue> =
    match peek(state) {
        None => ParseErr("Unterminated array"),
        Some(c) =>
            if c == ']' then ParseOk(JsonArray(acc), advance(state))
            else match parseValue(state) {
                ParseErr(e) => ParseErr(e),
                ParseOk(value, newState) => {
                    let afterComma = skipWhitespace(newState)
                    match peek(afterComma) {
                        None => ParseErr("Unterminated array"),
                        Some(next) =>
                            if next == ']' then ParseOk(JsonArray(List.concat(acc, [value])), advance(afterComma))
                            else if next == ',' then parseArrayElements(skipWhitespace(advance(afterComma)), List.concat(acc, [value]))
                            else ParseErr("Expected , or ]")
                    }
                }
            }
    }

// Public parse function
fn parse(input: String): Result<JsonValue, String> =
    match parseValue(ParseState(String.chars(input), 0)) {
        ParseErr(e) => Err(e),
        ParseOk(value, _) => Ok(value)
    }

// Pretty print JSON
fn valueToString(value: JsonValue): String =
    match value {
        JsonNull => "null",
        JsonBool(b) => if b then "true" else "false",
        JsonNumber(n) => toString(n),
        JsonString(s) => "\"" + s + "\"",
        JsonArray(items) => "[" + String.join(List.map(items, valueToString), ", ") + "]"
    }

// Test the parser
fn main(): Unit with {Console} = {
    Console.print("=== JSON Parser Test ===")
    Console.print("")

    Console.print("Test 1: Simple values")
    testParse("null")
    testParse("true")
    testParse("false")
    testParse("42")
    testParse("\"hello\"")
    Console.print("")

    Console.print("Test 2: Arrays")
    testParse("[]")
    testParse("[1, 2, 3]")
    testParse("[true, false, null]")
    testParse("[1, [2, 3], 4]")
    Console.print("")

    Console.print("Test 3: Nested arrays")
    testParse("[[1, 2], [3, 4]]")
}

fn testParse(input: String): Unit with {Console} =
    match parse(input) {
        Ok(value) => Console.print("  " + input + " => " + valueToString(value)),
        Err(e) => Console.print("  ERROR: " + e)
    }

let output = run main() with {}
