module CodegenCommon()

(**
 * CodegenCommon.mc
 * 5 June 2026
 * 
 * Shared code generation helpers used by both the full C emitter (codegen_c.c)
 * and the header emitter (codegen_header.mc).
 * This file should contain only low-level emission primatives and context
 * management that are independent of "full program" vs "header only" logic.
 *
 * 28 August 2026 - Conversion form .c to .mc started
 *)


// TODO: Why didn't semantic validate things like `pchar` and `pTType` before they were 
// used in the body of code AND listed in an import?

import from "tmodc.h"

import ppvoid, pchar from arena
import Node, pNode, from "node.h"
import DynBuf, pDynBuf, dynbuf_append, dynbuf_appendn, from dynbuf
import TType, pTType, type_c_spelling, from ttype
import from symkind
import from symbol
import from utils
import from mh_reader
import from Lexer

import size_t from "stddef.h"
import from "stdio.h"
import from "stdlib.h"
import from "string.h"
import from "version.h"


(** ===================================
 *  Codegen Context
 *  ===================================
 *)


(**
 * Target backend selection
 *)
export type CodegenTarget = enum
  TARGET_C,         // Current c backend (default)
  TARGET_HEADER,    // Header .h backend conaining exports.
  TARGET_MH,        // modc-mh/1 export table backend.
  TARGET_VM,        // Future bytecode VM
  TARGET_WASM,      // Future WebAssembly (optional)
  TARGET_JSON,      // JSON representation of the AST.
end


(**
 * Context passed through all recursive codegen functions.
 * Contains the output buffer, current indentation, and parent/scope intormation.
 * This eliminates long parameter lists and makes future extensions (symbol table,
 * defer lists, etc.) trivial. This is setup by the main codegen_generate function.
 *)
export type TCodegenContext = struct
    out: pDynBuf                        // REQUIRED: Output buffer - never NULL after init
    ast: const pNode                    // REQUIRED: Top of the AST.
    indent: integer                     // REQUIRED: Current indentation level
    debug_enabled: bool                 // REQUIRED: Indicate if debug mode is on or not.
    assert_off: bool                    // REQUIRED: Turn off assert statemetns if true
    dbc_off: bool                       // REQUIRED: Turn off DbC statements if true
    target: CodegenTarget               // REQUIRED: Code generation target.
    parent_ctx: ^TCodegenContext        // NULL: parent ctx. May be NULL.
    parent_node: const pNode            // NULL: immediate parent node (may be NULL)
    enclosing_func: const pNode         // NULL: nearest function/procedure for RETURN/DEFER
    enclosing_loop: const pNode         // NULL: nearest loop (for BREAK/CONTINUE)
end
export type pCodegenContext = ^TCodegenContext


(* ===============================================
 * Conext Helpers
 * =============================================== *)

(**
 * Creae a child context for a nested node.
 *)
export function ctx_push(ctx: const pCodegenContext, new_parent: const pNode): TCodegenContext forward


(* ===============================================
 * Low-level Emission Helpers
 * =============================================== *)

export procedure emit_indent(out: pDynBuf, indent: int) forward

export function codegen_unit_binds_portable(prog: const pNode, name: string, name_len: size_t): bool forward

export procedure codegen_emit_c_prelude(out: pDynBuf, prog: const pNode) forward

export procedure emit_return_type(out: pDynBuf, t: const pTType) forward

export procedure emit_array_type(out: pDynBuf, t: const pTType, name: const pchar, name_len: size_t) forward

export procedure emit_type_specifier(out: pDynBuf, t: const pTType, name: const pchar, length: size_t,
        is_param: bool, is_var: bool, is_ref: bool, is_const: bool) forward

export procedure emit_param_list(out: pDynBuf, params: ^pNode, count: size_t, is_parameter: bool) forward

export procedure emit_var_decl(out: pDynBuf, name: const pchar, name_len: size_t, t: const pTType) forward

export procedure emit_doc_comment_as_c(out: pDynBuf, n: const pNode) forward


(**
 * Emit a LET binding name: name_l{line}_c{col}
 * binding_decl is the NODE_LET_ITEM (sym-decl or let item node).
 *)
export procedure emit_let_binding_name(out: pDynBuf, name: const pchar, name_len: size_t,
                        binding_decl: const pNode) forward

(**
 * Emit an identifier expression (mangles LET uses via resolved_sym).
 *)
export procedure emit_let_ident(out: pDynBuf, n: const pNode) forward


(* ============================================
 * Reusable Declaration Emitters
 * ============================================ *)

export procedure codegen_common_type_decl(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_proc_or_func(n: const pNode, ctx: CodegenContext) forward

export procedure codege_common_struct_decl(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_union_decl(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_enum_type(n: const pNode, type_name: const pchar, type_name_len: size_t,
        ctx: CodegenContext) forward

export procedure codegen_common_method_type(method: const pNode, type_decl: const pNode, 
        ctx: CodegenContext) forward

export procedure codegen_common_expr(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_emit_literal(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_array_literal(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_preprocessor(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_field_access(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_sizeof_expr(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_inc_dec(n: const pNode, ctx: CodegenContext, as_statement: bool) forward

export procedure codegen_common_module_prototype(n: const pNode, ctx: CodegenContext) forward

export procedure codegen_common_import(n: const pNode, ctx: CodegeContext) forward


(* ============================================
 * Implementation
 * ============================================ *)


procedure emit_type_leaf_name(out: pDynBuf, t: const pTType)
begin
    var s: const pchar = nil
    var n: size_t = 0

    if t == nil then
        dynbuf_append(out, "int")
        return
    end

    if type_c_spelling(t, @s, @n) and s <> nil and n > 0 then
        dynbuf_appendn(out, s, n)
        return
    end

    if t^.name <> nil and t^.name_len > 0 then
        dynbuf_appendn(out, t^.name, t^.name_len)
        return
    end

    dynbuf_append(out, "int")
end emit_type_leaf_name


(**
 * Create a child context for a nested node.
 * Struct copy is cheap and safe.
 * Exits on fatal error (should never happen in normal use.).
 *)
export function ctx_push(ctx: const pCodegenContext, new_parent: const pNode): CodegenContext
begin
    require ctx <> nil and ctx^.out <> nil

    var child: CodegenContext = ctx^        // struct copy
    child.parent_node := new_parent
    child.parent_ctx := ctx

    // Update enclosing scopes when appropriate
    if new_parent <> nil then
        if new_parent^.kind == NODE_PROC_DECL or
                new_parent^.kind == NODE_FUNC_DECL then
            child.enclosing_func := new_parent
        end

        if new_parent^.kind == NODE_FOR or
                new_parent^.kind == NODE_WHILE or
                new_parent^.kind == NODE_REPEAT_UNTIL or
                new_parent^.kind == NODE_LOOP then
            child.enclosing_loop := new_parent
        end
    return child
end ctx_push


begin
end CodegenCommon
