import t, c
from stdint import *
import llvmlite
import memhub
import string


# ============================================================
# HandlesVar - 嵌套作用域符号表 + 变量管理
#
# 使用 Scope 链（Parent 指针）管理嵌套作用域，
# 每个作用域包含一个 VarEntry 数组（扁平化，已验证可工作）。
#
# 作用域类型:
#   SCOPE_MODULE  — 模块级（根作用域）
#   SCOPE_FUNCTION — 函数级
#   SCOPE_BLOCK   — 块级（if/for/while，当前未使用块作用域）
# ============================================================

# 作用域类型常量
SCOPE_MODULE: t.CDefine = 0
SCOPE_FUNCTION: t.CDefine = 1
SCOPE_BLOCK: t.CDefine = 2

# 每个作用域最大变量数
MAX_VARS: t.CDefine = 256


# ============================================================
# VarEntry - 变量表条目
# ============================================================
@t.NoVTable
class VarEntry:
    """变量表条目"""
    Name: t.CChar | t.CPtr
    Alloca: llvmlite.Value | t.CPtr
    Used: t.CInt
    AnnotClassName: t.CChar | t.CPtr      # 原始类型注解的类名（str 别名在结构体字段中触发编译器 bug，改用显式联合类型）


# ============================================================
# Scope - 作用域节点
#
# Parent 指向父作用域（None 表示根），
# Vars 是 VarEntry 数组，VarCount 是当前变量数。
# ============================================================
@t.NoVTable
class Scope:
    """作用域节点"""
    Parent: Scope | t.CPtr
    Vars: VarEntry | t.CPtr
    VarCount: t.CInt
    Kind: t.CInt


# ============================================================
# SymbolTable - 嵌套作用域符号表
#
# Root 是模块级根作用域，Current 是当前作用域。
# enter_scope/exit_scope 管理作用域栈。
# ============================================================
@t.NoVTable
class SymbolTable:
    """嵌套作用域符号表"""
    Pool: memhub.MemBuddy | t.CPtr
    Root: Scope | t.CPtr
    Current: Scope | t.CPtr


# ============================================================
# init_vars — 分配并清零 VarEntry 数组
# ============================================================
def init_vars(pool: memhub.MemBuddy | t.CPtr) -> VarEntry | t.CPtr:
    """分配并清零变量表数组"""
    size: t.CSizeT = MAX_VARS * VarEntry.__sizeof__()
    vars_ptr: VarEntry | t.CPtr = pool.alloc(size)
    if vars_ptr is not None:
        string.memset(vars_ptr, 0, size)
    return vars_ptr


# ============================================================
# find_var — 在变量表中按名称查找
# ============================================================
def find_var(vars_ptr: VarEntry | t.CPtr,
             var_count: int,
             name: str) -> llvmlite.Value | t.CPtr:
    """在变量表中按名称查找"""
    if name is None or vars_ptr is None:
        return None
    entry_size: t.CSizeT = VarEntry.__sizeof__()
    for i in range(var_count):
        entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + i * entry_size
        entry: VarEntry | t.CPtr = (VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
        if entry.Name is not None and entry.Used != 0:
            if string.strcmp(entry.Name, name) == 0:
                return entry.Alloca
    return None


# ============================================================
# find_var_entry — 在变量表中按名称查找，返回 VarEntry（含 AnnotTy）
# ============================================================
def find_var_entry(vars_ptr: VarEntry | t.CPtr,
                   var_count: int,
                   name: str) -> VarEntry | t.CPtr:
    """在变量表中按名称查找，返回 VarEntry 或 None"""
    if name is None or vars_ptr is None:
        return None
    entry_size: t.CSizeT = VarEntry.__sizeof__()
    for i in range(var_count):
        entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + i * entry_size
        entry: VarEntry | t.CPtr = (VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
        if entry.Name is not None and entry.Used != 0:
            if string.strcmp(entry.Name, name) == 0:
                return entry
    return None


# ============================================================
# lookup_var_entry — 从当前作用域逐级向上查找变量，返回 VarEntry
# ============================================================
def lookup_var_entry(symtab: SymbolTable | t.CPtr,
                     name: str) -> VarEntry | t.CPtr:
    """从当前作用域逐级向上查找变量，返回 VarEntry 或 None"""
    if symtab is None or name is None:
        return None
    scope: Scope | t.CPtr = symtab.Current
    while scope is not None:
        result: VarEntry | t.CPtr = find_var_entry(scope.Vars, scope.VarCount, name)
        if result is not None:
            return result
        scope = scope.Parent
    return None


# ============================================================
# set_var_annot_class_name — 设置变量的原始类型注解类名
#
# 在函数参数定义后调用，存储原始类型注解的类名。
# 方法调用检测时，当 alloca 类型是 Ptr(i8)（联合类型简化），
# 通过 AnnotClassName 查找实际结构体类型。
# ============================================================
def set_var_annot_class_name(symtab: SymbolTable | t.CPtr,
                             name: str,
                             class_name: str) -> int:
    """设置变量的原始类型注解类名，返回 0=成功 / 1=失败"""
    if symtab is None or name is None:
        return 1
    entry: VarEntry | t.CPtr = lookup_var_entry(symtab, name)
    if entry is None:
        return 1
    entry.AnnotClassName = class_name
    return 0


# ============================================================
# add_var — 添加变量到变量表
# ============================================================
def add_var(vars_ptr: VarEntry | t.CPtr,
            var_count: int,
            name: str,
            alloca: llvmlite.Value | t.CPtr) -> int:
    """添加变量到变量表"""
    if name is None or alloca is None or vars_ptr is None:
        return 1
    if var_count >= MAX_VARS:
        return 1
    entry_size: t.CSizeT = VarEntry.__sizeof__()
    entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + var_count * entry_size
    entry: VarEntry | t.CPtr = (VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
    entry.Name = name
    entry.Alloca = alloca
    entry.Used = 1
    return 0


# ============================================================
# get_or_create_var — 查找或创建变量 alloca（旧版兼容）
# ============================================================
def get_or_create_var(pool: memhub.MemBuddy | t.CPtr,
                      builder: llvmlite.IRBuilder | t.CPtr,
                      vars_ptr: VarEntry | t.CPtr,
                      var_count: int,
                      name: str,
                      ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
    """查找或创建变量 alloca"""
    existing: llvmlite.Value | t.CPtr = find_var(vars_ptr, var_count, name)
    if existing is not None:
        return existing
    alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
    if alloca is None:
        return None
    if add_var(vars_ptr, var_count, name, alloca) != 0:
        return None
    return alloca


# ============================================================
# _create_scope — 创建新作用域节点（内部辅助函数）
# ============================================================
def _create_scope(pool: memhub.MemBuddy | t.CPtr,
                  parent: Scope | t.CPtr,
                  kind: int) -> Scope | t.CPtr:
    """创建并初始化作用域节点"""
    scope: Scope | t.CPtr = pool.alloc(Scope.__sizeof__())
    if scope is None:
        return None
    string.memset(scope, 0, Scope.__sizeof__())
    scope.Parent = parent
    scope.Vars = init_vars(pool)
    scope.VarCount = 0
    scope.Kind = kind
    if scope.Vars is None:
        return None
    return scope


# ============================================================
# init_symbol_table — 创建符号表（含模块级根作用域）
# ============================================================
def init_symbol_table(pool: memhub.MemBuddy | t.CPtr) -> SymbolTable | t.CPtr:
    """创建并初始化符号表，包含模块级根作用域"""
    if pool is None:
        return None
    symtab: SymbolTable | t.CPtr = pool.alloc(SymbolTable.__sizeof__())
    if symtab is None:
        return None
    string.memset(symtab, 0, SymbolTable.__sizeof__())
    symtab.Pool = pool

    # 创建根作用域（模块级）
    root: Scope | t.CPtr = _create_scope(pool, None, SCOPE_MODULE)
    if root is None:
        return None

    symtab.Root = root
    symtab.Current = root
    return symtab


# ============================================================
# enter_scope — 进入新作用域
# ============================================================
def enter_scope(symtab: SymbolTable | t.CPtr,
                kind: int) -> Scope | t.CPtr:
    """进入新作用域，返回新创建的作用域"""
    if symtab is None:
        return None
    pool: memhub.MemBuddy | t.CPtr = symtab.Pool
    scope: Scope | t.CPtr = _create_scope(pool, symtab.Current, kind)
    if scope is None:
        return None
    symtab.Current = scope
    return scope


# ============================================================
# exit_scope — 退出当前作用域
# ============================================================
def exit_scope(symtab: SymbolTable | t.CPtr):
    """退出当前作用域，恢复到父作用域"""
    if symtab is not None and symtab.Current is not None:
        symtab.Current = symtab.Current.Parent


# ============================================================
# define_var — 在当前作用域定义变量
# ============================================================
def define_var(symtab: SymbolTable | t.CPtr,
               name: str,
               alloca: llvmlite.Value | t.CPtr) -> int:
    """在当前作用域定义变量，返回 0 成功"""
    if symtab is None or name is None or alloca is None:
        return 1
    scope: Scope | t.CPtr = symtab.Current
    if scope is None:
        return 1
    ret: int = add_var(scope.Vars, scope.VarCount, name, alloca)
    if ret == 0:
        scope.VarCount = scope.VarCount + 1
    return ret


# ============================================================
# define_module_var — 在模块作用域定义变量
# ============================================================
def define_module_var(symtab: SymbolTable | t.CPtr,
                      name: str,
                      alloca: llvmlite.Value | t.CPtr) -> int:
    """在模块作用域定义变量，返回 0 成功"""
    if symtab is None or name is None or alloca is None:
        return 1
    scope: Scope | t.CPtr = symtab.Root
    if scope is None:
        return 1
    ret: int = add_var(scope.Vars, scope.VarCount, name, alloca)
    if ret == 0:
        scope.VarCount = scope.VarCount + 1
    return ret


# ============================================================
# lookup_var — 从当前作用域逐级向上查找变量
# ============================================================
def lookup_var(symtab: SymbolTable | t.CPtr,
               name: str) -> llvmlite.Value | t.CPtr:
    """从当前作用域逐级向上查找变量，返回 alloca 或 None"""
    if symtab is None or name is None:
        return None
    scope: Scope | t.CPtr = symtab.Current
    while scope is not None:
        result: llvmlite.Value | t.CPtr = find_var(
            scope.Vars, scope.VarCount, name)
        if result is not None:
            return result
        scope = scope.Parent
    return None


# ============================================================
# lookup_current — 仅在当前作用域查找变量
# ============================================================
def lookup_current(symtab: SymbolTable | t.CPtr,
                   name: str) -> llvmlite.Value | t.CPtr:
    """仅在当前作用域查找变量"""
    if symtab is None or name is None:
        return None
    scope: Scope | t.CPtr = symtab.Current
    if scope is None:
        return None
    return find_var(scope.Vars, scope.VarCount, name)


# ============================================================
# lookup_module_var — 在模块作用域查找变量
# ============================================================
def lookup_module_var(symtab: SymbolTable | t.CPtr,
                      name: str) -> llvmlite.Value | t.CPtr:
    """在模块作用域查找变量"""
    if symtab is None or name is None:
        return None
    scope: Scope | t.CPtr = symtab.Root
    if scope is None:
        return None
    return find_var(scope.Vars, scope.VarCount, name)


# ============================================================
# get_or_create_sym — 查找或创建变量（在当前作用域）
# ============================================================
def get_or_create_sym(symtab: SymbolTable | t.CPtr,
                      pool: memhub.MemBuddy | t.CPtr,
                      builder: llvmlite.IRBuilder | t.CPtr,
                      name: str,
                      ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
    """查找或创建变量 alloca（在当前作用域）"""
    existing: llvmlite.Value | t.CPtr = lookup_current(symtab, name)
    if existing is not None:
        return existing
    alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
    if alloca is None:
        return None
    define_var(symtab, name, alloca)
    return alloca
