// hir_lower.bux — HIR lowering: AST → HIR transformation (ported from hir_lower.nim)
// Transforms the typed AST into a lower-level IR suitable for code generation.
module HirLower {

// ---------------------------------------------------------------------------
// Lowering context
// ---------------------------------------------------------------------------
struct LowerCtx {
    module: *Module,
    scope: *Scope,
    funcs: *HirFunc,
    funcCount: int,
    externFuncs: *HirFunc,
    externCount: int,
    varCounter: int,
    tryCounter: int,
}

// ---------------------------------------------------------------------------
// TypeExpr.kind → Type.kind resolver
// TypeExpr.kind values (0-5) overlap with Type.kind values — this
// resolves the correct Type.kind for codegen.
// ---------------------------------------------------------------------------

func Lcx_ResolveTypeKind(te: *TypeExpr) -> int {
    if te == null as *TypeExpr { return tyUnknown; }
    let name: String = te.typeName;

    if te.kind == tekPointer { return tyPointer; }
    if te.kind == tekSlice { return tySlice; }
    if te.kind == tekTuple { return tyTuple; }

    // Named types: resolve by name
    if String_Eq(name, "void") { return tyVoid; }
    if String_Eq(name, "bool") { return tyBool; }
    if String_Eq(name, "bool8") { return tyBool8; }
    if String_Eq(name, "bool16") { return tyBool16; }
    if String_Eq(name, "bool32") { return tyBool32; }
    if String_Eq(name, "char8") { return tyChar8; }
    if String_Eq(name, "char16") { return tyChar16; }
    if String_Eq(name, "char32") { return tyChar32; }
    if String_Eq(name, "String") { return tyStr; }
    if String_Eq(name, "str") { return tyStr; }
    if String_Eq(name, "int8") { return tyInt8; }
    if String_Eq(name, "int16") { return tyInt16; }
    if String_Eq(name, "int32") { return tyInt32; }
    if String_Eq(name, "int64") { return tyInt64; }
    if String_Eq(name, "int") { return tyInt; }
    if String_Eq(name, "uint8") { return tyUInt8; }
    if String_Eq(name, "uint16") { return tyUInt16; }
    if String_Eq(name, "uint32") { return tyUInt32; }
    if String_Eq(name, "uint64") { return tyUInt64; }
    if String_Eq(name, "uint") { return tyUInt; }
    if String_Eq(name, "float32") { return tyFloat32; }
    if String_Eq(name, "float64") { return tyFloat64; }
    if String_Eq(name, "float") { return tyFloat64; }

    return tyNamed;
}

// ---------------------------------------------------------------------------
// Fresh name generation
// ---------------------------------------------------------------------------

func Lcx_FreshName(ctx: *LowerCtx) -> String {
    ctx.varCounter = ctx.varCounter + 1;
    return String_FromInt(ctx.varCounter as int64);
}

// ---------------------------------------------------------------------------
// Type → HIR type
// ---------------------------------------------------------------------------

func Lcx_LowerType(typeKind: int, typeName: String) -> int {
    return typeKind;
}

// ---------------------------------------------------------------------------
// Expression lowering
// ---------------------------------------------------------------------------

func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
    if expr == null as *Expr { return null as *HirNode; }

    let line: uint32 = expr.line;
    let col: uint32 = expr.column;
    let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
    n.kind = hBlock;
    n.line = line;
    n.column = col;

    let kind: int = expr.kind;

    // Literal
    if kind == ekLiteral {
        n.kind = hLit;
        n.intValue = expr.tokKind;
        n.strValue = expr.tokText;
        return n;
    }

    // Identifier → variable reference
    if kind == ekIdent {
        n.kind = hVar;
        n.strValue = expr.strValue;
        let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
        n.typeKind = sym.typeKind;
        if expr.refType != null as *TypeExpr {
            n.typeName = expr.refType.typeName;
        }
        if sym.typeName != null as String && !String_Eq(sym.typeName, "") {
            n.typeName = sym.typeName;
        }
        return n;
    }

    // self → variable reference named "self"
    if kind == ekSelf {
        n.kind = hVar;
        n.strValue = "self";
        let sym: Symbol = Scope_Lookup(ctx.scope, "self");
        n.typeKind = sym.typeKind;
        if sym.typeName != null as String && !String_Eq(sym.typeName, "") {
            n.typeName = sym.typeName;
        }
        return n;
    }

    // Binary
    if kind == ekBinary {
        // Assignment operator → use hAssign
        if expr.intValue == tkAssign {
            n.kind = hAssign;
            n.child1 = Lcx_LowerExpr(ctx, expr.child1);
            n.child2 = Lcx_LowerExpr(ctx, expr.child2);
            return n;
        }
        n.kind = hBinary;
        n.intValue = expr.intValue; // operator
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        n.child2 = Lcx_LowerExpr(ctx, expr.child2);
        return n;
    }

    // Unary
    if kind == ekUnary {
        n.kind = hUnary;
        n.intValue = expr.intValue;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        return n;
    }

    // Call
    if kind == ekCall {
        n.kind = hCall;
        // Method call desugaring: obj.method(args) → Type_method(obj, args)
        if expr.child1 != null as *Expr && expr.child1.kind == ekField {
            let methodName: String = expr.child1.strValue;
            var receiverTypeName: String = "";
            if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent {
                let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.child1.strValue);
                receiverTypeName = sym.typeName;
            }
            if String_Eq(receiverTypeName, "") && expr.child1.child1 != null as *Expr && expr.child1.child1.refType != null as *TypeExpr {
                receiverTypeName = expr.child1.child1.refType.typeName;
            }
            if !String_Eq(receiverTypeName, "") {
                // Strip trailing '*' from pointer type names (e.g. "Box*" → "Box")
                var baseName: String = receiverTypeName;
                let len: int = bux_strlen(baseName) as int;
                if len > 0 {
                    let lastChar: String = bux_str_slice(baseName, (len - 1) as uint, 1);
                    if String_Eq(lastChar, "*") {
                        baseName = bux_str_slice(baseName, 0, (len - 1) as uint);
                    }
                }
                n.strValue = String_Concat(baseName, "_");
                n.strValue = String_Concat(n.strValue, methodName);
            }
            // Lower receiver as first argument
            let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1);
            n.child1 = recv;
            // Lower remaining arguments from linked list
            var arg: *ExprList = expr.callArgs;
            var argIdx: int = 0;
            while arg != null as *ExprList {
                let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
                if argIdx == 0 {
                    n.child2 = lowered;
                } else if argIdx == 1 {
                    // Third argument — start linked list
                    let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
                    firstExtra.node = lowered;
                    firstExtra.next = null as *HirArgList;
                    n.extraData = firstExtra as *void;
                    n.extraCount = 1;
                } else {
                    // Additional args — append to linked list
                    var cur: *HirArgList = n.extraData as *HirArgList;
                    while cur.next != null as *HirArgList {
                        cur = cur.next;
                    }
                    let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
                    newNode.node = lowered;
                    newNode.next = null as *HirArgList;
                    cur.next = newNode;
                    n.extraCount = n.extraCount + 1;
                }
                arg = arg.next;
                argIdx = argIdx + 1;
            }
            return n;
        }
        // Get callee name
        if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
            n.strValue = expr.child1.strValue;
        }
        // Lower arguments from linked list
        var arg: *ExprList = expr.callArgs;
        var argIdx: int = 0;
        while arg != null as *ExprList {
            let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
            if argIdx == 0 {
                n.child1 = lowered;
            } else if argIdx == 1 {
                n.child2 = lowered;
            } else if argIdx == 2 {
                // Third argument — start linked list
                let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
                firstExtra.node = lowered;
                firstExtra.next = null as *HirArgList;
                n.extraData = firstExtra as *void;
                n.extraCount = 1;
            } else {
                // Additional args — append to linked list
                var cur: *HirArgList = n.extraData as *HirArgList;
                while cur.next != null as *HirArgList {
                    cur = cur.next;
                }
                let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
                newNode.node = lowered;
                newNode.next = null as *HirArgList;
                cur.next = newNode;
                n.extraCount = n.extraCount + 1;
            }
            arg = arg.next;
            argIdx = argIdx + 1;
        }
        return n;
    }

    // Sizeof
    if kind == ekSizeOf {
        n.kind = hSizeOf;
        if expr.refType != null as *TypeExpr {
            n.typeName = expr.refType.typeName;
        }
        return n;
    }

    // Field access
    if kind == ekField {
        // Check if this is enum variant access: Color::Green
        if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
            let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
            if sym.decl != null as *Decl && sym.decl.kind == dkEnum {
                // Emit as variable reference: Color_Green
                n.kind = hVar;
                n.strValue = String_Concat(String_Concat(expr.child1.strValue, "_"), expr.strValue);
                return n;
            }
        }
        n.kind = hFieldPtr;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        n.strValue = expr.strValue;
        // Get struct type from base expr refType
        if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr {
            n.typeName = expr.child1.refType.typeName;
        }
        return n;
    }

    // spawn Callee(args)
    if kind == ekSpawn {
        n.kind = hSpawn;
        if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
            n.strValue = expr.child1.strValue;
        }
        if expr.child2 != null as *Expr {
            n.child1 = Lcx_LowerExpr(ctx, expr.child2);
        }
        return n;
    }

    // expr.await
    if kind == ekAwait {
        n.kind = hAwait;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        return n;
    }

    // Index: arr[idx]
    if kind == ekIndex {
        n.kind = hIndexPtr;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        n.child2 = Lcx_LowerExpr(ctx, expr.child2);
        return n;
    }

    // Assign: target = value
    if kind == ekAssign {
        n.kind = hAssign;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1); // target
        n.child2 = Lcx_LowerExpr(ctx, expr.child2); // value
        return n;
    }

    // Cast
    if kind == ekCast {
        n.kind = hCast;
        n.child1 = Lcx_LowerExpr(ctx, expr.child1);
        if expr.refType != null as *TypeExpr {
            let resolvedKind: int = Lcx_ResolveTypeKind(expr.refType);
            n.typeKind = resolvedKind;
            // For pointer types, construct "PointeeType*"
            if expr.refType.kind == tekPointer && expr.refType.pointerPointee != null as *TypeExpr {
                n.typeName = String_Concat(expr.refType.pointerPointee.typeName, "*");
            } else if !String_Eq(expr.refType.typeName, "") {
                n.typeName = expr.refType.typeName;
            }
        }
        return n;
    }

    // Struct init: TypeName { field: value, ... }
    if kind == ekStructInit {
        n.kind = hStructInit;
        n.strValue = expr.structName;
        // Lower each field (fields are chained via child3 on synthetic ekField exprs)
        var field: *Expr = expr.child1;
        var firstField: *HirNode = null as *HirNode;
        var lastField: *HirNode = null as *HirNode;
        while field != null as *Expr {
            let fNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
            fNode.kind = hBlock; // placeholder, field is identified by name+value
            fNode.line = expr.line;
            fNode.column = expr.column;
            fNode.strValue = field.strValue;  // field name
            fNode.child1 = Lcx_LowerExpr(ctx, field.child1); // field value
            if firstField == null as *HirNode {
                firstField = fNode;
                lastField = fNode;
            } else {
                lastField.child3 = fNode;
                lastField = fNode;
            }
            field = field.child3;
        }
        n.child1 = firstField;
        return n;
    }

    return n;
}

// ---------------------------------------------------------------------------
// Statement lowering
// ---------------------------------------------------------------------------

func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
    if stmt == null as *Stmt { return null as *HirNode; }

    let line: uint32 = stmt.line;
    let col: uint32 = stmt.column;
    let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
    n.kind = hBlock;
    n.line = line;
    n.column = col;

    let kind: int = stmt.kind;

    // Let/var → alloca + store
    if kind == skLet {
        let init: *HirNode = Lcx_LowerExpr(ctx, stmt.child1);
        // alloca for the variable
        let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
        alloca.kind = hAlloca;
        alloca.line = line;
        alloca.column = col;
        alloca.strValue = stmt.strValue;
        // Set type from the declared type expression
        alloca.typeName = "";
        if stmt.refStmtType != null as *TypeExpr {
            alloca.intValue = stmt.refStmtType.kind;
            alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType);
            // For pointer types, construct "PointeeType*"
            if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
                alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
            } else if !String_Eq(stmt.refStmtType.typeName, "") {
                alloca.typeName = stmt.refStmtType.typeName;
            }
        }
        // Add to scope for field offset lookups
        var sym: Symbol;
        sym.kind = skVar;
        sym.name = stmt.strValue;
        sym.typeKind = alloca.typeKind;
        sym.typeName = alloca.typeName;
        sym.isMutable = false;
        sym.isPublic = false;
        sym.decl = null as *Decl;
        discard Scope_Define(ctx.scope, sym);
        // store the init value
        n.kind = hStore;
        n.child1 = alloca;
        n.child2 = init;
        return n;
    }

    // Return
    if kind == skReturn {
        n.kind = hReturn;
        if stmt.child1 != null as *Expr {
            n.child1 = Lcx_LowerExpr(ctx, stmt.child1);
        }
        return n;
    }

    // Expression statement
    if kind == skExpr && stmt.child1 != null as *Expr {
        return Lcx_LowerExpr(ctx, stmt.child1);
    }

    // If
    if kind == skIf {
        n.kind = hIf;
        n.child1 = Lcx_LowerExpr(ctx, stmt.child1); // condition
        if stmt.refStmtBlock != null as *Block {
            n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
        }
        if stmt.refStmtElse != null as *Block {
            // Store else block in extraData (child3 is reserved for block chaining)
            n.extraData = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1) as *void;
        }
        return n;
    }

    // While
    if kind == skWhile {
        n.kind = hWhile;
        n.child1 = Lcx_LowerExpr(ctx, stmt.child1);
        if stmt.refStmtBlock != null as *Block {
            n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
        }
        return n;
    }

    // Loop
    if kind == skLoop {
        n.kind = hLoop;
        if stmt.refStmtBlock != null as *Block {
            n.child1 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
        }
        return n;
    }

    return n;
}

// ---------------------------------------------------------------------------
// Block lowering
// ---------------------------------------------------------------------------

func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
    if block == null as *Block { return null as *HirNode; }
    if block.stmtCount == 0 { return null as *HirNode; }

    // Build a linked list of HirNodes via child3:
    //   node1 (stmt1) → child3 → node2 (stmt2) → child3 → node3 (stmt3) → null
    // child3 is safe for chaining because:
    //   hStore: child1=alloca, child2=value, child3 unused
    //   hReturn: child1=value, child2/child3 unused
    //   hCall: child1=arg1, child2=arg2, child3 unused
    var firstNode: *HirNode = null as *HirNode;
    var prevNode: *HirNode = null as *HirNode;
    var stmt: *Stmt = block.firstStmt;
    while stmt != null as *Stmt {
        let lowered: *HirNode = Lcx_LowerStmt(ctx, stmt);
        if lowered != null as *HirNode {
            if firstNode == null as *HirNode {
                firstNode = lowered;
                prevNode = lowered;
            } else {
                prevNode.child3 = lowered;
                prevNode = lowered;
            }
        }
        stmt = stmt.nextStmt;
    }

    // Wrap in an hBlock node with child1 = first statement in chain
    let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
    n.kind = hBlock;
    n.line = block.line;
    n.column = block.column;
    n.boolValue = true;
    n.child1 = firstNode;
    return n;
}

// ---------------------------------------------------------------------------
// Param → HirParam conversion
// ---------------------------------------------------------------------------

func Lcx_LowerParam(out: *HirParam, p: *Param) {
    out.name = p.name;
    if p.refParamType != null as *TypeExpr {
        out.typeKind = Lcx_ResolveTypeKind(p.refParamType);
        // Use typeName directly if set (parser may have constructed "String*")
        if !String_Eq(p.refParamType.typeName, "") {
            out.typeName = p.refParamType.typeName;
        } else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
            out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
        } else {
            out.typeName = "";
        }
    } else {
        out.typeKind = 0;
        out.typeName = "";
    }
}

// ---------------------------------------------------------------------------
// Function lowering
// ---------------------------------------------------------------------------

func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
    let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc;
    f.name = decl.strValue;
    f.isPublic = decl.isPublic;
    f.paramCount = decl.paramCount;
    Lcx_LowerParam(&f.param0, &decl.param0);
    Lcx_LowerParam(&f.param1, &decl.param1);
    Lcx_LowerParam(&f.param2, &decl.param2);
    Lcx_LowerParam(&f.param3, &decl.param3);
    Lcx_LowerParam(&f.param4, &decl.param4);
    Lcx_LowerParam(&f.param5, &decl.param5);
    Lcx_LowerParam(&f.param6, &decl.param6);
    Lcx_LowerParam(&f.param7, &decl.param7);
    Lcx_LowerParam(&f.param8, &decl.param8);

    if decl.retType != null as *TypeExpr {
        f.retTypeName = decl.retType.typeName;
        f.retTypeKind = Lcx_ResolveTypeKind(decl.retType);
    } else {
        f.retTypeName = "";
        f.retTypeKind = 0;
    }

    // Add parameters to hir_lower scope for field offset lookups
    var pi: int = 0;
    while pi < decl.paramCount {
        var p: *Param = null as *Param;
        if pi == 0 { p = &decl.param0; }
        else if pi == 1 { p = &decl.param1; }
        else if pi == 2 { p = &decl.param2; }
        else if pi == 3 { p = &decl.param3; }
        else if pi == 4 { p = &decl.param4; }
        else if pi == 5 { p = &decl.param5; }
        else if pi == 6 { p = &decl.param6; }
        else if pi == 7 { p = &decl.param7; }
        else if pi == 8 { p = &decl.param8; }
        if p != null as *Param && p.refParamType != null as *TypeExpr {
            var sym: Symbol;
            sym.kind = skVar;
            sym.name = p.name;
            sym.typeKind = Lcx_ResolveTypeKind(p.refParamType);
            sym.typeName = p.refParamType.typeName;
            sym.isMutable = false;
            sym.isPublic = false;
            sym.decl = null as *Decl;
            discard Scope_Define(ctx.scope, sym);
        }
        pi = pi + 1;
    }

    if decl.refBody != null as *Block {
        f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
    } else {
        f.body = null as *HirNode;
    }

    return f;
}

// ---------------------------------------------------------------------------
// Module lowering — main entry point
// ---------------------------------------------------------------------------

func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
    let ctx: *LowerCtx = bux_alloc(sizeof(LowerCtx)) as *LowerCtx;
    ctx.module = mod;
    ctx.scope = sema.scope;
    ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
    ctx.funcCount = 0;
    ctx.externFuncs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
    ctx.externCount = 0;
    ctx.varCounter = 0;

    let hm: *HirModule = bux_alloc(sizeof(HirModule)) as *HirModule;
    hm.funcCount = 0;
    hm.funcs = ctx.funcs;
    hm.structCount = 0;
    hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
    hm.enumCount = 0;
    hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
    hm.constCount = 0;
    hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;

    // First pass: count structs (to allocate field arrays later)
    // Second pass: actually collect them
    // For simplicity, do single pass with pre-allocated field arrays

    // Iterate declarations
    var decl: *Decl = mod.firstItem;
    while decl != null as *Decl {
        if decl.kind == dkFunc {
            let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
            ctx.funcs[ctx.funcCount] = *f;
            ctx.funcCount = ctx.funcCount + 1;
        }
        if decl.kind == dkImpl {
            let implTypeName: String = decl.strValue;
            var implDecl: *Decl = decl.childDecl1;
            while implDecl != null as *Decl {
                if implDecl.kind == dkFunc {
                    let mangled: String = String_Concat(implTypeName, "_");
                    implDecl.strValue = String_Concat(mangled, implDecl.strValue);
                    let f: *HirFunc = Lcx_LowerFunc(ctx, implDecl);
                    ctx.funcs[ctx.funcCount] = *f;
                    ctx.funcCount = ctx.funcCount + 1;
                }
                implDecl = implDecl.childDecl2;
            }
        }
        if decl.kind == dkExternFunc {
            let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
            ctx.externFuncs[ctx.externCount] = *f;
            ctx.externCount = ctx.externCount + 1;
        }
        if decl.kind == dkStruct {
            // Collect struct definition for C codegen
            let si: int = hm.structCount;
            hm.structs[si].name = decl.strValue;
            hm.structs[si].fieldCount = decl.fieldCount;
            hm.structs[si].fields = bux_alloc(decl.fieldCount as uint * sizeof(HirStructField)) as *HirStructField;
            var fi: int = 0;
            while fi < decl.fieldCount {
                var fname: String = "";
                var ftype: *TypeExpr = null as *TypeExpr;
                if fi == 0 { fname = decl.field0.name; ftype = decl.field0.refFieldType; }
                else if fi == 1 { fname = decl.field1.name; ftype = decl.field1.refFieldType; }
                else if fi == 2 { fname = decl.field2.name; ftype = decl.field2.refFieldType; }
                else if fi == 3 { fname = decl.field3.name; ftype = decl.field3.refFieldType; }
                else if fi == 4 { fname = decl.field4.name; ftype = decl.field4.refFieldType; }
                else if fi == 5 { fname = decl.field5.name; ftype = decl.field5.refFieldType; }
                else if fi == 6 { fname = decl.field6.name; ftype = decl.field6.refFieldType; }
                else if fi == 7 { fname = decl.field7.name; ftype = decl.field7.refFieldType; }
                // Skip empty field names
                if String_Eq(fname, "") {
                    fi = fi + 1;
                    continue;
                }
                hm.structs[si].fields[fi].name = fname;
                if ftype != null as *TypeExpr {
                    if ftype.kind == tekPointer && ftype.pointerPointee != null as *TypeExpr {
                        // Pointer type: emit "TypeName*"
                        if !String_Eq(ftype.pointerPointee.typeName, "") {
                            hm.structs[si].fields[fi].typeName = String_Concat(ftype.pointerPointee.typeName, "*");
                        }
                    } else if !String_Eq(ftype.typeName, "") {
                        hm.structs[si].fields[fi].typeName = ftype.typeName;
                    }
                }
                fi = fi + 1;
            }
            hm.structCount = hm.structCount + 1;
        }
        if decl.kind == dkConst && hm.constCount < 512 {
            let ci: int = hm.constCount;
            hm.consts[ci].name = decl.strValue;
            var val: int = 0;
            if decl.constValue != null as *Expr {
                if decl.constValue.kind == ekLiteral {
                    val = decl.constValue.intValue;
                }
            }
            hm.consts[ci].value = val;
            hm.constCount = hm.constCount + 1;
        }
        if decl.kind == dkEnum {
            let ei: int = hm.enumCount;
            hm.enums[ei].name = decl.strValue;
            hm.enumCount = hm.enumCount + 1;
            // Emit enum variants as constants: EnumName_VariantName = value
            var vi: int = 0;
            while vi < decl.variantCount && hm.constCount < 512 {
                var vname: String = "";
                if vi == 0 { vname = decl.variant0.name; }
                if vi == 1 { vname = decl.variant1.name; }
                if vi == 2 { vname = decl.variant2.name; }
                if vi == 3 { vname = decl.variant3.name; }
                if !String_Eq(vname, "") {
                    let ci: int = hm.constCount;
                    hm.consts[ci].name = String_Concat(String_Concat(decl.strValue, "_"), vname);
                    hm.consts[ci].value = vi;
                    hm.constCount = hm.constCount + 1;
                }
                vi = vi + 1;
            }
        }
        decl = decl.childDecl2;
    }


    hm.funcCount = ctx.funcCount;
    hm.funcs = ctx.funcs;
    hm.externCount = ctx.externCount;
    hm.externFuncs = ctx.externFuncs;


    return hm;
}

func HirLower_Free(ctx: *LowerCtx) {
    bux_free(ctx.funcs as *void);
    bux_free(ctx.externFuncs as *void);
    bux_free(ctx as *void);
}
}
