// runtime/json.el — El JSON operations
//
// Thin El wrappers over seed JSON primitives, plus pure-El builders and
// helpers.  Each function here corresponds to (and replaces) a C function
// from runtime/el_runtime.c (lines 2692–3333).
//
// Seed primitives consumed by this module:
//   __json_get(json, key)              -> String   (value as string)
//   __json_get_raw(json, key)          -> String   (raw JSON token)
//   __json_parse_map(s)                -> Map<String, Any>
//   __json_stringify_val(v)            -> String
//   __json_array_len(arr)              -> Int
//   __json_array_get(arr, i)           -> String   (element as JSON fragment)
//   __json_array_get_string(arr, i)    -> String   (element as string value)
//   __json_set(json, key, value)       -> String   (JSON mutation)
//   __str_to_int(s)                    -> Int
//   __str_to_float(s)                  -> Float

// ---------------------------------------------------------------------------
// Core — thin wrappers that delegate directly to seed
// ---------------------------------------------------------------------------

// json_get — extract a value from a JSON object as a string.
// Supports dot-path traversal ("a.b.c") and array indices ("items.0.name").
fn json_get(json: String, key: String) -> String {
    return __json_get(json, key)
}

// json_get_raw — extract a raw JSON token (the un-decoded fragment) for a key.
// Useful when the caller wants to pass a sub-object to another JSON function.
fn json_get_raw(json: String, key: String) -> String {
    return __json_get_raw(json, key)
}

// json_parse — parse a JSON string into a Map<String, Any>.
// Arrays become ElList; objects become ElMap; scalars are typed values.
fn json_parse(s: String) -> Map<String, Any> {
    return __json_parse_map(s)
}

// json_stringify — serialize an El value (ElMap, ElList, String, Int) to JSON.
fn json_stringify(v: Any) -> String {
    return __json_stringify_val(v)
}

// json_array_len — return the number of elements in a JSON array string.
fn json_array_len(arr: String) -> Int {
    return __json_array_len(arr)
}

// json_array_get — return the i-th element of a JSON array as a JSON fragment.
// Nested objects and arrays are returned verbatim.  Out-of-range -> "".
fn json_array_get(arr: String, i: Int) -> String {
    return __json_array_get(arr, i)
}

// json_array_get_string — return the i-th element of a JSON array as a plain
// string value (quotes and escape sequences removed).  Non-string elements
// and out-of-range indices yield "".
fn json_array_get_string(arr: String, i: Int) -> String {
    return __json_array_get_string(arr, i)
}

// ---------------------------------------------------------------------------
// Typed extractors — delegate to seed then convert
// ---------------------------------------------------------------------------

// json_get_string — extract a string value for a key.
// Equivalent to json_get but named explicitly for readability.
fn json_get_string(json: String, key: String) -> String {
    return __json_get(json, key)
}

// json_get_int — extract an integer value for a key.
fn json_get_int(json: String, key: String) -> Int {
    let s: String = __json_get(json, key)
    return str_to_int(s)
}

// json_get_float — extract a floating-point value for a key.
fn json_get_float(json: String, key: String) -> Float {
    let s: String = __json_get(json, key)
    return str_to_float(s)
}

// json_get_bool — extract a boolean value for a key.
// Returns true only when the raw JSON token is the literal "true".
fn json_get_bool(json: String, key: String) -> Bool {
    let s: String = __json_get(json, key)
    return str_eq(s, "true")
}

// ---------------------------------------------------------------------------
// Mutation
// ---------------------------------------------------------------------------

// json_set — set or insert a key/value pair in a JSON object string.
// If the key already exists its value is replaced in-place; otherwise the
// pair is appended before the closing brace.  The value must already be a
// valid JSON-encoded string (e.g. a quoted string, number, or sub-object).
fn json_set(json: String, key: String, value: String) -> String {
    return __json_set(json, key, value)
}

// ---------------------------------------------------------------------------
// Pure-El builders — no seed call required
// ---------------------------------------------------------------------------

// json_build_object — build a JSON object from alternating key/value strings.
//
// keys_and_values must contain an even number of elements laid out as:
//   [key0, val0, key1, val1, ...]
//
// Both keys and values are assumed to be plain strings that will be
// double-quoted and JSON-escaped by this function.  Pass a pre-encoded
// number or sub-object as the value if you need non-string JSON types.
//
// Example:
//   json_build_object(["name", "alice", "role", "admin"])
//   -> {"name":"alice","role":"admin"}
fn json_build_object(keys_and_values: [String]) -> String {
    let n: Int = el_list_len(keys_and_values)
    let result: String = "{"
    let i: Int = 0
    while i < n - 1 {
        let key: String = el_list_get(keys_and_values, i)
        let val: String = el_list_get(keys_and_values, i + 1)
        let sep: String = if i == 0 { "" } else { "," }
        let escaped_key: String = json_escape_string(key)
        let escaped_val: String = json_escape_string(val)
        let result = result + sep + "\"" + escaped_key + "\":\"" + escaped_val + "\""
        let i = i + 2
    }
    return result + "}"
}

// json_build_array — build a JSON array from a list of already-JSON-encoded
// strings.
//
// Each element in items must be a valid JSON fragment (quoted string, number,
// object, array, or literal).  The function joins them with commas and wraps
// the result in brackets.
//
// Example:
//   json_build_array(["\"alice\"", "\"bob\""])
//   -> ["alice","bob"]
fn json_build_array(items: [String]) -> String {
    let n: Int = el_list_len(items)
    let result: String = "["
    let i: Int = 0
    while i < n {
        let item: String = el_list_get(items, i)
        let sep: String = if i == 0 { "" } else { "," }
        let result = result + sep + item
        let i = i + 1
    }
    return result + "]"
}

// json_array_push — append a pre-encoded JSON element to a JSON array string.
// elem must be a valid JSON fragment (e.g. "\"foo\"" or "42").
// Returns a new JSON array string with elem appended.
// Example: json_array_push("[]", "\"alice\"") -> "[\"alice\"]"
fn json_array_push(arr: String, elem: String) -> String {
    let n: Int = json_array_len(arr)
    if n == 0 {
        return "[" + elem + "]"
    }
    // arr ends with ']'; insert before it
    let inner_end: Int = str_last_index_of(arr, "]")
    if inner_end < 0 {
        return "[" + elem + "]"
    }
    let prefix: String = str_slice(arr, 0, inner_end)
    return prefix + "," + elem + "]"
}

// json_escape_string — escape a raw string so it can be safely embedded as a
// JSON string value.
//
// Characters escaped: backslash, double-quote, newline, carriage return, tab.
// The returned value does NOT include surrounding double-quotes; wrap it in
// quotes if you need a complete JSON string literal.
fn json_escape_string(s: String) -> String {
    let s1: String = str_replace(s, "\\", "\\\\")
    let s2: String = str_replace(s1, "\"", "\\\"")
    let s3: String = str_replace(s2, "\n", "\\n")
    let s4: String = str_replace(s3, "\r", "\\r")
    let s5: String = str_replace(s4, "\t", "\\t")
    return s5
}

// ---------------------------------------------------------------------------
// DHARMA byte decoding
// ---------------------------------------------------------------------------

// bytes_to_str — decode a JSON array of integer byte values back to a string.
// "[104,105]" -> "hi"
// Inverse of str_to_bytes (defined in string.el). Defined here because it
// depends on json_array_len and json_array_get_string which live in this file.
fn bytes_to_str(arr: String) -> String {
    let n: Int = json_array_len(arr)
    if n == 0 { return "" }
    let out: String = __str_alloc(n)
    let i: Int = 0
    while i < n {
        let elem: String = json_array_get_string(arr, i)
        let b: Int = __str_to_int(elem)
        out = __str_set_char(out, i, b)
        i = i + 1
    }
    return out
}
