using Echo.Core.Common.Packed;
using ImGuiNET;
using SDL2;
using SDL2Demo.SdlWrapper;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using testdrid.SdlWrapper;

namespace Echo.UserInterface.Backend;

using static SDL;

/// <summary>
/// Implementation reference:
/// https://github.com/ocornut/imgui/blob/e23c5edd5fdef85ea0f5418b1368adb94bf86230/backends/imgui_impl_sdl.cpp
/// https://github.com/ocornut/imgui/blob/e23c5edd5fdef85ea0f5418b1368adb94bf86230/backends/imgui_impl_sdlrenderer.cpp
/// </summary>
public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
{
	private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
	public ImGuiDevice(IntPtr window, IntPtr renderer)
    {
        this.window = window;
        this.renderer = renderer;
        io = ImGui.GetIO();

        //Assign global names and flags
        AssignBackendNames();
        io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
        io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
        io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;

        //Link viewport data
        SDL_SysWMinfo info = default;
        SDL_VERSION(out info.version);

        if (SDL_GetWindowWMInfo(window, ref info) == SDL_bool.SDL_TRUE)
        {
            ImGuiViewportPtr viewport = ImGui.GetMainViewport();
            viewport.PlatformHandleRaw = info.info.win.window;
        }

        //Setup SDL hint
        SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); //Can be nearest, linear, or best
    }

    public void Initialize()
    {
        //Create resources
        CreateMouseCursors();
        CreateFontTexture();
        CreateClipboardSetup();
    }

    readonly IntPtr window;
    readonly IntPtr renderer;
    ImGuiIOPtr io;

    IntPtr[] mouseCursors;
    IntPtr fontTexture;

    int mouseButtonDownCount;
    int pendingMouseLeaveFrame;

    bool disposed;

    static readonly SetClipboardTextFn SetClipboardText = (_, text) => SDL_SetClipboardText(text);
    static readonly GetClipboardTextFn GetClipboardText = _ => SDL_GetClipboardText();

    public void ProcessEvent(in SDL_Event sdlEvent)
    {
        switch (sdlEvent.type)
        {
            case SDL_EventType.SDL_MOUSEMOTION:
                {
                    io.AddMousePosEvent(sdlEvent.motion.x, sdlEvent.motion.y);
                    break;
                }
            case SDL_EventType.SDL_MOUSEWHEEL:
                {
                    io.AddMouseWheelEvent(sdlEvent.wheel.x, sdlEvent.wheel.y);
                    break;
                }
            case SDL_EventType.SDL_MOUSEBUTTONDOWN:
            case SDL_EventType.SDL_MOUSEBUTTONUP:
                {
                    ProcessMouseButtonEvent(sdlEvent.button);
                    break;
                }
            case SDL_EventType.SDL_TEXTINPUT:
                {
                    fixed (byte* ptr = sdlEvent.text.text) ImGuiNative.ImGuiIO_AddInputCharactersUTF8(io.NativePtr, ptr);
                    break;
                }
            case SDL_EventType.SDL_KEYDOWN:
            case SDL_EventType.SDL_KEYUP:
                {
                    ProcessKeyboardEvent(sdlEvent.key);
                    break;
                }
            case SDL_EventType.SDL_WINDOWEVENT:
                {
                    ProcessWindowEvent(sdlEvent.window);
                    break;
                }
        }
    }

    public void NewFrame(in TimeSpan deltaTime)
    {
        io.DeltaTime = (float)deltaTime.TotalSeconds;

        RefreshDisplaySize();
        UpdateMouseData();
        UpdateMouseCursor();
    }

    public void Render(ImDrawDataPtr data)
    {

        //Setup clip information
        Vector2 scale = data.FramebufferScale;
        Float4 clipSize = Widen(scale * data.DisplaySize);
        if (clipSize.X <= 0f || clipSize.Y <= 0f) return;

        SDL_Rect zerocopy = new SDL_Rect();
        //Render
        if (SDL_RenderSetScale(renderer, scale.X, scale.Y) != 0)
            throw SdlException.GenerateException();

        if (SDL_RenderSetClipRect(renderer, ref zerocopy) != 0)
            throw SdlException.GenerateException();

        Float4 clipOffset = Widen(data.DisplayPos);
        var lists = data.CmdListsRange;

        for (int i = 0; i < lists.Count; i++)
        {
            ExecuteCommandList(lists[i], clipOffset, clipSize);
        }

       // SDL_RenderPresent(renderer);

        static Float4 Widen(Vector2 vector) => new Float4(vector.AsVector128()).XYXY;
    }

    public IntPtr CreateTexture(Int2 size, bool streaming, bool bigEndian = false)
    {
        int access = streaming ?
            (int)SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING :
            (int)SDL_TextureAccess.SDL_TEXTUREACCESS_STATIC;
        uint format = bigEndian ? SDL_PIXELFORMAT_RGBA8888 : SDL_PIXELFORMAT_ABGR8888;

        IntPtr texture = SDL_CreateTexture(renderer, format, access, size.X, size.Y);
        if (texture == IntPtr.Zero)
            throw SdlException.GenerateException();

        if (SDL_SetTextureBlendMode(texture, SDL_BlendMode.SDL_BLENDMODE_BLEND) != 0)
            throw SdlException.GenerateException();

        return texture;
    }

    public void DestroyTexture(ref IntPtr texture)
    {
        if (texture == IntPtr.Zero) return;

        SDL_DestroyTexture(texture);
        texture = IntPtr.Zero;
    }

    public void Dispose()
    {
        if (disposed) return;
        disposed = true;

        DestroyMouseCursors();
        DestroyFontTexture();
        DestroyClipboardSetup();
        io = null;
    }

    void AssignBackendNames()
    {
        if (SDL_GetRendererInfo(renderer, out SDL_RendererInfo info) != 0)
            throw SdlException.GenerateException();

        var name = (Marshal.PtrToStringAnsi(info.name) ?? "unknown").ToUpper();
        var size = new Int2(info.max_texture_width, info.max_texture_height);

        io.NativePtr->BackendPlatformName = (byte*)Marshal.StringToHGlobalAnsi("SDL2 & Dear ImGui for C#");
        io.NativePtr->BackendRendererName = (byte*)Marshal.StringToHGlobalAnsi($"{name} {size.X}x{size.Y}");
    }

    void CreateMouseCursors()
    {
        mouseCursors = new IntPtr[(int)ImGuiMouseCursor.COUNT];

        mouseCursors[(int)ImGuiMouseCursor.Arrow] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW);
        mouseCursors[(int)ImGuiMouseCursor.TextInput] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM);
        mouseCursors[(int)ImGuiMouseCursor.ResizeAll] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL);
        mouseCursors[(int)ImGuiMouseCursor.ResizeNS] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS);
        mouseCursors[(int)ImGuiMouseCursor.ResizeEW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE);
        mouseCursors[(int)ImGuiMouseCursor.ResizeNESW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW);
        mouseCursors[(int)ImGuiMouseCursor.ResizeNWSE] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE);
        mouseCursors[(int)ImGuiMouseCursor.Hand] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND);
        mouseCursors[(int)ImGuiMouseCursor.NotAllowed] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO);
    }

    void DestroyMouseCursors()
    {
        if (mouseCursors == null) return;

        foreach (IntPtr cursor in mouseCursors)
        {
            if (cursor == IntPtr.Zero) continue;
            SDL_FreeCursor(cursor);
        }

        mouseCursors = null;
    }

    void CreateFontTexture()
    {
        io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height);
        fontTexture = CreateTexture(new Int2(width, height), false);

        if (SDL_UpdateTexture(fontTexture, IntPtr.Zero, pixels, width * sizeof(uint)) != 0)
            throw SdlException.GenerateException();
        if (SDL_SetTextureBlendMode(fontTexture, SDL_BlendMode.SDL_BLENDMODE_BLEND) != 0)
            throw SdlException.GenerateException();
        if (SDL_SetTextureScaleMode(fontTexture, SDL_ScaleMode.SDL_ScaleModeLinear) != 0)
            throw SdlException.GenerateException();

        io.Fonts.SetTexID(fontTexture);
    }

    void DestroyFontTexture()
    {
        DestroyTexture(ref fontTexture);
        io.Fonts.SetTexID(IntPtr.Zero);
    }

    void CreateClipboardSetup()
    {
        io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(SetClipboardText);
        io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(GetClipboardText);
    }

    void DestroyClipboardSetup()
    {
        io.SetClipboardTextFn = IntPtr.Zero;
        io.GetClipboardTextFn = IntPtr.Zero;
    }

    void ProcessMouseButtonEvent(SDL_MouseButtonEvent mouseButtonEvent)
    {
        int mouseButton = (uint)mouseButtonEvent.button switch
        {
            SDL_BUTTON_LEFT => 0,
            SDL_BUTTON_RIGHT => 1,
            SDL_BUTTON_MIDDLE => 2,
            SDL_BUTTON_X1 => 3,
            SDL_BUTTON_X2 => 4,
            _ => -1
        };

        if (mouseButton < 0) return;

        bool down = mouseButtonEvent.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
        io.AddMouseButtonEvent(mouseButton, down);
        mouseButtonDownCount += down ? 1 : -1;
    }

    void ProcessKeyboardEvent(in SDL_KeyboardEvent keyboardEvent)
    {
        ref readonly SDL_Keysym key = ref keyboardEvent.keysym;
        bool down = keyboardEvent.type == SDL_EventType.SDL_KEYDOWN;

        io.AddKeyEvent(ImGuiKey.ModCtrl, (key.mod & SDL_Keymod.KMOD_CTRL) != 0);
        io.AddKeyEvent(ImGuiKey.ModShift, (key.mod & SDL_Keymod.KMOD_SHIFT) != 0);
        io.AddKeyEvent(ImGuiKey.ModAlt, (key.mod & SDL_Keymod.KMOD_ALT) != 0);
        io.AddKeyEvent(ImGuiKey.ModSuper, (key.mod & SDL_Keymod.KMOD_GUI) != 0);

        io.AddKeyEvent(SDL_KeycodeToImGuiKey(key.sym), down);
    }

    public static ImGuiKey SDL_KeycodeToImGuiKey(SDL_Keycode key) => key switch
    {
        SDL_Keycode.SDLK_TAB => ImGuiKey.Tab,
        SDL_Keycode.SDLK_LEFT => ImGuiKey.LeftArrow,
        SDL_Keycode.SDLK_RIGHT => ImGuiKey.RightArrow,
        SDL_Keycode.SDLK_UP => ImGuiKey.UpArrow,
        SDL_Keycode.SDLK_DOWN => ImGuiKey.DownArrow,
        SDL_Keycode.SDLK_PAGEUP => ImGuiKey.PageUp,
        SDL_Keycode.SDLK_PAGEDOWN => ImGuiKey.PageDown,
        SDL_Keycode.SDLK_HOME => ImGuiKey.Home,
        SDL_Keycode.SDLK_END => ImGuiKey.End,
        SDL_Keycode.SDLK_INSERT => ImGuiKey.Insert,
        SDL_Keycode.SDLK_DELETE => ImGuiKey.Delete,
        SDL_Keycode.SDLK_BACKSPACE => ImGuiKey.Backspace,
        SDL_Keycode.SDLK_SPACE => ImGuiKey.Space,
        SDL_Keycode.SDLK_RETURN => ImGuiKey.Enter,
        SDL_Keycode.SDLK_ESCAPE => ImGuiKey.Escape,
        SDL_Keycode.SDLK_QUOTE => ImGuiKey.Apostrophe,
        SDL_Keycode.SDLK_COMMA => ImGuiKey.Comma,
        SDL_Keycode.SDLK_MINUS => ImGuiKey.Minus,
        SDL_Keycode.SDLK_PERIOD => ImGuiKey.Period,
        SDL_Keycode.SDLK_SLASH => ImGuiKey.Slash,
        SDL_Keycode.SDLK_SEMICOLON => ImGuiKey.Semicolon,
        SDL_Keycode.SDLK_EQUALS => ImGuiKey.Equal,
        SDL_Keycode.SDLK_LEFTBRACKET => ImGuiKey.LeftBracket,
        SDL_Keycode.SDLK_BACKSLASH => ImGuiKey.Backslash,
        SDL_Keycode.SDLK_RIGHTBRACKET => ImGuiKey.RightBracket,
        SDL_Keycode.SDLK_BACKQUOTE => ImGuiKey.GraveAccent,
        SDL_Keycode.SDLK_CAPSLOCK => ImGuiKey.CapsLock,
        SDL_Keycode.SDLK_SCROLLLOCK => ImGuiKey.ScrollLock,
        SDL_Keycode.SDLK_NUMLOCKCLEAR => ImGuiKey.NumLock,
        SDL_Keycode.SDLK_PRINTSCREEN => ImGuiKey.PrintScreen,
        SDL_Keycode.SDLK_PAUSE => ImGuiKey.Pause,
        SDL_Keycode.SDLK_KP_0 => ImGuiKey.Keypad0,
        SDL_Keycode.SDLK_KP_1 => ImGuiKey.Keypad1,
        SDL_Keycode.SDLK_KP_2 => ImGuiKey.Keypad2,
        SDL_Keycode.SDLK_KP_3 => ImGuiKey.Keypad3,
        SDL_Keycode.SDLK_KP_4 => ImGuiKey.Keypad4,
        SDL_Keycode.SDLK_KP_5 => ImGuiKey.Keypad5,
        SDL_Keycode.SDLK_KP_6 => ImGuiKey.Keypad6,
        SDL_Keycode.SDLK_KP_7 => ImGuiKey.Keypad7,
        SDL_Keycode.SDLK_KP_8 => ImGuiKey.Keypad8,
        SDL_Keycode.SDLK_KP_9 => ImGuiKey.Keypad9,
        SDL_Keycode.SDLK_KP_PERIOD => ImGuiKey.KeypadDecimal,
        SDL_Keycode.SDLK_KP_DIVIDE => ImGuiKey.KeypadDivide,
        SDL_Keycode.SDLK_KP_MULTIPLY => ImGuiKey.KeypadMultiply,
        SDL_Keycode.SDLK_KP_MINUS => ImGuiKey.KeypadSubtract,
        SDL_Keycode.SDLK_KP_PLUS => ImGuiKey.KeypadAdd,
        SDL_Keycode.SDLK_KP_ENTER => ImGuiKey.KeypadEnter,
        SDL_Keycode.SDLK_KP_EQUALS => ImGuiKey.KeypadEqual,
        SDL_Keycode.SDLK_LCTRL => ImGuiKey.LeftCtrl,
        SDL_Keycode.SDLK_LSHIFT => ImGuiKey.LeftShift,
        SDL_Keycode.SDLK_LALT => ImGuiKey.LeftAlt,
        SDL_Keycode.SDLK_LGUI => ImGuiKey.LeftSuper,
        SDL_Keycode.SDLK_RCTRL => ImGuiKey.RightCtrl,
        SDL_Keycode.SDLK_RSHIFT => ImGuiKey.RightShift,
        SDL_Keycode.SDLK_RALT => ImGuiKey.RightAlt,
        SDL_Keycode.SDLK_RGUI => ImGuiKey.RightSuper,
        SDL_Keycode.SDLK_APPLICATION => ImGuiKey.Menu,
        SDL_Keycode.SDLK_0 => ImGuiKey._0,
        SDL_Keycode.SDLK_1 => ImGuiKey._1,
        SDL_Keycode.SDLK_2 => ImGuiKey._2,
        SDL_Keycode.SDLK_3 => ImGuiKey._3,
        SDL_Keycode.SDLK_4 => ImGuiKey._4,
        SDL_Keycode.SDLK_5 => ImGuiKey._5,
        SDL_Keycode.SDLK_6 => ImGuiKey._6,
        SDL_Keycode.SDLK_7 => ImGuiKey._7,
        SDL_Keycode.SDLK_8 => ImGuiKey._8,
        SDL_Keycode.SDLK_9 => ImGuiKey._9,
        SDL_Keycode.SDLK_a => ImGuiKey.A,
        SDL_Keycode.SDLK_b => ImGuiKey.B,
        SDL_Keycode.SDLK_c => ImGuiKey.C,
        SDL_Keycode.SDLK_d => ImGuiKey.D,
        SDL_Keycode.SDLK_e => ImGuiKey.E,
        SDL_Keycode.SDLK_f => ImGuiKey.F,
        SDL_Keycode.SDLK_g => ImGuiKey.G,
        SDL_Keycode.SDLK_h => ImGuiKey.H,
        SDL_Keycode.SDLK_i => ImGuiKey.I,
        SDL_Keycode.SDLK_j => ImGuiKey.J,
        SDL_Keycode.SDLK_k => ImGuiKey.K,
        SDL_Keycode.SDLK_l => ImGuiKey.L,
        SDL_Keycode.SDLK_m => ImGuiKey.M,
        SDL_Keycode.SDLK_n => ImGuiKey.N,
        SDL_Keycode.SDLK_o => ImGuiKey.O,
        SDL_Keycode.SDLK_p => ImGuiKey.P,
        SDL_Keycode.SDLK_q => ImGuiKey.Q,
        SDL_Keycode.SDLK_r => ImGuiKey.R,
        SDL_Keycode.SDLK_s => ImGuiKey.S,
        SDL_Keycode.SDLK_t => ImGuiKey.T,
        SDL_Keycode.SDLK_u => ImGuiKey.U,
        SDL_Keycode.SDLK_v => ImGuiKey.V,
        SDL_Keycode.SDLK_w => ImGuiKey.W,
        SDL_Keycode.SDLK_x => ImGuiKey.X,
        SDL_Keycode.SDLK_y => ImGuiKey.Y,
        SDL_Keycode.SDLK_z => ImGuiKey.Z,
        SDL_Keycode.SDLK_F1 => ImGuiKey.F1,
        SDL_Keycode.SDLK_F2 => ImGuiKey.F2,
        SDL_Keycode.SDLK_F3 => ImGuiKey.F3,
        SDL_Keycode.SDLK_F4 => ImGuiKey.F4,
        SDL_Keycode.SDLK_F5 => ImGuiKey.F5,
        SDL_Keycode.SDLK_F6 => ImGuiKey.F6,
        SDL_Keycode.SDLK_F7 => ImGuiKey.F7,
        SDL_Keycode.SDLK_F8 => ImGuiKey.F8,
        SDL_Keycode.SDLK_F9 => ImGuiKey.F9,
        SDL_Keycode.SDLK_F10 => ImGuiKey.F10,
        SDL_Keycode.SDLK_F11 => ImGuiKey.F11,
        SDL_Keycode.SDLK_F12 => ImGuiKey.F12,
        _ => ImGuiKey.None
    };

    void ProcessWindowEvent(in SDL_WindowEvent windowEvent)
    {
        switch (windowEvent.windowEvent)
        {
            case SDL_WindowEventID.SDL_WINDOWEVENT_ENTER:
                {
                    pendingMouseLeaveFrame = 0;
                    break;
                }
            case SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE:
                {
                    pendingMouseLeaveFrame = ImGui.GetFrameCount() + 1;
                    break;
                }
            case SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
                {
                    io.AddFocusEvent(true);
                    break;
                }
            case SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
                {
                    io.AddFocusEvent(false);
                    break;
                }
        }
    }

    void RefreshDisplaySize()
    {
        SDL_GetWindowSize(window, out int width, out int height);
        var size = io.DisplaySize = new Vector2(width, height);

        if (SDL_GetRendererOutputSize(renderer, out int displayWidth, out int displayHeight) != 0)
            throw SdlException.GenerateException();

        if ((SDL_GetWindowFlags(window) & (uint)SDL_WindowFlags.SDL_WINDOW_MINIMIZED) != 0) size = Vector2.Zero;
        if (size.X > 0f && size.Y > 0f) io.DisplayFramebufferScale = new Vector2(displayWidth, displayHeight) / size;
    }

    private long nandate = 0;
    void UpdateMouseData()
    {
        if (pendingMouseLeaveFrame >= ImGui.GetFrameCount() && mouseButtonDownCount == 0)
        {
            io.AddMousePosEvent(float.MinValue, float.MinValue);
            pendingMouseLeaveFrame = 0;
        }

        if (SDL_CaptureMouse(mouseButtonDownCount != 0 ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE) != 0)
        {
            nandate++;
            //throw SdlException.GenerateException();
        }

        if (window != SDL_GetKeyboardFocus()) return;
        if (io.WantSetMousePos) SDL_WarpMouseInWindow(window, (int)io.MousePos.X, (int)io.MousePos.Y);

        if (mouseButtonDownCount == 0)
        {
            _ = SDL_GetGlobalMouseState(out int globalX, out int globalY);
            SDL_GetWindowPosition(window, out int windowX, out int windowY);

            io.AddMousePosEvent(globalX - windowX, globalY - windowY);
        }
    }

    void UpdateMouseCursor()
    {
        if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0) return;

        ImGuiMouseCursor cursor = ImGui.GetMouseCursor();

        if (!io.MouseDrawCursor && cursor != ImGuiMouseCursor.None)
        {
            IntPtr mouseCursor = mouseCursors[(int)cursor];
            const int Fallback = (int)ImGuiMouseCursor.Arrow;

            if (mouseCursor == IntPtr.Zero) mouseCursor = mouseCursors[Fallback];

            SDL_SetCursor(mouseCursor);
            _ = SDL_ShowCursor((int)SDL_bool.SDL_TRUE);
        }
        else _ = SDL_ShowCursor((int)SDL_bool.SDL_FALSE);
    }

    private uint numErrors;
    void ExecuteCommandList(ImDrawListPtr list, in Float4 clipOffset, in Float4 clipSize)
    {
        ImPtrVector<ImDrawCmdPtr> buffer = list.CmdBuffer;
        var vertices = (ImDrawVert*)list.VtxBuffer.Data;
        var indices = (ushort*)list.IdxBuffer.Data;

        for (int i = 0; i < buffer.Size; i++)
        {
            ImDrawCmdPtr command = buffer[i];

            if (command.UserCallback == IntPtr.Zero)
            {
                var clipRect = new Float4(command.ClipRect.AsVector128()) - clipOffset;

                Float4 clipMin = clipRect.Max(Float4.Zero); //(minX, minY, ____, ____).Max(zero)
                Float4 clipMax = clipRect.Min(clipSize);    //(____, ____, maxX, maxY).Min(size)

                if (clipMax.Z <= clipMin.X || clipMax.W <= clipMin.Y) continue;

                clipMin = clipMin.XYXY; //(minX, minY, minX, minY)
                clipMax = clipMax.__ZW; //(0000, 0000, maxX, maxY)

                Float4 clip = (clipMax - clipMin).Absoluted;     //(-minX, -minY, maxX - minX, maxY - minY).Absoluted
                var rect = Sse2.ConvertToVector128Int32(clip.v); //(    X,     Y,       width,      height)
                ImDrawVert* vertex = vertices + command.VtxOffset;
                int stride = sizeof(ImDrawVert);

                lock (Renderer.Lockable)
                {
                    if (SDL_RenderGeometryRaw
                        (
                            renderer, command.TextureId,
                            (float*)&vertex->pos, stride,
                            (int*)&vertex->col, stride,
                            (float*)&vertex->uv, stride,
                            list.VtxBuffer.Size - (int)command.VtxOffset,
                            (IntPtr)(indices + command.IdxOffset),
                            (int)command.ElemCount, sizeof(ushort)
                        ) != 0)
                    {
	                    numErrors++;
						SdlException sdlException = SdlException.GenerateException();
                        //We can tolerate the Geometry Renderer going sideways a few times
                        logger.Log(PluginLogLevel.Error, String.Format("{0}, ({1}x)",sdlException.ToString(),numErrors));
                    }
                }
            }
            else
            {
                var callback = Marshal.GetDelegateForFunctionPointer<ImDrawCallback>(command.UserCallback);
                callback(new IntPtr(list), new IntPtr(command)); //Perform user callback, not really used
            }
        }
    }

    [DllImport("SDL2", CallingConvention = CallingConvention.Cdecl)]
    public static extern unsafe int SDL_RenderGeometryRaw(IntPtr renderer,
        IntPtr texture,
        float* xy,
        int xy_stride,
        int* color,
        int color_stride,
        float* uv,
        int uv_stride,
        int num_vertices,
        IntPtr indices,
        int num_indices,
        int size_indices);

    delegate void SetClipboardTextFn(IntPtr _, string text);
    delegate string GetClipboardTextFn(IntPtr _);
    delegate void ImDrawCallback(IntPtr list, IntPtr cmd);

    public void ProcessEvent(SDL_Event evt)
    {
        ProcessEvent(in evt);
    }
}