// c_backend.bux — C transpiler backend (ported from c_backend.nim)
// Generates C code from the HIR.
module CBackend {


// ---------------------------------------------------------------------------
// Type → C type name
// ---------------------------------------------------------------------------

func CBackend_TypeToC(kind: int) -> String {
    if kind == tyVoid { return "void"; }
    if kind == tyBool { return "bool"; }
    if kind == tyBool8 { return "bool"; }
    if kind == tyBool16 { return "bool"; }
    if kind == tyBool32 { return "bool"; }
    if kind == tyChar8 { return "char"; }
    if kind == tyChar16 { return "uint16"; }
    if kind == tyChar32 { return "uint32"; }
    if kind == tyStr { return "String"; }
    if kind == tyInt8 { return "int8"; }
    if kind == tyInt16 { return "int16"; }
    if kind == tyInt32 { return "int32"; }
    if kind == tyInt64 { return "int64"; }
    if kind == tyInt{ return "int"; }
    if kind == tyUInt8 { return "uint8"; }
    if kind == tyUInt16 { return "uint16"; }
    if kind == tyUInt32 { return "uint32"; }
    if kind == tyUInt64 { return "uint64"; }
    if kind == tyUInt { return "uint"; }
    if kind == tyFloat32 { return "float32"; }
    if kind == tyFloat64 { return "float64"; }
    if kind == tyPointer { return "void*"; }
    if kind == tyNamed { return "int"; }
    if kind == tyFunc { return "void (*)(void)"; }
    return "int";
}

// Emit a C parameter/variable declaration, embedding the name inside function-pointer syntax.
func CBE_CParamDecl(typeStr: String, name: String) -> String {
    if String_Contains(typeStr, "(*)") {
        let replacement: String = String_Concat("(*", String_Concat(name, ")"));
        return String_Replace(typeStr, "(*)", replacement);
    }
    return String_Concat(typeStr, String_Concat(" ", name));
}

func CBackend_OpToC(op: int) -> String {
    if op == tkPlus { return "+"; }
    if op == tkMinus { return "-"; }
    if op == tkStar { return "*"; }
    if op == tkSlash { return "/"; }
    if op == tkPercent { return "%"; }
    if op == tkEq { return "=="; }
    if op == tkNe { return "!="; }
    if op == tkLt { return "<"; }
    if op == tkLe { return "<="; }
    if op == tkGt { return ">"; }
    if op == tkGe { return ">="; }
    if op == tkAmpAmp { return "&&"; }
    if op == tkPipePipe { return "||"; }
    if op == tkBang { return "!"; }
    if op == tkAmp { return "&"; }
    if op == tkPipe { return "|"; }
    if op == tkCaret { return "^"; }
    if op == tkShl { return "<<"; }
    if op == tkShr { return ">>"; }
    if op == tkAssign { return "="; }
    return "?";
}

// ---------------------------------------------------------------------------
// StringBuilder-based C emitter
// ---------------------------------------------------------------------------

struct CEmitter {
    sb: StringBuilder,
    indent: int,
    mod: *HirModule,
    deferCount: int,
    defer0: *HirNode,
    defer1: *HirNode,
    defer2: *HirNode,
    defer3: *HirNode,
    defer4: *HirNode,
    defer5: *HirNode,
    defer6: *HirNode,
    defer7: *HirNode,
}

func CBE_PushDefer(cbe: *CEmitter, node: *HirNode) {
    if cbe.deferCount == 0 { cbe.defer0 = node; }
    if cbe.deferCount == 1 { cbe.defer1 = node; }
    if cbe.deferCount == 2 { cbe.defer2 = node; }
    if cbe.deferCount == 3 { cbe.defer3 = node; }
    if cbe.deferCount == 4 { cbe.defer4 = node; }
    if cbe.deferCount == 5 { cbe.defer5 = node; }
    if cbe.deferCount == 6 { cbe.defer6 = node; }
    if cbe.deferCount == 7 { cbe.defer7 = node; }
    cbe.deferCount = cbe.deferCount + 1;
}

func CBE_EmitDefers(cbe: *CEmitter) -> int {
    if cbe.deferCount == 0 { return 0; }
    var i: int = cbe.deferCount - 1;
    while i >= 0 {
        StringBuilder_Append(&cbe.sb, "\n");
        var sp: int = 0;
        while sp < cbe.indent {
            StringBuilder_Append(&cbe.sb, "    ");
            sp = sp + 1;
        }
        var dn: *HirNode = null as *HirNode;
        if i == 0 { dn = cbe.defer0; }
        if i == 1 { dn = cbe.defer1; }
        if i == 2 { dn = cbe.defer2; }
        if i == 3 { dn = cbe.defer3; }
        if i == 4 { dn = cbe.defer4; }
        if i == 5 { dn = cbe.defer5; }
        if i == 6 { dn = cbe.defer6; }
        if i == 7 { dn = cbe.defer7; }
        CBE_EmitExpr(cbe, dn);
        StringBuilder_Append(&cbe.sb, ";");
        i = i - 1;
    }
    cbe.deferCount = 0;
    return 1;
}

func CBE_Emit(cbe: *CEmitter, text: String) {
    var i: int = 0;
    while i < cbe.indent {
        StringBuilder_Append(&cbe.sb, "    ");
        i = i + 1;
    }
    StringBuilder_Append(&cbe.sb, text);
}

// ---------------------------------------------------------------------------
// Emit HIR node
// ---------------------------------------------------------------------------

func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
    if node == null as *HirNode { return; }
    let kind: int = node.kind;

    // Literal
    if kind == hLit {
        if node.intValue == tkNull {
            StringBuilder_Append(&cbe.sb, "0");
        } else {
            let s: String = node.strValue;
            let slen: int = String_Len(s) as int;
            var start: int = 0;
            var end: int = slen;
            var isQuoted: bool = false;
            var isBacktick: bool = false;
            // If the literal is wrapped in quotes (from lexer), preserve outer quotes
            // and only escape the inner content.
            if slen >= 2 && s[0] == 34 as char8 && s[slen - 1] == 34 as char8 {
                isQuoted = true;
                start = 1;
                end = slen - 1;
            }
            // Backtick raw string: strip backticks, escape inner content for C
            if slen >= 2 && s[0] == 96 as char8 && s[slen - 1] == 96 as char8 {
                isBacktick = true;
                start = 1;
                end = slen - 1;
            }
            if isQuoted || isBacktick {
                StringBuilder_Append(&cbe.sb, "\"");
            }
            var i: int = start;
            while i < end {
                let c: int = s[i] as int;
                if c == 34 {
                    StringBuilder_Append(&cbe.sb, "\\\"");
                } else if c == 92 {
                    StringBuilder_Append(&cbe.sb, "\\\\");
                } else if c == 10 {
                    StringBuilder_Append(&cbe.sb, "\\n");
                } else if c == 9 {
                    StringBuilder_Append(&cbe.sb, "\\t");
                } else if c == 13 {
                    StringBuilder_Append(&cbe.sb, "\\r");
                } else {
                    StringBuilder_AppendChar(&cbe.sb, c as char8);
                }
                i = i + 1;
            }
            if isQuoted || isBacktick {
                StringBuilder_Append(&cbe.sb, "\"");
            }
        }
        return;
    }

    // Variable
    if kind == hVar {
        if node.intValue != 0 {
            StringBuilder_Append(&cbe.sb, "/* hVar tk=");
            StringBuilder_AppendInt(&cbe.sb, node.intValue);
            StringBuilder_Append(&cbe.sb, " */");
        }
        StringBuilder_Append(&cbe.sb, node.strValue);
        return;
    }

    // Binary
    if kind == hBinary {
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, " ");
        StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
        StringBuilder_Append(&cbe.sb, " ");
        CBE_EmitExpr(cbe, node.child2);
        return;
    }

    // Unary
    if kind == hUnary {
        StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
        CBE_EmitExpr(cbe, node.child1);
        return;
    }

    // Call
    if kind == hCall {
        StringBuilder_Append(&cbe.sb, node.strValue);
        StringBuilder_Append(&cbe.sb, "(");
        var needsComma: bool = false;
        if node.child1 != null as *HirNode {
            CBE_EmitExpr(cbe, node.child1);
            needsComma = true;
        }
        if node.child2 != null as *HirNode {
            if needsComma {
                StringBuilder_Append(&cbe.sb, ", ");
            }
            CBE_EmitExpr(cbe, node.child2);
            needsComma = true;
        }
        // Emit extra args from linked list
        var ai: int = 0;
        var curExtra: *HirArgList = node.extraData as *HirArgList;
        while ai < node.extraCount {
            if needsComma {
                StringBuilder_Append(&cbe.sb, ", ");
            }
            CBE_EmitExpr(cbe, curExtra.node);
            needsComma = true;
            curExtra = curExtra.next;
            ai = ai + 1;
        }
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // Indirect call through function pointer
    if kind == hCallIndirect {
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, "(");
        var needsComma: bool = false;
        if node.child2 != null as *HirNode {
            CBE_EmitExpr(cbe, node.child2);
            needsComma = true;
        }
        if node.child3 != null as *HirNode {
            if needsComma {
                StringBuilder_Append(&cbe.sb, ", ");
            }
            CBE_EmitExpr(cbe, node.child3);
            needsComma = true;
        }
        // Emit extra args from linked list
        var ai: int = 0;
        var curExtra: *HirArgList = node.extraData as *HirArgList;
        while ai < node.extraCount {
            if needsComma {
                StringBuilder_Append(&cbe.sb, ", ");
            }
            CBE_EmitExpr(cbe, curExtra.node);
            needsComma = true;
            curExtra = curExtra.next;
            ai = ai + 1;
        }
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // spawn Callee(args)
    if kind == hSpawn {
        StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
        StringBuilder_Append(&cbe.sb, node.strValue);
        if node.child1 != null as *HirNode {
            StringBuilder_Append(&cbe.sb, ", (void*)");
            CBE_EmitExpr(cbe, node.child1);
        }
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // await
    if kind == hAwait {
        StringBuilder_Append(&cbe.sb, "bux_async_await(");
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // Defer
    if kind == hDefer {
        CBE_PushDefer(cbe, node.child1);
        return;
    }

    // Return
    if kind == hReturn {
        let hadDefers: int = CBE_EmitDefers(cbe);
        if hadDefers != 0 {
            StringBuilder_Append(&cbe.sb, "\n");
            var sp: int = 0;
            while sp < cbe.indent {
                StringBuilder_Append(&cbe.sb, "    ");
                sp = sp + 1;
            }
        }
        StringBuilder_Append(&cbe.sb, "return");
        if node.child1 != null as *HirNode {
            StringBuilder_Append(&cbe.sb, " ");
            CBE_EmitExpr(cbe, node.child1);
        }
        return;
    }

    // Alloca
    if kind == hAlloca {
        var ct: String = "int";
        if !String_Eq(node.typeName, "") {
            ct = node.typeName;
        }
        StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.strValue));
        return;
    }

    // Store: combine alloca + value into single declaration
    if kind == hStore {
        if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
            // Declaration with initializer: Type x = value;
            var ct: String = CBackend_TypeToC(node.child1.intValue);
            if !String_Eq(node.child1.typeName, "") {
                ct = node.child1.typeName;
            }
            StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.child1.strValue));
            if node.child2 != null as *HirNode {
                StringBuilder_Append(&cbe.sb, " = ");
                CBE_EmitExpr(cbe, node.child2);
            }
            return;
        }
        // Plain assignment
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, " = ");
        CBE_EmitExpr(cbe, node.child2);
        return;
    }

    // If
    if kind == hIf {

        StringBuilder_Append(&cbe.sb, "if (");
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ") {\n");
        if node.child2 != null as *HirNode {
            cbe.indent = cbe.indent + 1;
            CBE_EmitExpr(cbe, node.child2);
            cbe.indent = cbe.indent - 1;
        }
        var sp: int = 0;
        while sp < cbe.indent {
            StringBuilder_Append(&cbe.sb, "    ");
            sp = sp + 1;
        }
        StringBuilder_Append(&cbe.sb, "}");
        let elseBlock: *HirNode = node.extraData as *HirNode;
        if elseBlock != null as *HirNode {
            StringBuilder_Append(&cbe.sb, " else {\n");
            cbe.indent = cbe.indent + 1;
            CBE_EmitExpr(cbe, elseBlock);
            cbe.indent = cbe.indent - 1;
            sp = 0;
            while sp < cbe.indent {
                StringBuilder_Append(&cbe.sb, "    ");
                sp = sp + 1;
            }
            StringBuilder_Append(&cbe.sb, "}\n");
        } else {
            StringBuilder_Append(&cbe.sb, "\n");
        }
        return;
    }

    // While
    if kind == hWhile {
        StringBuilder_Append(&cbe.sb, "while (");
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ") {\n");
        if node.child2 != null as *HirNode {
            cbe.indent = cbe.indent + 1;
            CBE_EmitExpr(cbe, node.child2);
            cbe.indent = cbe.indent - 1;
        }
        var sp: int = 0;
        while sp < cbe.indent {
            StringBuilder_Append(&cbe.sb, "    ");
            sp = sp + 1;
        }
        StringBuilder_Append(&cbe.sb, "}");
        return;
    }

    // Loop (infinite)
    if kind == hLoop {
        StringBuilder_Append(&cbe.sb, "while (1) {\n");
        if node.child1 != null as *HirNode {
            cbe.indent = cbe.indent + 1;
            CBE_EmitExpr(cbe, node.child1);
            cbe.indent = cbe.indent - 1;
        }
        var sp: int = 0;
        while sp < cbe.indent {
            StringBuilder_Append(&cbe.sb, "    ");
            sp = sp + 1;
        }
        StringBuilder_Append(&cbe.sb, "}");
        return;
    }

    // Break / Continue
    if kind == hBreak {
        StringBuilder_Append(&cbe.sb, "break");
        return;
    }
    if kind == hContinue {
        StringBuilder_Append(&cbe.sb, "continue");
        return;
    }

    // Field access on value (direct dot): obj.field
    if kind == hFieldAccess {
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ".");
        StringBuilder_Append(&cbe.sb, node.strValue);
        return;
    }

    // Field access: obj.field — use -> if base is pointer, else .
    if kind == hFieldPtr {
        CBE_EmitExpr(cbe, node.child1);
        var isPtr: bool = false;
        if node.child1 != null as *HirNode {
            let childType: String = CBE_GetExprTypeName(cbe.mod, node.child1);
            if String_EndsWith(childType, "*") {
                isPtr = true;
            }
        }
        if isPtr {
            StringBuilder_Append(&cbe.sb, "->");
        } else {
            StringBuilder_Append(&cbe.sb, ".");
        }
        StringBuilder_Append(&cbe.sb, node.strValue);
        return;
    }

    // Sizeof: sizeof(Type) — emit as sizeof(Type)
    if kind == hSizeOf {
        StringBuilder_Append(&cbe.sb, "sizeof(");
        if !String_Eq(node.typeName, "") {
            StringBuilder_Append(&cbe.sb, node.typeName);
        } else {
            StringBuilder_Append(&cbe.sb, "int");
        }
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // Index: arr[idx] — emit as arr[idx]
    if kind == hIndexPtr {
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, "[");
        CBE_EmitExpr(cbe, node.child2);
        StringBuilder_Append(&cbe.sb, "]");
        return;
    }

    // Load: *ptr — emit as *ptr (dereference)
    // Optimize common patterns to avoid & / * temporaries
    if kind == hLoad {
        if node.child1 != null as *HirNode {
            let ptrNode: *HirNode = node.child1;
            let ptrKind: int = ptrNode.kind;
            // field access: load(field_ptr(base, field)) → base.field
            if ptrKind == hFieldPtr {
                CBE_EmitExpr(cbe, ptrNode.child1);
                var isPtr: bool = false;
                if ptrNode.child1 != null as *HirNode {
                    let childType: String = CBE_GetExprTypeName(cbe.mod, ptrNode.child1);
                    if String_EndsWith(childType, "*") {
                        isPtr = true;
                    }
                }
                if isPtr {
                    StringBuilder_Append(&cbe.sb, "->");
                } else {
                    StringBuilder_Append(&cbe.sb, ".");
                }
                StringBuilder_Append(&cbe.sb, ptrNode.strValue);
                return;
            }
            // arrow field: load(arrow_field(base, field)) → base->field
            if ptrKind == hArrowField {
                CBE_EmitExpr(cbe, ptrNode.child1);
                StringBuilder_Append(&cbe.sb, "->");
                StringBuilder_Append(&cbe.sb, ptrNode.strValue);
                return;
            }
            // arrow field: load(arrow_field(base, field)) → base->field
            if ptrKind == hArrowField {
                CBE_EmitExpr(cbe, ptrNode.child1);
                StringBuilder_Append(&cbe.sb, "->");
                StringBuilder_Append(&cbe.sb, ptrNode.strValue);
                return;
            }
            // index: load(index_ptr(base, idx)) → base[idx]
            if ptrKind == hIndexPtr {
                CBE_EmitExpr(cbe, ptrNode.child1);
                StringBuilder_Append(&cbe.sb, "[");
                CBE_EmitExpr(cbe, ptrNode.child2);
                StringBuilder_Append(&cbe.sb, "]");
                return;
            }
        }
        StringBuilder_Append(&cbe.sb, "(*");
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // Assign: target = value
    if kind == hAssign {
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, " = ");
        CBE_EmitExpr(cbe, node.child2);
        return;
    }

    // Cast
    if kind == hCast {
        StringBuilder_Append(&cbe.sb, "((");
        if !String_Eq(node.typeName, "") {
            StringBuilder_Append(&cbe.sb, node.typeName);
        } else {
            StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.typeKind));
        }
        StringBuilder_Append(&cbe.sb, ")");
        CBE_EmitExpr(cbe, node.child1);
        StringBuilder_Append(&cbe.sb, ")");
        return;
    }

    // Struct init: ((TypeName){.field = value, ...})
    if kind == hStructInit {
        StringBuilder_Append(&cbe.sb, "((");
        StringBuilder_Append(&cbe.sb, node.strValue);
        StringBuilder_Append(&cbe.sb, "){");
        // Emit fields (chained via child3)
        var field: *HirNode = node.child1;
        var first: bool = true;
        while field != null as *HirNode {
            if !first {
                StringBuilder_Append(&cbe.sb, ", ");
            }
            StringBuilder_Append(&cbe.sb, ".");
            StringBuilder_Append(&cbe.sb, field.strValue);
            StringBuilder_Append(&cbe.sb, " = ");
            CBE_EmitExpr(cbe, field.child1);
            first = false;
            field = field.child3;
        }
        StringBuilder_Append(&cbe.sb, "})");
        return;
    }

    // Block — emit each statement via child3 linked list
    if kind == hBlock {
        var child: *HirNode = node.child1;
        while child != null as *HirNode {
            if child.kind == hDefer {
                CBE_EmitExpr(cbe, child);
                child = child.child3;
                continue;
            }
            // Indent
            var sp: int = 0;
            while sp < cbe.indent {
                StringBuilder_Append(&cbe.sb, "    ");
                sp = sp + 1;
            }
            CBE_EmitExpr(cbe, child);
            // Add semicolon for statements that need it
            if child.kind != hBlock && child.kind != hIf && child.kind != hWhile && child.kind != hLoop {
                StringBuilder_Append(&cbe.sb, ";");
            }
            StringBuilder_Append(&cbe.sb, "\n");
            child = child.child3;
        }
        return;
    }
}

// ---------------------------------------------------------------------------
// Emit function declaration
// ---------------------------------------------------------------------------

func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
    // Return type
    if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
        StringBuilder_Append(&cbe.sb, "void ");
    } else {
        StringBuilder_Append(&cbe.sb, f.retTypeName);
        StringBuilder_Append(&cbe.sb, " ");
    }

    StringBuilder_Append(&cbe.sb, f.name);
    StringBuilder_Append(&cbe.sb, "(");

    // Parameters
    var i: int = 0;
    while i < f.paramCount {
        if i > 0 {
            StringBuilder_Append(&cbe.sb, ", ");
        }
        // Use param type info
        var pname: String = "";
        var ptype: String = "";
        if i == 0 { pname = f.param0.name; ptype = f.param0.typeName; }
        if i == 1 { pname = f.param1.name; ptype = f.param1.typeName; }
        if i == 2 { pname = f.param2.name; ptype = f.param2.typeName; }
        if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
        if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; }
        if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; }
        if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; }
        if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; }
        if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; }
        // Emit type + name
        if String_Eq(ptype, "") {
            ptype = "int";
        }
        StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ptype, pname));
        i = i + 1;
    }

    StringBuilder_Append(&cbe.sb, ")");
}

// ---------------------------------------------------------------------------
// Helpers for detecting generic declarations (not yet monomorphized)
// ---------------------------------------------------------------------------

func CBE_IsGenericTypeName(name: String) -> bool {
    if String_Eq(name, "T") || String_Eq(name, "K") || String_Eq(name, "V") { return true; }
    if String_Eq(name, "T*") || String_Eq(name, "K*") || String_Eq(name, "V*") { return true; }
    // Any type name containing '<' is a generic instantiation or parameter
    if String_Contains(name, "<") { return true; }
    // Generic container structs and their pointer variants
    if String_Eq(name, "Array") || String_Eq(name, "Array*") { return true; }
    if String_Eq(name, "Channel") || String_Eq(name, "Channel*") { return true; }
    if String_Eq(name, "Iter") || String_Eq(name, "Iter*") { return true; }
    if String_Eq(name, "Set") || String_Eq(name, "Set*") { return true; }
    if String_Eq(name, "SetEntry") || String_Eq(name, "SetEntry*") { return true; }
    if String_Eq(name, "Map") || String_Eq(name, "Map*") { return true; }
    if String_Eq(name, "MapEntry") || String_Eq(name, "MapEntry*") { return true; }
    if String_Eq(name, "StringMap") || String_Eq(name, "StringMap*") { return true; }
    if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; }
    return false;
}

func CBE_StructHasGeneric(st: *HirStruct) -> bool {
    var fi: int = 0;
    while fi < st.fieldCount {
        if CBE_IsGenericTypeName(st.fields[fi].typeName) { return true; }
        fi = fi + 1;
    }
    return false;
}

func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
    var pi: int = 0;
    while pi < f.paramCount {
        var ptype: String = "";
        if pi == 0 { ptype = f.param0.typeName; }
        if pi == 1 { ptype = f.param1.typeName; }
        if pi == 2 { ptype = f.param2.typeName; }
        if pi == 3 { ptype = f.param3.typeName; }
        if pi == 4 { ptype = f.param4.typeName; }
        if pi == 5 { ptype = f.param5.typeName; }
        if pi == 6 { ptype = f.param6.typeName; }
        if pi == 7 { ptype = f.param7.typeName; }
        if pi == 8 { ptype = f.param8.typeName; }
        if CBE_IsGenericTypeName(ptype) { return true; }
        pi = pi + 1;
    }
    if CBE_IsGenericTypeName(f.retTypeName) { return true; }
    return false;
}

func CBE_IsArrayTypeName(name: String) -> bool {
    if String_Eq(name, "") { return false; }
    if String_StartsWith(name, "Array") { return true; }
    return false;
}

func CBE_IsPrimitiveTypeName(name: String) -> bool {
    if String_Eq(name, "int") || String_Eq(name, "") { return true; }
    if String_Eq(name, "String") { return true; }
    if String_Eq(name, "bool") { return true; }
    if String_Eq(name, "uint32") { return true; }
    if String_Eq(name, "uint64") { return true; }
    if String_Eq(name, "int64") { return true; }
    if String_Eq(name, "float32") { return true; }
    if String_Eq(name, "float64") { return true; }
    if String_Eq(name, "uint8") { return true; }
    if String_Eq(name, "uint16") { return true; }
    if String_Eq(name, "int8") { return true; }
    if String_Eq(name, "int16") { return true; }
    if String_Eq(name, "uint") { return true; }
    if String_Eq(name, "char8") { return true; }
    if String_Eq(name, "void") { return true; }
    if String_Eq(name, "size_t") { return true; }
    return false;
}

func CBE_StructHasValueStructField(st: *HirStruct) -> bool {
    var fi: int = 0;
    while fi < st.fieldCount {
        let ft: String = st.fields[fi].typeName;
        if !CBE_IsPrimitiveTypeName(ft) && !String_EndsWith(ft, "*") {
            return true;
        }
        fi = fi + 1;
    }
    return false;
}

func CBE_EmitStructDef(cbe: *CEmitter, st: *HirStruct) {
    if String_Eq(st.name, "") { return; }
    StringBuilder_Append(&cbe.sb, "struct ");
    StringBuilder_Append(&cbe.sb, st.name);
    StringBuilder_Append(&cbe.sb, " {\n");
    var fi: int = 0;
    while fi < st.fieldCount {
        StringBuilder_Append(&cbe.sb, "    ");
        var ft: String = st.fields[fi].typeName;
        if String_Eq(ft, "int") || String_Eq(ft, "") {
            StringBuilder_Append(&cbe.sb, "int");
        } else if String_Eq(ft, "String") {
            StringBuilder_Append(&cbe.sb, "String");
        } else if String_Eq(ft, "bool") {
            StringBuilder_Append(&cbe.sb, "bool");
        } else if String_Eq(ft, "uint32") {
            StringBuilder_Append(&cbe.sb, "uint32");
        } else {
            StringBuilder_Append(&cbe.sb, ft);
        }
        StringBuilder_Append(&cbe.sb, " ");
        StringBuilder_Append(&cbe.sb, st.fields[fi].name);
        StringBuilder_Append(&cbe.sb, ";\n");
        fi = fi + 1;
    }
    StringBuilder_Append(&cbe.sb, "};\n\n");
}

func CBE_LookupFieldType(mod: *HirModule, structName: String, fieldName: String) -> String {
    var si: int = 0;
    while si < mod.structCount {
        if String_Eq(mod.structs[si].name, structName) {
            var fi: int = 0;
            while fi < mod.structs[si].fieldCount {
                if String_Eq(mod.structs[si].fields[fi].name, fieldName) {
                    let ft: String = mod.structs[si].fields[fi].typeName;
                    if ft != null as String { return ft; }
                    return "";
                }
                fi = fi + 1;
            }
        }
        si = si + 1;
    }
    return "";
}

func CBE_GetExprTypeName(mod: *HirModule, node: *HirNode) -> String {
    if node == null as *HirNode { return ""; }
    if node.kind == hVar {
        if node.typeName != null as String { return node.typeName; }
        return "";
    }
    if node.kind == hFieldPtr {
        let baseType: String = CBE_GetExprTypeName(mod, node.child1);
        var structName: String = baseType;
        if String_EndsWith(baseType, "*") {
            let len: uint = String_Len(baseType);
            if len > 1 {
                structName = String_Slice(baseType, 0, len - 1);
            }
        }
        if !String_Eq(structName, "") {
            return CBE_LookupFieldType(mod, structName, node.strValue);
        }
    }
    if node.typeName != null as String { return node.typeName; }
    return "";
}

// ---------------------------------------------------------------------------
// Generate complete C module
// ---------------------------------------------------------------------------

func CBackend_Generate(mod: *HirModule) -> String {
    let cbe: *CEmitter = bux_alloc(sizeof(CEmitter)) as *CEmitter;
    cbe.sb = StringBuilder_NewCap(8192);
    cbe.indent = 0;
    cbe.mod = mod;

    // Header
    StringBuilder_Append(&cbe.sb, "// Generated by Bux C Backend v2\n");
    StringBuilder_Append(&cbe.sb, "#include <stdint.h>\n");
    StringBuilder_Append(&cbe.sb, "#include <stdbool.h>\n");
    StringBuilder_Append(&cbe.sb, "#include <string.h>\n");
    StringBuilder_Append(&cbe.sb, "#include <stdio.h>\n");
    StringBuilder_Append(&cbe.sb, "#include <stdlib.h>\n\n");
    // Type aliases
    StringBuilder_Append(&cbe.sb, "typedef const char* String;\n");
    StringBuilder_Append(&cbe.sb, "typedef unsigned char uint8;\n");
    StringBuilder_Append(&cbe.sb, "typedef unsigned short uint16;\n");
    StringBuilder_Append(&cbe.sb, "typedef unsigned int uint32;\n");
    StringBuilder_Append(&cbe.sb, "typedef unsigned long long uint64;\n");
    StringBuilder_Append(&cbe.sb, "typedef signed char int8;\n");
    StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
    StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
    StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
    StringBuilder_Append(&cbe.sb, "typedef double float64;\n");
    StringBuilder_Append(&cbe.sb, "typedef char char8;\n\n");
    // Runtime declarations
    StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
    StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");

    // Forward declare all struct types (skip empty names)
    var si: int = 0;
    while si < mod.structCount {
        if !String_Eq(mod.structs[si].name, "") {
            StringBuilder_Append(&cbe.sb, "typedef struct ");
            StringBuilder_Append(&cbe.sb, mod.structs[si].name);
            StringBuilder_Append(&cbe.sb, " ");
            StringBuilder_Append(&cbe.sb, mod.structs[si].name);
            StringBuilder_Append(&cbe.sb, ";\n");
        }
        si = si + 1;
    }
    StringBuilder_Append(&cbe.sb, "\n");

    // Enum definitions
    var ei: int = 0;
    while ei < mod.enumCount {
        let en: *HirEnum = &mod.enums[ei];
        var hasData: bool = false;
        var vi: int = 0;
        while vi < en.variantCount {
            if en.variants[vi].fieldCount > 0 {
                hasData = true;
            }
            vi = vi + 1;
        }
        // Emit tag enum for all enums (simple and algebraic)
        StringBuilder_Append(&cbe.sb, "typedef enum {\n");
        vi = 0;
        while vi < en.variantCount {
            StringBuilder_Append(&cbe.sb, "    ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, "_");
            StringBuilder_Append(&cbe.sb, en.variants[vi].name);
            if vi < en.variantCount - 1 {
                StringBuilder_Append(&cbe.sb, ",");
            }
            StringBuilder_Append(&cbe.sb, "\n");
            vi = vi + 1;
        }
        StringBuilder_Append(&cbe.sb, "} ");
        StringBuilder_Append(&cbe.sb, en.name);
        StringBuilder_Append(&cbe.sb, "_Tag;\n\n");

        if !hasData {
            // Simple enum: struct wrapper with only tag
            StringBuilder_Append(&cbe.sb, "typedef struct {\n");
            StringBuilder_Append(&cbe.sb, "    ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, "_Tag tag;\n");
            StringBuilder_Append(&cbe.sb, "} ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, ";\n\n");
        } else {
            // Algebraic enum: data union + struct (tag enum already emitted above)
            // 1. Data union
            StringBuilder_Append(&cbe.sb, "typedef union {\n");
            vi = 0;
            while vi < en.variantCount {
                let ev: *HirEnumVariant = &en.variants[vi];
                if ev.fieldCount > 0 {
                    if ev.fieldCount == 1 {
                        StringBuilder_Append(&cbe.sb, "    ");
                        StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
                        StringBuilder_Append(&cbe.sb, " ");
                        StringBuilder_Append(&cbe.sb, ev.name);
                        StringBuilder_Append(&cbe.sb, "_0;\n");
                    } else {
                        StringBuilder_Append(&cbe.sb, "    struct {\n");
                        StringBuilder_Append(&cbe.sb, "        ");
                        StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
                        StringBuilder_Append(&cbe.sb, " ");
                        StringBuilder_Append(&cbe.sb, ev.fieldName0);
                        StringBuilder_Append(&cbe.sb, ";\n");
                        if ev.fieldCount > 1 {
                            StringBuilder_Append(&cbe.sb, "        ");
                            StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType1));
                            StringBuilder_Append(&cbe.sb, " ");
                            StringBuilder_Append(&cbe.sb, ev.fieldName1);
                            StringBuilder_Append(&cbe.sb, ";\n");
                        }
                        StringBuilder_Append(&cbe.sb, "    } ");
                        StringBuilder_Append(&cbe.sb, ev.name);
                        StringBuilder_Append(&cbe.sb, ";\n");
                    }
                }
                vi = vi + 1;
            }
            StringBuilder_Append(&cbe.sb, "} ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, "_Data;\n\n");
            // 3. Main struct
            StringBuilder_Append(&cbe.sb, "typedef struct {\n");
            StringBuilder_Append(&cbe.sb, "    ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, "_Tag tag;\n");
            StringBuilder_Append(&cbe.sb, "    ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, "_Data data;\n");
            StringBuilder_Append(&cbe.sb, "} ");
            StringBuilder_Append(&cbe.sb, en.name);
            StringBuilder_Append(&cbe.sb, ";\n\n");
        }
        ei = ei + 1;
    }

    // Constant definitions
    var ci: int = 0;
    while ci < mod.constCount {
        StringBuilder_Append(&cbe.sb, "#define ");
        StringBuilder_Append(&cbe.sb, mod.consts[ci].name);
        StringBuilder_Append(&cbe.sb, " ");
        StringBuilder_Append(&cbe.sb, String_FromInt(mod.consts[ci].value as int64));
        StringBuilder_Append(&cbe.sb, "\n");
        ci = ci + 1;
    }
    if mod.constCount > 0 {
        StringBuilder_Append(&cbe.sb, "\n");
    }

    // Struct definitions (skip generics)
    // Pass 1: emit structs with no value-typed struct fields (leaf structs)
    si = 0;
    while si < mod.structCount {
        if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
            si = si + 1;
            continue;
        }
        if CBE_StructHasValueStructField(&mod.structs[si]) {
            si = si + 1;
            continue;
        }
        CBE_EmitStructDef(cbe, &mod.structs[si]);
        si = si + 1;
    }
    // Pass 2: emit structs that contain value-typed struct fields
    si = 0;
    while si < mod.structCount {
        if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
            si = si + 1;
            continue;
        }
        if !CBE_StructHasValueStructField(&mod.structs[si]) {
            si = si + 1;
            continue;
        }
        CBE_EmitStructDef(cbe, &mod.structs[si]);
        si = si + 1;
    }

    // Forward declarations for all functions (skip generics)
    var i: int = 0;
    while i < mod.funcCount {
        if !CBE_FuncHasGeneric(&mod.funcs[i]) {
            CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
            StringBuilder_Append(&cbe.sb, ";\n");
        }
        i = i + 1;
    }
    StringBuilder_Append(&cbe.sb, "\n");

    // Extern declarations
    i = 0;
    while i < mod.externCount {
        CBE_EmitFuncDecl(cbe, &mod.externFuncs[i]);
        StringBuilder_Append(&cbe.sb, ";\n");
        i = i + 1;
    }
    StringBuilder_Append(&cbe.sb, "\n");

    // Function definitions (skip generics)
    var hasMain: bool = false;
    i = 0;
    while i < mod.funcCount {
        if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
        // Skip generic functions
        if CBE_FuncHasGeneric(&mod.funcs[i]) {
            i = i + 1;
            continue;
        }
        // Skip forward declarations (functions without body)
        let body: *HirNode = mod.funcs[i].body;
        if body == null as *HirNode {
            i = i + 1;
            continue;
        }
        // Emit env struct and global instance for closures with captures
        if mod.funcs[i].captureCount > 0 {
            let envName: String = mod.funcs[i].envStructName;
            let instName: String = mod.funcs[i].envInstanceName;
            if !String_Eq(envName, "") {
                // Emit struct definition
                StringBuilder_Append(&cbe.sb, "struct ");
                StringBuilder_Append(&cbe.sb, envName);
                StringBuilder_Append(&cbe.sb, " {\n");
                var ci: int = 0;
                while ci < mod.funcs[i].captureCount {
                    var capName: String = "";
                    var capType: String = "int";
                    if ci == 0 { capName = mod.funcs[i].captureName0; capType = CBackend_TypeToC(mod.funcs[i].captureType0); }
                    else if ci == 1 { capName = mod.funcs[i].captureName1; capType = CBackend_TypeToC(mod.funcs[i].captureType1); }
                    else if ci == 2 { capName = mod.funcs[i].captureName2; capType = CBackend_TypeToC(mod.funcs[i].captureType2); }
                    else if ci == 3 { capName = mod.funcs[i].captureName3; capType = CBackend_TypeToC(mod.funcs[i].captureType3); }
                    else if ci == 4 { capName = mod.funcs[i].captureName4; capType = CBackend_TypeToC(mod.funcs[i].captureType4); }
                    else if ci == 5 { capName = mod.funcs[i].captureName5; capType = CBackend_TypeToC(mod.funcs[i].captureType5); }
                    else if ci == 6 { capName = mod.funcs[i].captureName6; capType = CBackend_TypeToC(mod.funcs[i].captureType6); }
                    else if ci == 7 { capName = mod.funcs[i].captureName7; capType = CBackend_TypeToC(mod.funcs[i].captureType7); }
                    StringBuilder_Append(&cbe.sb, "    ");
                    StringBuilder_Append(&cbe.sb, capType);
                    StringBuilder_Append(&cbe.sb, " ");
                    StringBuilder_Append(&cbe.sb, capName);
                    StringBuilder_Append(&cbe.sb, ";\n");
                    ci = ci + 1;
                }
                StringBuilder_Append(&cbe.sb, "};\n");
                // Emit global instance
                StringBuilder_Append(&cbe.sb, "static struct ");
                StringBuilder_Append(&cbe.sb, envName);
                StringBuilder_Append(&cbe.sb, " ");
                StringBuilder_Append(&cbe.sb, instName);
                StringBuilder_Append(&cbe.sb, ";\n\n");
            }
        }
        CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
        StringBuilder_Append(&cbe.sb, " {\n");
        // Body
        var hasReturn: bool = false;
        cbe.indent = 1;
        CBE_EmitExpr(cbe, body);
        CBE_EmitDefers(cbe);
        cbe.indent = 0;
        // Check if body has a return statement
        var stmt: *HirNode = body.child1;
        while stmt != null as *HirNode {
            if stmt.kind == hReturn { hasReturn = true; }
            stmt = stmt.child3;
        }
        // Add default return 0 only if function returns int and has no explicit return
        if String_Eq(mod.funcs[i].retTypeName, "int") && !hasReturn {
            StringBuilder_Append(&cbe.sb, "    return 0;\n");
        }
        StringBuilder_Append(&cbe.sb, "\n}\n\n");
        i = i + 1;
    }

    // Generate C main wrapper if Main function exists
    if hasMain {
        StringBuilder_Append(&cbe.sb, "extern int g_argc;\n");
        StringBuilder_Append(&cbe.sb, "extern char** g_argv;\n");
        StringBuilder_Append(&cbe.sb, "int main(int argc, char** argv) {\n");
        StringBuilder_Append(&cbe.sb, "    g_argc = argc;\n");
        StringBuilder_Append(&cbe.sb, "    g_argv = argv;\n");
        StringBuilder_Append(&cbe.sb, "    return Main();\n");
        StringBuilder_Append(&cbe.sb, "}\n");
    }

    return StringBuilder_Build(&cbe.sb);
}

}
