import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar


# ============================================================
# HandlesNonlocal - nonlocal 变量访问（通过闭包 env）
#
# 闭包 env 结构: {i8* ptr0, i8* ptr1, ...} (每个 nonlocal 变量一个 i8* 指针)
# 指针指向原始变量（可能是 alloca 或全局变量）
#
# 在提升的嵌套函数中:
#   1. 函数签名: define i32 @__closure_{name}(i8* %env)
#   2. 入口处: %env_alloca = alloca i8*; store i8* %env, i8** %env_alloca
#   3. 访问 nonlocal var:
#      a. %env_ptr = load i8*, i8** %env_alloca
#      b. %addr = gep i8, i8* %env_ptr, i32 (index * 8)
#      c. %ptr_addr = bitcast i8* %addr to i8**
#      d. %var_ptr_raw = load i8*, i8** %ptr_addr
#      e. %var_ptr = bitcast i8* %var_ptr_raw to i32*
#      f. read: %val = load i32, i32* %var_ptr
#      write: store i32 %new, i32* %var_ptr
# ============================================================


# ============================================================
# 获取或创建 _env_ptr 变量（存储 env 参数的 alloca）
# ============================================================
def get_env_ptr_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
    """获取或创建 _env_ptr 变量（存储闭包 env 指针）"""
    env_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
        trans.SymTab, "_env_ptr")
    if env_var is not None:
        return env_var
    # 创建 alloca 存储 env
    pool: memhub.MemBuddy | t.CPtr = trans.Pool
    builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty)
    if alloca is None:
        return None
    HandlesVar.define_var(trans.SymTab, "_env_ptr", alloca)
    return alloca


# ============================================================
# 加载 nonlocal 变量值 → 返回 i32 Value
#
# env 直接存储 i32 值（不是指针），每个 nonlocal 变量占 4 字节
# ============================================================
def load_nonlocal_var(trans: HT.Translator | t.CPtr,
                      name: str) -> llvmlite.Value | t.CPtr:
    """从闭包 env 加载 nonlocal 变量值（env 直接存储 i32 值）"""
    idx: int = HT.get_nonlocal_index(trans, name)
    if idx < 0:
        return None

    pool: memhub.MemBuddy | t.CPtr = trans.Pool
    builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
    i32_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i32_ty)

    # 1. 加载 env_ptr
    env_alloca: llvmlite.Value | t.CPtr = get_env_ptr_var(trans)
    if env_alloca is None:
        return None
    env_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
    if env_ptr is None:
        return None

    # 2. GEP to offset (idx * 4) — env 直接存储 i32 值
    offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, idx * 4)
    addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
    if addr is None:
        return None

    # 3. Bitcast to i32* and load
    var_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, addr, i32_ptr_ty)
    if var_ptr is None:
        return None
    return llvmlite.build_load(builder, i32_ty, var_ptr)


# ============================================================
# 获取 nonlocal 变量指针（用于 store）
#
# 返回 env 中 i32 槽位的地址（i32*），用于直接 store
# ============================================================
def get_nonlocal_var_ptr(trans: HT.Translator | t.CPtr,
                         name: str) -> llvmlite.Value | t.CPtr:
    """获取 nonlocal 变量在 env 中的地址（i32*），用于 store 操作"""
    idx: int = HT.get_nonlocal_index(trans, name)
    if idx < 0:
        return None

    pool: memhub.MemBuddy | t.CPtr = trans.Pool
    builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
    i32_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i32_ty)

    # 1. 加载 env_ptr
    env_alloca: llvmlite.Value | t.CPtr = get_env_ptr_var(trans)
    if env_alloca is None:
        return None
    env_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
    if env_ptr is None:
        return None

    # 2. GEP to offset (idx * 4) — env 直接存储 i32 值
    offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, idx * 4)
    addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
    if addr is None:
        return None

    # 3. Bitcast to i32* and return (直接指向 env 中的 i32 槽位)
    return llvmlite.build_bitcast(builder, addr, i32_ptr_ty)
