from __future__ import annotations

import ast
from typing import TYPE_CHECKING
import llvmlite.ir as ir

from lib.core.ConstEvaluator import ConstEvaluator

if TYPE_CHECKING:
    from lib.core.LlvmCodeGenerator import LlvmCodeGenerator


class ConstEvalMixin:
    """常量表达式求值 Mixin

    提供数组初始化常量构建与常量表达式求值能力。
    """

    @staticmethod
    def _bswap_constant_value(val: int, width: int) -> int:
        """编译期字节序交换"""
        if width == 16:
            return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
        elif width == 32:
            return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
        elif width == 64:
            return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
        return val

    def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
        _zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
        _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
        if not isinstance(value_node, (ast.List, ast.Set)):
            return None
        if isinstance(ElemType, ir.ArrayType):
            constants: list[ir.Constant] = []
            for elt in value_node.elts:
                if isinstance(elt, (ast.List, ast.Set)):
                    inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
                    if inner_consts:
                        try:
                            constants.append(ir.Constant(ElemType, inner_consts))
                        except Exception:  # fallback to _fallback on constant construction failure
                            constants.append(_fallback)
                    else:
                        constants.append(_fallback)
                else:
                    constants.append(_fallback)
            while len(constants) < ArrayCount:
                constants.append(_fallback)
            return constants[:ArrayCount]
        StructName: str | None = None
        if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
            StructName = elem_type_node.id
        constants = []
        for elt in value_node.elts:
            if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
                const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
                if const:
                    constants.append(const)
                else:
                    constants.append(_fallback)
            elif isinstance(elt, ast.Constant):
                # 字符串常量：根据引号类型决定 i8 或 i8*
                if isinstance(elt.value, str):
                    const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
                    if const:
                        constants.append(const)
                    else:
                        constants.append(_fallback)
                else:
                    const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
                    if const:
                        constants.append(const)
                    else:
                        constants.append(_fallback)
            elif isinstance(elt, ast.UnaryOp):
                const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
                if const:
                    constants.append(const)
                else:
                    constants.append(_fallback)
            elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
                if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
                    try:
                        constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
                    except Exception:  # fallback to _fallback on constant construction failure
                        constants.append(_fallback)
                else:
                    constants.append(_fallback)
            elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
                if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
                    try:
                        constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
                    except Exception:  # fallback to _fallback on constant construction failure
                        constants.append(_fallback)
                else:
                    constants.append(_fallback)
            else:
                const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
                if const_val is not None:
                    try:
                        constants.append(ir.Constant(ElemType, const_val))
                    except Exception:  # fallback to _fallback on constant construction failure
                        constants.append(_fallback)
                else:
                    constants.append(_fallback)
        while len(constants) < ArrayCount:
            constants.append(_fallback)
        if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
            swapped: list[ir.Constant] = []
            for c in constants:
                if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
                    swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
                    swapped.append(ir.Constant(ElemType, swapped_val))
                else:
                    swapped.append(c)
            constants = swapped
        return constants[:ArrayCount]

    def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
        """根据引号类型决定字符串常量的类型：

        - 单引号单字符 → i8 (char 值)
        - 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
        - 空字符串 → 0 (None)，传入 ptr 目标自动隐式转换
        """
        if not isinstance(node.value, str):
            return None
        s: str = node.value
        # 空字符串 → 0 (None)
        if len(s) == 0:
            try:
                return ir.Constant(target_type, 0)
            except Exception:
                return None
        # 获取源代码以判断引号类型
        quote_char: str | None = None
        is_triple: bool = False
        col_offset: int = getattr(node, 'col_offset', 0)
        node_lineno: int = getattr(node, 'lineno', 0)
        # AST 的 lineno 可能不准确（常量节点可能指向下一行），
        # 因此在 lineno 和 lineno-1 两行中查找引号
        for try_lineno in (node_lineno, node_lineno - 1):
            source_line: str | None = Gen._get_source_line(try_lineno)
            if source_line and col_offset < len(source_line):
                ch: str = source_line[col_offset]
                if ch in ("'", '"'):
                    quote_char = ch
                    if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
                        is_triple = True
                    break
        # 判断是否为 char (i8)：单引号、非三重、单字符
        is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
        if is_char:
            # 单引号单字符 → i8
            if isinstance(target_type, ir.IntType):
                try:
                    return ir.Constant(target_type, ord(s))
                except Exception:
                    return None
            # 目标类型不是整数 → 类型不匹配
            return None
        else:
            # 双引号/多字符/三重 → i8* (C 字符串)
            if isinstance(target_type, ir.PointerType):
                str_val: str = s + '\x00'
                str_bytes: bytes = str_val.encode('utf-8')
                arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
                gv_name: str = f"str_const_{id(node)}"
                gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
                gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
                gv.linkage = 'internal'
                return ir.Constant(target_type, gv.get_reference())
            # 目标类型不是指针 → 类型不匹配
            return None

    def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
        """统一的标量常量构建方法。

        合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性：
        - 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
        - 字符串处理委托给 _BuildStringLiteralConstant
        - 显式拒绝 BaseStructType
        - 全部 try/except 保护
        """
        # 显式拒绝结构体类型
        if isinstance(target_type, ir.BaseStructType):
            return None
        # 处理常量节点
        if isinstance(node, ast.Constant):
            # bool 必须在 int 之前判断（bool 是 int 的子类）
            if isinstance(node.value, bool):
                try:
                    return ir.Constant(target_type, 1 if node.value else 0)
                except Exception:
                    return None
            elif isinstance(node.value, int):
                try:
                    return ir.Constant(target_type, node.value)
                except Exception:
                    return None
            elif isinstance(node.value, float):
                try:
                    return ir.Constant(target_type, node.value)
                except Exception:
                    return None
            elif isinstance(node.value, str):
                # 字符串处理委托给 _BuildStringLiteralConstant
                const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
                if const is not None:
                    return const
                # 回退：直接创建字符串全局变量（i8*）
                try:
                    return Gen._create_string_global(node.value)
                except Exception:
                    return None
        # 处理一元负号
        elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
            if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
                try:
                    return ir.Constant(target_type, -node.operand.value)
                except Exception:
                    return None
        # 处理 True/False 名称节点
        elif isinstance(node, ast.Name):
            if node.id == 'True':
                try:
                    return ir.Constant(target_type, 1)
                except Exception:
                    return None
            elif node.id == 'False':
                try:
                    if isinstance(target_type, ir.PointerType):
                        return ir.Constant(target_type, None)
                    return ir.Constant(target_type, 0)
                except Exception:
                    return None
        return None

    def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
        if isinstance(node, ast.Call):
            func_name: str | None = None
            if isinstance(node.func, ast.Name):
                func_name = node.func.id
            elif isinstance(node.func, ast.Attribute):
                func_name = node.func.attr
            if func_name:
                arg_vals: list[int | float | str | None] = []
                for arg in node.args:
                    v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
                    if v is None:
                        return None
                    arg_vals.append(v)
                if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
                    r: int | float | str | None
                    g: int | float | str | None
                    b: int | float | str | None
                    r, g, b = arg_vals
                    return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
                if func_name in Gen._DefineConstants:
                    return Gen._DefineConstants[func_name]
                for key in Gen.functions:
                    if key.endswith(f'__{func_name}'):
                        return None
            return None
        return self._try_eval_const_expr(node, Gen)

    def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
        return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)

    def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
        inner_type: ir.Type = ArrayType.element
        count: int = ArrayType.count
        elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
        _zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
        _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
        constants: list[ir.Constant] = []
        for elt in elts:
            if isinstance(inner_type, ir.ArrayType):
                if isinstance(elt, (ast.List, ast.Set)):
                    inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
                    if inner_consts:
                        constants.append(ir.Constant(inner_type, inner_consts))
                    else:
                        constants.append(_fallback)
                else:
                    constants.append(_fallback)
            elif isinstance(elt, ast.Constant):
                const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
                if const:
                    constants.append(const)
                else:
                    constants.append(_fallback)
            elif isinstance(elt, ast.UnaryOp):
                const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
                if const:
                    constants.append(const)
                else:
                    constants.append(_fallback)
            else:
                constants.append(_fallback)
        while len(constants) < count:
            constants.append(_fallback)
        return constants[:count]
