// Comptime compiler API — the WRITE side: one kind-branching `register_type`
// minting an actual enum AND a graph of mutually-recursive types (Phase 3).
//
// `declare_type` / `pointer_to` / `register_type` are bound to the `compiler`
// library. They MINT into the type table, so they run at LOWERING time (lazily,
// on demand) — when a `-> Type` builder's result is first referenced — where the
// compiler still resolves references to the new types. (`#run` is too late: it
// runs at emit time, after the type table is frozen.) They take/return real
// `Type` values (like the metatype's declare/define), and `register_type`
// branches on the `kind` arg IN THE COMPILER — the codes match the read-side
// `type_kind`: 1 struct · 2 enum · 3 tagged_union · 4 tuple.
//
//   Suit   :: enum { hearts; spades; diamonds; }          (actual, payloadless)
//   GraphA :: enum { self_ref: *A; to_b: B; tag: u32; }   (payloads → tagged_union)
//   GraphB :: enum { back_a:  *A; self_b: *B; num: u32; }
//
// Forward `declare_type` handles + `pointer_to` make the A<->B cycle expressible
// before either body is filled.

#import "modules/std.sx";


Member :: struct { name: string; ty: Type; }

StringId :: u32;
TypeId   :: u32;

intern        :: (s: string) -> StringId abi(.compiler);
find_type     :: (name: StringId) -> TypeId abi(.compiler);
type_kind     :: (t: TypeId) -> i64 abi(.compiler);
declare_type  :: (name: string) -> Type abi(.compiler);
pointer_to    :: (t: Type) -> Type abi(.compiler);
register_type :: (handle: Type, kind: i64, members: []Member) -> Type abi(.compiler);

KIND_ENUM         :: 2; // an ACTUAL payloadless enum
KIND_TAGGED_UNION :: 3; // a payload-carrying enum

// An actual enum: variants are names, no payloads (ty = void).
make_suit :: () -> Type {
    return register_type(declare_type("Suit"), KIND_ENUM, .[
        Member.{ name = "hearts",   ty = void },
        Member.{ name = "spades",   ty = void },
        Member.{ name = "diamonds", ty = void },
    ]);
}
Suit :: make_suit();

// The mutually-recursive A <-> B graph (payload variants → tagged_union).
build_graph :: () -> Type {
    hA := declare_type("GraphA");
    hB := declare_type("GraphB");
    register_type(hA, KIND_TAGGED_UNION, .[
        Member.{ name = "self_ref", ty = pointer_to(hA) }, // *A — self-reference
        Member.{ name = "to_b",     ty = hB },             // B by value (forward)
        Member.{ name = "tag",      ty = u32 },            // a plain payload
    ]);
    register_type(hB, KIND_TAGGED_UNION, .[
        Member.{ name = "back_a", ty = pointer_to(hA) },    // *A — back-reference
        Member.{ name = "self_b", ty = pointer_to(hB) },    // *B — self-reference
        Member.{ name = "num",    ty = u32 },
    ]);
    return hA;
}
GraphA :: build_graph();

// Reflect the minted types (read side, at #run) to confirm their kinds.
suit_kind   :: #run type_kind(find_type(intern("Suit")));   // 2 = actual enum
grapha_kind :: #run type_kind(find_type(intern("GraphA"))); // 3 = tagged_union

main :: () -> i32 {
    // Suit is a real, usable enum.
    s := Suit.spades;
    if s == {
        case .hearts:   { print("hearts\n"); }
        case .spades:   { print("spades\n"); }
        case .diamonds: { print("diamonds\n"); }
    }
    // GraphA is a real, usable tagged union.
    a := GraphA.tag(7);
    if a == {
        case .tag: (n)      { print("tag={}\n", n); }
        case .self_ref: (p) { print("self_ref\n"); }
        case .to_b: (b)     { print("to_b\n"); }
    }
    print("Suit kind={}, GraphA kind={}\n", suit_kind, grapha_kind);
    return 0;
}
