import t, c
from stdint import *
import memhub
import string
from . import (
    JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT,
    JSON_STRING, JSON_ARRAY, JSON_OBJECT,
    null, bool_val, int_val, float_val, string_val, array, object,
)


class ParseState:
    src: t.CChar | t.CPtr
    pos: t.CSizeT
    len: t.CSizeT
    pool: memhub.MemManager | t.CPtr

    def __init__(self, src: t.CChar | t.CPtr, pool: memhub.MemManager | t.CPtr):
        self.src = src
        self.pos = 0
        self.len = string.strlen(src) if src != None else 0
        self.pool = pool


def _peek(s: ParseState | t.CPtr) -> t.CChar:
    if s.pos >= s.len:
        return '\0'
    return s.src[s.pos]

def _advance(s: ParseState | t.CPtr) -> t.CChar:
    if s.pos >= s.len:
        return '\0'
    ch: t.CChar = s.src[s.pos]
    s.pos += 1
    return ch

def _skip_whitespace(s: ParseState | t.CPtr):
    while s.pos < s.len:
        ch: t.CChar = s.src[s.pos]
        if ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r':
            s.pos += 1
        else:
            break

def _expect(s: ParseState | t.CPtr, ch: t.CChar) -> bool:
    _skip_whitespace(s)
    if s.pos >= s.len:
        return False
    if s.src[s.pos] != ch:
        return False
    s.pos += 1
    return True


def _parse_string(s: ParseState | t.CPtr) -> t.CChar | t.CPtr:
    if _peek(s) != '"':
        return None
    _advance(s)

    start: t.CSizeT = s.pos
    slen: t.CSizeT = 0
    while s.pos < s.len:
        ch: t.CChar = s.src[s.pos]
        if ch == '"':
            break
        if ch == '\\':
            s.pos += 1
            slen += 1
        slen += 1
        s.pos += 1

    buf: t.CChar | t.CPtr = s.pool.alloc(slen + 1)
    if buf == None:
        return None

    s.pos = start
    di: t.CSizeT = 0
    while s.pos < s.len:
        ch: t.CChar = s.src[s.pos]
        if ch == '"':
            s.pos += 1
            break
        if ch == '\\':
            s.pos += 1
            if s.pos < s.len:
                esc: t.CChar = s.src[s.pos]
                if esc == 'n':
                    buf[di] = '\n'
                elif esc == 't':
                    buf[di] = '\t'
                elif esc == 'r':
                    buf[di] = '\r'
                elif esc == '\\':
                    buf[di] = '\\'
                elif esc == '"':
                    buf[di] = '"'
                elif esc == '/':
                    buf[di] = '/'
                else:
                    buf[di] = esc
                di += 1
                s.pos += 1
            continue
        buf[di] = ch
        di += 1
        s.pos += 1

    buf[di] = '\0'
    return buf


def _parse_number(s: ParseState | t.CPtr) -> JsonValue | t.CPtr:
    _skip_whitespace(s)
    is_float: bool = False
    start: t.CSizeT = s.pos

    if s.pos < s.len and s.src[s.pos] == '-':
        s.pos += 1

    while s.pos < s.len:
        ch: t.CChar = s.src[s.pos]
        if ch >= '0' and ch <= '9':
            s.pos += 1
        else:
            break

    if s.pos < s.len and s.src[s.pos] == '.':
        is_float = True
        s.pos += 1
        while s.pos < s.len:
            ch: t.CChar = s.src[s.pos]
            if ch >= '0' and ch <= '9':
                s.pos += 1
            else:
                break

    if s.pos < s.len and (s.src[s.pos] == 'e' or s.src[s.pos] == 'E'):
        is_float = True
        s.pos += 1
        if s.pos < s.len and (s.src[s.pos] == '+' or s.src[s.pos] == '-'):
            s.pos += 1
        while s.pos < s.len:
            ch: t.CChar = s.src[s.pos]
            if ch >= '0' and ch <= '9':
                s.pos += 1
            else:
                break

    num_len: t.CSizeT = s.pos - start
    buf: t.CChar | t.CPtr = s.pool.alloc(num_len + 1)
    if buf == None:
        return null(s.pool)

    i: t.CSizeT = 0
    while i < num_len:
        buf[i] = s.src[start + i]
        i += 1
    buf[num_len] = '\0'

    if is_float:
        val: t.CDouble = string.atof(buf)
        return float_val(s.pool, val)
    else:
        ival: t.CInt64T = string.atoll(buf)
        return int_val(s.pool, ival)


def _parse_value(s: ParseState | t.CPtr) -> JsonValue | t.CPtr:
    _skip_whitespace(s)
    if s.pos >= s.len:
        return null(s.pool)

    ch: t.CChar = _peek(s)

    if ch == '"':
        sv: t.CChar | t.CPtr = _parse_string(s)
        return string_val(s.pool, sv)

    if ch == '{':
        return _parse_object(s)

    if ch == '[':
        return _parse_array(s)

    if ch == '-' or (ch >= '0' and ch <= '9'):
        return _parse_number(s)

    if ch == 't':
        if s.pos + 3 < s.len:
            if s.src[s.pos + 1] == 'r' and s.src[s.pos + 2] == 'u' and s.src[s.pos + 3] == 'e':
                s.pos += 4
                return bool_val(s.pool, True)
        return null(s.pool)

    if ch == 'f':
        if s.pos + 4 < s.len:
            if s.src[s.pos + 1] == 'a' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 's' and s.src[s.pos + 4] == 'e':
                s.pos += 5
                return bool_val(s.pool, False)
        return null(s.pool)

    if ch == 'n':
        if s.pos + 3 < s.len:
            if s.src[s.pos + 1] == 'u' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 'l':
                s.pos += 4
                return null(s.pool)
        return null(s.pool)

    return null(s.pool)


def _parse_array(s: ParseState | t.CPtr) -> JsonValue | t.CPtr:
    if not _expect(s, '['):
        return null(s.pool)

    arr: JsonValue | t.CPtr = array(s.pool)

    _skip_whitespace(s)
    if s.pos < s.len and _peek(s) == ']':
        _advance(s)
        return arr

    item: JsonValue | t.CPtr = _parse_value(s)
    arr._append_child(item)

    while True:
        _skip_whitespace(s)
        if s.pos >= s.len:
            break
        if _peek(s) != ',':
            break
        _advance(s)
        item = _parse_value(s)
        arr._append_child(item)

    _expect(s, ']')
    return arr


def _parse_object(s: ParseState | t.CPtr) -> JsonValue | t.CPtr:
    if not _expect(s, '{'):
        return null(s.pool)

    obj: JsonValue | t.CPtr = object(s.pool)

    _skip_whitespace(s)
    if s.pos < s.len and _peek(s) == '}':
        _advance(s)
        return obj

    _skip_whitespace(s)
    key: t.CChar | t.CPtr = _parse_string(s)
    _expect(s, ':')
    val: JsonValue | t.CPtr = _parse_value(s)
    if key != None and val != None:
        klen: t.CSizeT = string.strlen(key)
        kbuf: t.CChar | t.CPtr = s.pool.alloc(klen + 1)
        if kbuf != None:
            string.memcpy(kbuf, key, klen + 1)
            val.key = kbuf
        obj._append_child(val)

    while True:
        _skip_whitespace(s)
        if s.pos >= s.len:
            break
        if _peek(s) != ',':
            break
        _advance(s)
        _skip_whitespace(s)
        key = _parse_string(s)
        _expect(s, ':')
        val = _parse_value(s)
        if key != None and val != None:
            klen2: t.CSizeT = string.strlen(key)
            kbuf2: t.CChar | t.CPtr = s.pool.alloc(klen2 + 1)
            if kbuf2 != None:
                string.memcpy(kbuf2, key, klen2 + 1)
                val.key = kbuf2
            obj._append_child(val)

    _expect(s, '}')
    return obj


def parse(pool: memhub.MemManager | t.CPtr, src: t.CChar | t.CPtr) -> JsonValue | t.CPtr:
    """解析 JSON 字符串，返回 JsonValue 树"""
    if src == None:
        return null(pool)
    s: ParseState | t.CPtr = ParseState(src, pool)
    return _parse_value(s)
