// Phase 1 steps 1.11–1.13 (PLAN-FFI.md): `#objc_call` call sites
// embedded inside the sx surface constructs. None touch a new ABI
// path — the lowering routes the call identically regardless of
// the enclosing scope, and this test pins that lemma.
//
//   1. Struct method body                  Probe.fetch
//   2. Protocol impl method body           impl Hashable for Probe
//   3. Closure value body                  closure that calls hash
//
// 1.14 (separate test): `inline if OS == { case }` gating across
// targets — verified by `tests/cross_compile.sh`.

#import "modules/std.sx";
#import "modules/compiler.sx";
#import "modules/std/objc.sx";

// ── 1. Struct method calling #objc_call ─────────────────────────────
Probe :: struct {
    receiver: *void = null;
    fetch :: (self: *Probe) -> s64 {
        #objc_call(s64)(self.receiver, "hash");
    }
}

// ── 2. Protocol impl method ────────────────────────────────────────
Hashable :: protocol {
    sx_hash :: (self: *Self) -> s64;
}

impl Hashable for Probe {
    sx_hash :: (self: *Probe) -> s64 {
        #objc_call(s64)(self.receiver, "hash") * 2;
    }
}

// ── 3. Closure body invoking #objc_call ─────────────────────────────
// The closure captures `recv` from its enclosing function and
// references it inside the `#objc_call` arg list. Locked in by
// `examples/103-ffi-closure-capture.sx`.
make_hasher :: (recv: *void) -> Closure(s32) -> s64 {
    closure((dummy: s32) -> s64 => #objc_call(s64)(recv, "hash"));
}

// ── 4. Generic function body — instantiated per call site ───────────
hash_through :: (recv: $T) -> s64 {
    p : *void = xx recv;
    #objc_call(s64)(p, "hash");
}

main :: () -> s32 {
    inline if OS == .macos {
        ns_object := objc_getClass("NSObject".ptr);

        p : Probe = .{ receiver = ns_object };

        // 1. struct method
        h1 := p.fetch();
        print("fetch != 0   = {}\n", h1 != 0);

        // 2. protocol method (doubles the raw hash; mostly checking
        //    dispatch / arg threading, not the math)
        h2 := p.sx_hash();
        print("protocol h2 = {}\n", h2 == h1 * 2);

        // 3. closure (receives a dummy arg to keep the `Closure(T) -> R`
        //    arity matching 35-closures.sx; `recv` is captured from
        //    `make_hasher`'s arg list and used inside the `#objc_call`).
        hasher := make_hasher(ns_object);
        h3 := hasher(0);
        print("closure h3  = {}\n", h3 == h1);

        // 4. generic function — instantiates with T = *void here
        h4 := hash_through(ns_object);
        print("generic h4  = {}\n", h4 == h1);
    }
    inline if OS != .macos {
        print("skipped (not macos)\n");
    }
    0;
}
