using System.Runtime.InteropServices;

// [UnmanagedCallersOnly] static methods for vc_callbacks.on_event/on_level — true native
// function pointers, not GC-tracked delegates (avoids the classic P/Invoke pitfall where a
// delegate is collected by the GC sometime after the call that registered it returns; see
// docs/tech-stack.md §2). vc_callbacks.user is a GCHandle-wrapped VoiceCatClient (allocated in
// VoiceCatClient's constructor, freed in Dispose) — these methods must be static, so they
// resolve back to the right client instance via that handle rather than closing over state.
namespace VoiceCat.Interop;

internal static unsafe class NativeCallbacks
{
    [UnmanagedCallersOnly]
    internal static void OnEvent(IntPtr userContext, VcEventNative* ev)
    {
        if (userContext == IntPtr.Zero) return;
        if (GCHandle.FromIntPtr(userContext).Target is not VoiceCatClient client) return;

        // CRITICAL (voicecat.h's vc_event doc comment): ev->Text is owned by the core and
        // valid ONLY for the duration of this callback. Convert to a managed string NOW,
        // before returning — never store/queue the raw VcEventNative across the callback
        // boundary, or Text will be a dangling pointer by the time it's read.
        client.EnqueueEvent(VoiceCatEvent.FromNative(*ev));
    }

    [UnmanagedCallersOnly]
    internal static void OnLevel(IntPtr userContext, uint streamId, float rms)
    {
        if (userContext == IntPtr.Zero) return;
        if (GCHandle.FromIntPtr(userContext).Target is not VoiceCatClient client) return;
        client.EnqueueLevel(streamId, rms);
    }
}
