import t, c
from stdint import *
import memhub
import string
from .tokens import (
    Token, TokenType, TokOp, _init_tables, _kw_lookup, _op_head,
    OpEntry, new_token, token_set_str, token_set_str_literal,
)


# 缩进栈最大深度
LEXER_MAX_INDENT: t.CDefine = 256


# ============================================================
# Lexer 状态结构体
# ============================================================
class Lexer:
    src: str          # 源代码（NUL 结尾）
    pos: t.CSizeT                  # 当前位置
    len: t.CSizeT                  # 源代码长度
    lineno: t.CInt                 # 当前行号（从 1 开始）
    col: t.CInt                    # 当前列号（从 0 开始）
    pool: memhub.MemManager | t.CPtr     # 内存池
    indent_stack: t.CInt | t.CPtr  # 缩进栈（int 数组，从 pool 分配）
    indent_top: t.CInt             # 栈顶索引（-1 表示空栈）
    paren_depth: t.CInt            # 括号深度（>0 时不产生 TokenType.NewLine）
    tokens_head: Token | t.CPtr    # token 链表头
    tokens_tail: Token | t.CPtr    # token 链表尾
    at_line_start: t.CInt          # 1=行首（需处理缩进）
    error_count: t.CInt            # 错误计数

    def __new__(self, pool: memhub.MemManager | t.CPtr):
        ptr: Lexer | t.CPtr = pool.alloc(Lexer.__sizeof__())
        if ptr:
            string.memset(ptr, 0, Lexer.__sizeof__())
        return ptr


def new_lexer(pool: memhub.MemManager | t.CPtr) -> Lexer | t.CPtr:
    """从 pool 分配并返回 Lexer（跨模块安全包装）"""
    return Lexer(pool)


def _lexer_init(lx: Lexer | t.CPtr, src: str,
                pool: memhub.MemManager | t.CPtr):
    """初始化 Lexer"""
    if lx == None: return
    lx.src = src
    lx.pos = 0
    lx.len = string.strlen(src) if src != None else 0
    lx.lineno = 1
    lx.col = 0
    lx.pool = pool
    # 分配缩进栈
    lx.indent_stack = pool.alloc(LEXER_MAX_INDENT * 8)
    if lx.indent_stack != None:
        string.memset(lx.indent_stack, 0, LEXER_MAX_INDENT * 8)
    lx.indent_top = -1
    # 栈底压入 0（基础缩进）
    if lx.indent_stack != None:
        lx.indent_stack[0] = 0
        lx.indent_top = 0
    lx.paren_depth = 0
    lx.tokens_head = None
    lx.tokens_tail = None
    lx.at_line_start = 1
    lx.error_count = 0
    _init_tables(pool)


# ============================================================
# 辅助函数
# ============================================================

def _peek(lx: Lexer | t.CPtr, offset: t.CSizeT) -> t.CChar:
    """查看 pos+offset 处字符，越界返回 '\0'"""
    p: t.CSizeT = lx.pos + offset
    if p >= lx.len:
        return '\0'
    return lx.src[p]


def _peek_cur(lx: Lexer | t.CPtr) -> t.CChar:
    if lx.pos >= lx.len:
        return '\0'
    return lx.src[lx.pos]


def _advance(lx: Lexer | t.CPtr) -> t.CChar:
    """前进一个字符，返回该字符"""
    if lx.pos >= lx.len:
        return '\0'
    ch: t.CChar = lx.src[lx.pos]
    lx.pos += 1
    if ch == '\n':
        lx.lineno += 1
        lx.col = 0
    else:
        lx.col += 1
    return ch


def _is_alpha(ch: t.CChar) -> t.CInt:
    if ch >= 'a' and ch <= 'z': return 1
    if ch >= 'A' and ch <= 'Z': return 1
    if ch == '_': return 1
    return 0


def _is_digit(ch: t.CChar) -> t.CInt:
    if ch >= '0' and ch <= '9': return 1
    return 0


def _is_alnum(ch: t.CChar) -> t.CInt:
    if _is_alpha(ch): return 1
    if _is_digit(ch): return 1
    return 0


def _is_hex(ch: t.CChar) -> t.CInt:
    if ch >= '0' and ch <= '9': return 1
    if ch >= 'a' and ch <= 'f': return 1
    if ch >= 'A' and ch <= 'F': return 1
    return 0


def _is_space(ch: t.CChar) -> t.CInt:
    if ch == ' ': return 1
    if ch == '\t': return 1
    if ch == '\f': return 1
    if ch == '\v': return 1
    if ch == '\r': return 1
    return 0


def _emit(lx: Lexer | t.CPtr, tok: Token | t.CPtr):
    """将 token 追加到链表"""
    if tok == None: return
    if lx.tokens_head == None:
        lx.tokens_head = tok
        lx.tokens_tail = tok
    else:
        lx.tokens_tail.next = tok
        lx.tokens_tail = tok


def _make_op_token(lx: Lexer | t.CPtr, op_id: t.CInt, length: t.CInt,
                   lineno: t.CInt, col: t.CInt) -> Token | t.CPtr:
    tok: Token | t.CPtr = new_token(lx.pool, TokenType.Op, lineno, col)
    if tok == None: return None
    tok.op_subtype = op_id
    tok.end_col_offset = col + length
    return tok


# ============================================================
# 缩进处理
# ============================================================

def _handle_indent(lx: Lexer | t.CPtr):
    """处理行首缩进，生成 TokenType.Indent/TokenType.Dedent。
    跳过空行和注释行（不产生缩进变化）。"""
    # 先跳过空行和注释行
    while lx.pos < lx.len:
        # 记住行首位置，用于非空行回退
        line_start_pos: t.CSizeT = lx.pos
        line_start_col: t.CInt = lx.col
        # 跳过空白（不跨行）
        while lx.pos < lx.len:
            ch: t.CChar = lx.src[lx.pos]
            if ch == ' ' or ch == '\t' or ch == '\f' or ch == '\v':
                lx.pos += 1
                lx.col += 1
            elif ch == '\r':
                # CRLF 行尾符的 \r，跳过（让 \n 触发空行处理）
                lx.pos += 1
            else:
                break
        if lx.pos >= lx.len:
            break
        ch = lx.src[lx.pos]
        if ch == '\n':
            # 空行，跳过
            lx.pos += 1
            lx.lineno += 1
            lx.col = 0
            continue
        if ch == '\r':
            # CRLF 空行（\r 后跟 \n），跳过 \r
            lx.pos += 1
            if lx.pos < lx.len and lx.src[lx.pos] == '\n':
                lx.pos += 1
            lx.lineno += 1
            lx.col = 0
            continue
        if ch == '#':
            # 注释行，跳过到行尾
            while lx.pos < lx.len and lx.src[lx.pos] != '\n':
                lx.pos += 1
            continue
        # 非空行：回退到行首，让后续缩进计算能读到前导空格
        lx.pos = line_start_pos
        lx.col = line_start_col
        break

    if lx.pos >= lx.len:
        # 文件末尾，不再处理缩进
        lx.at_line_start = 0
        return

    # 计算当前行缩进
    indent: t.CInt = 0
    save_pos: t.CSizeT = lx.pos
    save_col: t.CInt = lx.col
    while lx.pos < lx.len:
        ch: t.CChar = lx.src[lx.pos]
        if ch == ' ':
            indent += 1
            lx.pos += 1
            lx.col += 1
        elif ch == '\t':
            indent += 8 - (indent % 8)
            lx.pos += 1
            lx.col += 1
        else:
            break

    # 如果行尾（空行），不处理
    if lx.pos < lx.len:
        ch = lx.src[lx.pos]
        if ch == '\n' or ch == '\r' or ch == '#':
            # 空行或注释行，回退位置让主循环处理
            lx.pos = save_pos
            lx.col = save_col
            lx.at_line_start = 0
            return

    # 与缩进栈比较
    top_indent: t.CInt = lx.indent_stack[lx.indent_top]
    if indent > top_indent:
        # TokenType.Indent
        if lx.indent_top + 1 >= LEXER_MAX_INDENT:
            lx.error_count += 1
            return
        lx.indent_top += 1
        lx.indent_stack[lx.indent_top] = indent
        tok: Token | t.CPtr = new_token(lx.pool, TokenType.Indent, lx.lineno, 0)
        _emit(lx, tok)
    else:
        while indent < lx.indent_stack[lx.indent_top] and lx.indent_top > 0:
            lx.indent_top -= 1
            tok: Token | t.CPtr = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0)
            _emit(lx, tok)
    lx.at_line_start = 0


# ============================================================
# 标识符/关键字
# ============================================================

def _read_name(lx: Lexer | t.CPtr):
    start: t.CSizeT = lx.pos
    start_lineno: t.CInt = lx.lineno
    start_col: t.CInt = lx.col
    while lx.pos < lx.len:
        ch: t.CChar = lx.src[lx.pos]
        if _is_alnum(ch):
            lx.pos += 1
            lx.col += 1
        else:
            break
    length: t.CSizeT = lx.pos - start
    tok: Token | t.CPtr = new_token(lx.pool, TokenType.Name, start_lineno, start_col)
    if tok == None: return
    token_set_str(lx.pool, tok, lx.src, start, length)
    tok.end_lineno = lx.lineno
    tok.end_col_offset = start_col + t.CInt(length)
    # 查找关键字
    kw_id: t.CInt = _kw_lookup(tok.str_val)
    if kw_id:
        tok.kw_subtype = kw_id
    _emit(lx, tok)


# ============================================================
# 数字
# ============================================================

def _read_number(lx: Lexer | t.CPtr):
    start: t.CSizeT = lx.pos
    start_lineno: t.CInt = lx.lineno
    start_col: t.CInt = lx.col
    is_float: t.CInt = 0
    is_complex: t.CInt = 0

    ch0: t.CChar = lx.src[lx.pos]
    # 处理 0x/0o/0b 前缀
    if ch0 == '0' and lx.pos + 1 < lx.len:
        ch1: t.CChar = lx.src[lx.pos + 1]
        if ch1 == 'x' or ch1 == 'X':
            lx.pos += 2
            lx.col += 2
            while lx.pos < lx.len:
                ch: t.CChar = lx.src[lx.pos]
                if _is_hex(ch) or ch == '_':
                    lx.pos += 1
                    lx.col += 1
                else:
                    break
            _emit_number(lx, start, start_lineno, start_col, 0, 0, 0)
            return
        if ch1 == 'o' or ch1 == 'O':
            lx.pos += 2
            lx.col += 2
            while lx.pos < lx.len:
                ch: t.CChar = lx.src[lx.pos]
                if ch >= '0' and ch <= '7':
                    lx.pos += 1
                    lx.col += 1
                elif ch == '_':
                    lx.pos += 1
                    lx.col += 1
                else:
                    break
            _emit_number(lx, start, start_lineno, start_col, 0, 0, 0)
            return
        if ch1 == 'b' or ch1 == 'B':
            lx.pos += 2
            lx.col += 2
            while lx.pos < lx.len:
                ch: t.CChar = lx.src[lx.pos]
                if ch == '0' or ch == '1' or ch == '_':
                    lx.pos += 1
                    lx.col += 1
                else:
                    break
            _emit_number(lx, start, start_lineno, start_col, 0, 0, 0)
            return

    # 十进制整数/浮点
    while lx.pos < lx.len:
        ch: t.CChar = lx.src[lx.pos]
        if _is_digit(ch) or ch == '_':
            lx.pos += 1
            lx.col += 1
        else:
            break

    # 小数部分
    if lx.pos < lx.len and lx.src[lx.pos] == '.':
        is_float = 1
        lx.pos += 1
        lx.col += 1
        while lx.pos < lx.len:
            ch: t.CChar = lx.src[lx.pos]
            if _is_digit(ch) or ch == '_':
                lx.pos += 1
                lx.col += 1
            else:
                break

    # 指数部分
    if lx.pos < lx.len:
        ch: t.CChar = lx.src[lx.pos]
        if ch == 'e' or ch == 'E':
            is_float = 1
            lx.pos += 1
            lx.col += 1
            if lx.pos < lx.len:
                ch = lx.src[lx.pos]
                if ch == '+' or ch == '-':
                    lx.pos += 1
                    lx.col += 1
            while lx.pos < lx.len:
                ch: t.CChar = lx.src[lx.pos]
                if _is_digit(ch) or ch == '_':
                    lx.pos += 1
                    lx.col += 1
                else:
                    break

    # 虚数后缀
    if lx.pos < lx.len and lx.src[lx.pos] == 'j':
        is_complex = 1
        is_float = 1
        lx.pos += 1
        lx.col += 1

    _emit_number(lx, start, start_lineno, start_col, is_float, is_complex, 0)


def _emit_number(lx: Lexer | t.CPtr, start: t.CSizeT,
                 start_lineno: t.CInt, start_col: t.CInt,
                 is_float: t.CInt, is_complex: t.CInt, is_hex: t.CInt):
    length: t.CSizeT = lx.pos - start
    tok: Token | t.CPtr = new_token(lx.pool, TokenType.Number, start_lineno, start_col)
    if tok == None: return
    token_set_str(lx.pool, tok, lx.src, start, length)
    tok.is_float = is_float
    tok.is_complex = is_complex
    tok.end_lineno = lx.lineno
    tok.end_col_offset = start_col + t.CInt(length)
    # 解析数值
    if is_float:
        tok.float_val = string.atof(tok.str_val)
    else:
        tok.int_val = string.atoll(tok.str_val)
    _emit(lx, tok)


# ============================================================
# 字符串
# ============================================================

def _read_string(lx: Lexer | t.CPtr):
    """读取字符串字面量。
    支持前缀: r, b, u, f, rb, br, rf, fr（大小写不敏感）
    支持引号: ', ", ''', """
    start_lineno: t.CInt = lx.lineno
    start_col: t.CInt = lx.col
    prefix_start: t.CSizeT = lx.pos

    # 读取前缀（r/b/u/f）
    is_raw: t.CInt = 0
    is_bytes: t.CInt = 0
    is_fstring: t.CInt = 0
    while lx.pos < lx.len:
        ch: t.CChar = lx.src[lx.pos]
        if ch == 'r' or ch == 'R':
            is_raw = 1
            lx.pos += 1
            lx.col += 1
        elif ch == 'b' or ch == 'B':
            is_bytes = 1
            lx.pos += 1
            lx.col += 1
        elif ch == 'u' or ch == 'U':
            lx.pos += 1
            lx.col += 1
        elif ch == 'f' or ch == 'F':
            is_fstring = 1
            lx.pos += 1
            lx.col += 1
        else:
            break

    if lx.pos >= lx.len:
        lx.error_count += 1
        return

    # 确定引号
    quote: t.CChar = lx.src[lx.pos]
    if quote != '\'' and quote != '"':
        lx.error_count += 1
        return

    # 检查三引号
    is_triple: t.CInt = 0
    if lx.pos + 2 < lx.len:
        if lx.src[lx.pos] == quote and lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote:
            is_triple = 1

    content_start: t.CSizeT = lx.pos
    if is_triple:
        lx.pos += 3
        lx.col += 3
    else:
        lx.pos += 1
        lx.col += 1

    # 读取字符串内容
    content_buf_start: t.CSizeT = lx.pos
    while lx.pos < lx.len:
        ch = lx.src[lx.pos]
        if is_triple:
            if ch == quote:
                if lx.pos + 2 < lx.len:
                    if lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote:
                        break
                elif lx.pos + 2 == lx.len:
                    break
            if ch == '\\':
                lx.pos += 1
                lx.col += 1
                if lx.pos < lx.len:
                    if lx.src[lx.pos] == '\n':
                        lx.lineno += 1
                        lx.col = 0
                    lx.pos += 1
                    lx.col += 1
                continue
            if ch == '\n':
                lx.lineno += 1
                lx.col = 0
            lx.pos += 1
            lx.col += 1
        else:
            if ch == quote:
                break
            if ch == '\n':
                # 单引号字符串不能跨行（除非续行）
                lx.error_count += 1
                return
            if ch == '\\' and not is_raw:
                lx.pos += 1
                lx.col += 1
                if lx.pos < lx.len:
                    lx.pos += 1
                    lx.col += 1
                continue
            lx.pos += 1
            lx.col += 1

    content_end: t.CSizeT = lx.pos
    # 跳过结束引号
    if is_triple:
        if lx.pos + 2 < lx.len:
            lx.pos += 3
            lx.col += 3
        else:
            lx.pos = lx.len
    else:
        if lx.pos < lx.len:
            lx.pos += 1
            lx.col += 1

    # 创建 token
    tok: Token | t.CPtr = new_token(lx.pool, TokenType.String, start_lineno, start_col)
    if tok == None: return
    content_len: t.CSizeT = content_end - content_buf_start

    # 处理转义序列（非 raw 字符串）
    if is_raw == 0 and content_len > 0:
        # 分配转换缓冲区（最大情况：每个字符都是普通字符，无需扩展）
        conv_buf: str = lx.pool.alloc(content_len + 1)
        if conv_buf == None:
            token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len)
        else:
            wpos: t.CSizeT = 0
            rpos: t.CSizeT = 0
            while rpos < content_len:
                ch: t.CChar = lx.src[content_buf_start + rpos]
                if ch == '\\' and rpos + 1 < content_len:
                    rpos += 1
                    next_ch: t.CChar = lx.src[content_buf_start + rpos]
                    if next_ch == 'n':
                        conv_buf[wpos] = '\n'
                    elif next_ch == 't':
                        conv_buf[wpos] = '\t'
                    elif next_ch == 'r':
                        conv_buf[wpos] = '\r'
                    elif next_ch == '\\':
                        conv_buf[wpos] = '\\'
                    elif next_ch == '\'':
                        conv_buf[wpos] = '\''
                    elif next_ch == '"':
                        conv_buf[wpos] = '"'
                    elif next_ch == '0':
                        conv_buf[wpos] = '\0'
                    else:
                        # 未知转义，保留原样（包括 \xNN 等以后再处理）
                        conv_buf[wpos] = '\\'
                        wpos += 1
                        conv_buf[wpos] = next_ch
                    wpos += 1
                    rpos += 1
                else:
                    conv_buf[wpos] = ch
                    wpos += 1
                    rpos += 1
            conv_buf[wpos] = '\0'
            token_set_str_literal(lx.pool, tok, conv_buf)
    else:
        token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len)
    tok.end_lineno = lx.lineno
    tok.end_col_offset = lx.col
    # flags 字段复用：bit0=raw, bit1=bytes, bit2=fstring, bit3=triple
    if is_raw: tok.kw_subtype = tok.kw_subtype | 1
    if is_bytes: tok.kw_subtype = tok.kw_subtype | 2
    if is_fstring: tok.kw_subtype = tok.kw_subtype | 4
    if is_triple: tok.kw_subtype = tok.kw_subtype | 8
    _emit(lx, tok)


# ============================================================
# 运算符
# ============================================================

def _read_operator(lx: Lexer | t.CPtr):
    """贪婪匹配运算符"""
    start_lineno: t.CInt = lx.lineno
    start_col: t.CInt = lx.col
    remaining: t.CSizeT = lx.len - lx.pos

    # 遍历运算符表，找最长匹配
    best_entry: OpEntry | t.CPtr = None
    best_len: t.CInt = 0
    cur: OpEntry | t.CPtr = _op_head
    while cur != None:
        if cur.length <= t.CInt(remaining):
            # 比较 cur.name 与 src[pos:pos+cur.length]
            match: t.CInt = 1
            i: t.CSizeT = 0
            while i < t.CSizeT(cur.length):
                if lx.src[lx.pos + i] != cur.name[i]:
                    match = 0
                    break
                i += 1
            if match:
                if cur.length > best_len:
                    best_len = cur.length
                    best_entry = cur
        cur = cur.next

    if best_entry == None:
        # 未知字符，报错并跳过
        lx.error_count += 1
        lx.pos += 1
        lx.col += 1
        return

    # 更新括号深度
    if best_entry.op_id == TokOp.LPar or best_entry.op_id == TokOp.Lsqb or best_entry.op_id == TokOp.LBrace:
        lx.paren_depth += 1
    elif best_entry.op_id == TokOp.RPar or best_entry.op_id == TokOp.Rsqb or best_entry.op_id == TokOp.RBrace:
        if lx.paren_depth > 0:
            lx.paren_depth -= 1

    tok: Token | t.CPtr = _make_op_token(lx, best_entry.op_id, best_entry.length,
                                          start_lineno, start_col)
    if tok == None: return
    token_set_str(lx.pool, tok, lx.src, lx.pos, t.CSizeT(best_len))
    lx.pos += t.CSizeT(best_len)
    lx.col += best_len
    _emit(lx, tok)


# ============================================================
# 主词法分析入口
# ============================================================

def tokenize(lx: Lexer | t.CPtr) -> Token | t.CPtr:
    """执行词法分析，返回 token 链表头"""
    if lx == None: return None
    while lx.pos < lx.len:
        # 行首缩进处理
        if lx.at_line_start and lx.paren_depth == 0:
            _handle_indent(lx)
            if lx.pos >= lx.len:
                break

        ch: t.CChar = _peek_cur(lx)
        if ch == '\0':
            break

        # 换行
        if ch == '\n':
            if lx.paren_depth > 0:
                # 括号内，产生 TokenType.Nl
                tok: Token | t.CPtr = new_token(lx.pool, TokenType.Nl, lx.lineno, lx.col)
                _emit(lx, tok)
            else:
                # 逻辑换行
                tok: Token | t.CPtr = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col)
                _emit(lx, tok)
                lx.at_line_start = 1
            lx.pos += 1
            lx.lineno += 1
            lx.col = 0
            continue

        # 反斜线续行
        if ch == '\\' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '\n':
            lx.pos += 2
            lx.lineno += 1
            lx.col = 0
            continue

        # 空白
        if _is_space(ch):
            lx.pos += 1
            lx.col += 1
            continue

        # 注释
        if ch == '#':
            while lx.pos < lx.len and lx.src[lx.pos] != '\n':
                lx.pos += 1
            continue

        # 字符串前缀（如 r"..."）或字符串
        if ch == '\'' or ch == '"':
            _read_string(lx)
            continue
        # 字符串前缀字母后跟引号（必须在 _is_alpha 之前检测，否则 f/r/b/u 会被当作 TokenType.Name）
        if (ch == 'r' or ch == 'R' or ch == 'b' or ch == 'B' or
            ch == 'u' or ch == 'U' or ch == 'f' or ch == 'F'):
            if lx.pos + 1 < lx.len:
                ch1: t.CChar = lx.src[lx.pos + 1]
                if ch1 == '\'' or ch1 == '"':
                    _read_string(lx)
                    continue
                # 双前缀如 rb, br, rf, fr
                if lx.pos + 2 < lx.len:
                    ch2: t.CChar = lx.src[lx.pos + 2]
                    if ((ch1 == 'r' or ch1 == 'R' or ch1 == 'b' or ch1 == 'B' or
                         ch1 == 'f' or ch1 == 'F' or ch1 == 'u' or ch1 == 'U') and
                        (ch2 == '\'' or ch2 == '"')):
                        _read_string(lx)
                        continue

        # 标识符/关键字
        if _is_alpha(ch):
            _read_name(lx)
            continue

        # 数字
        if _is_digit(ch):
            _read_number(lx)
            continue

        # 运算符
        _read_operator(lx)

    # 文件结尾：产生 TokenType.NewLine（如果有内容）
    if lx.tokens_tail != None:
        if lx.tokens_tail.type != TokenType.NewLine and lx.tokens_tail.type != TokenType.Nl:
            tok: Token | t.CPtr = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col)
            _emit(lx, tok)

    # 产生剩余 TokenType.Dedent
    while lx.indent_top > 0:
        lx.indent_top -= 1
        tok: Token | t.CPtr = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0)
        _emit(lx, tok)

    # TokenType.EndMarker
    tok: Token | t.CPtr = new_token(lx.pool, TokenType.EndMarker, lx.lineno, 0)
    _emit(lx, tok)

    return lx.tokens_head
