using System.Runtime.InteropServices;
using System.Text;

namespace Nerfed.Runtime.Gui;

public static unsafe class GuiClipboard
{
    private static IntPtr clipboard;
    private static readonly Dictionary<Delegate, IntPtr> pinned = new Dictionary<Delegate, IntPtr>();

    public static readonly IntPtr GetFnPtr = GetPointerTo(Get);
    public static readonly IntPtr SetFnPtr = GetPointerTo(Set);

    private static unsafe void Set(void* userdata, byte* text)
    {
        int len = 0; while (text[len] != 0) len++;
        string str = Encoding.UTF8.GetString(text, len);
        SDL2.SDL.SDL_SetClipboardText(str);
    }

    private static unsafe byte* Get(void* userdata)
    {
        if (clipboard != IntPtr.Zero)
        {
            NativeMemory.Free((void*) clipboard);
            clipboard = IntPtr.Zero;
        }

        string str = SDL2.SDL.SDL_GetClipboardText();
        int length = Encoding.UTF8.GetByteCount(str);
        byte* bytes = (byte*)(clipboard = (nint)NativeMemory.Alloc((nuint)(length + 1)));

        Encoding.UTF8.GetBytes(str, new Span<byte>(bytes, length));
        bytes[length] = 0;
        return bytes;
    }

    // Stops the delegate pointer from being collected
    private static IntPtr GetPointerTo<T>(T fn) where T : Delegate
    {
        if (pinned.TryGetValue(fn, out nint ptr))
        {
            return ptr;
        }

        ptr = Marshal.GetFunctionPointerForDelegate(fn);
        pinned.Add(fn, ptr);
        return ptr;
    }
}