/**********************************************************************************************
*
*   rcore_desktop_rgfw - Functions to manage window, graphics device and inputs
*
*   PLATFORM: RGFW
*       - Windows (Win32, Win64)
*       - Linux (X11/Wayland desktop mode)
*       - MacOS (Cocoa)
*       - HTML5 (Emscripten)
*       - Others (untested)
*
*   LIMITATIONS:
*       - TODO
*
*   POSSIBLE IMPROVEMENTS:
*       - TODO
*
*   ADDITIONAL NOTES:
*       - TRACELOG() function is located in raylib [utils] module
*
*   CONFIGURATION:
*       #define RCORE_PLATFORM_RGFW
*           Custom flag for rcore on target platform RGFW
*
*   DEPENDENCIES:
*       - RGFW.h (main library): Windowing and inputs management
*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
*
*
*   LICENSE: zlib/libpng
*
*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5), Colleague Riley and contributors
*
*   This software is provided "as-is", without any express or implied warranty. In no event
*   will the authors be held liable for any damages arising from the use of this software.
*
*   Permission is granted to anyone to use this software for any purpose, including commercial
*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
*     1. The origin of this software must not be misrepresented; you must not claim that you
*     wrote the original software. If you use this software in a product, an acknowledgment
*     in the product documentation would be appreciated but is not required.
*
*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
*     as being the original software.
*
*     3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/

#ifndef RAYLIB_H /* this should never actually happen, it's only here for IDEs */
#include "raylib.h"
#include "../rcore.c"
#endif

#if defined(PLATFORM_WEB_RGFW)
#define RGFW_NO_GL_HEADER
#endif

#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(PLATFORM_WEB_RGFW)
    #define RGFW_OPENGL_ES2
#endif

void ShowCursor(void);
void CloseWindow(void);

#if defined(__linux__)
    #define _INPUT_EVENT_CODES_H
#endif

#if defined(__unix__) || defined(__linux__)
    #define _XTYPEDEF_FONT
#endif

#define RGFW_IMPLEMENTATION

#if defined(_WIN32) || defined(_WIN64)
    #define WIN32_LEAN_AND_MEAN
    #define Rectangle rectangle_win32
    #define CloseWindow CloseWindow_win32
    #define ShowCursor __imp_ShowCursor
    #define _APISETSTRING_
    
    #undef MAX_PATH

    __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
#endif

#if defined(__APPLE__)
    #define Point NSPOINT
    #define Size NSSIZE
#endif

#define RGFW_ALLOC RL_MALLOC
#define RGFW_FREE RL_FREE
#define RGFW_CALLOC RL_CALLOC

#include "../external/RGFW.h"

#if defined(_WIN32) || defined(_WIN64)
    #undef DrawText
    #undef ShowCursor
    #undef CloseWindow
    #undef Rectangle

    #undef MAX_PATH
    #define MAX_PATH 1025
#endif

#if defined(__APPLE__)
    #undef Point
    #undef Size
#endif

#include <stdbool.h>
#include <string.h>     // Required for: strcmp()

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
    RGFW_window *window;                // Native display device (physical screen connection)
    RGFW_monitor mon;
} PlatformData;

//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
extern CoreData CORE;                   // Global CORE state context

static PlatformData platform = { 0 };   // Platform specific

static bool RGFW_disableCursor = false;

static const unsigned short keyMappingRGFW[] = {
    [RGFW_keyNULL] = KEY_NULL,
    [RGFW_return] = KEY_ENTER,
    [RGFW_return] = KEY_ENTER,
    [RGFW_apostrophe] = KEY_APOSTROPHE,
    [RGFW_comma] = KEY_COMMA,
    [RGFW_minus] = KEY_MINUS,
    [RGFW_period] = KEY_PERIOD,
    [RGFW_slash] = KEY_SLASH,
    [RGFW_escape] = KEY_ESCAPE,
    [RGFW_F1] = KEY_F1,
    [RGFW_F2] = KEY_F2,
    [RGFW_F3] = KEY_F3,
    [RGFW_F4] = KEY_F4,
    [RGFW_F5] = KEY_F5,
    [RGFW_F6] = KEY_F6,
    [RGFW_F7] = KEY_F7,
    [RGFW_F8] = KEY_F8,
    [RGFW_F9] = KEY_F9,
    [RGFW_F10] = KEY_F10,
    [RGFW_F11] = KEY_F11,
    [RGFW_F12] = KEY_F12,
    [RGFW_backtick] = KEY_GRAVE,
    [RGFW_0] = KEY_ZERO,
    [RGFW_1] = KEY_ONE,
    [RGFW_2] = KEY_TWO,
    [RGFW_3] = KEY_THREE,
    [RGFW_4] = KEY_FOUR,
    [RGFW_5] = KEY_FIVE,
    [RGFW_6] = KEY_SIX,
    [RGFW_7] = KEY_SEVEN,
    [RGFW_8] = KEY_EIGHT,
    [RGFW_9] = KEY_NINE,
    [RGFW_equals] = KEY_EQUAL,
    [RGFW_backSpace] = KEY_BACKSPACE,
    [RGFW_tab] = KEY_TAB,
    [RGFW_capsLock] = KEY_CAPS_LOCK,
    [RGFW_shiftL] = KEY_LEFT_SHIFT,
    [RGFW_controlL] = KEY_LEFT_CONTROL,
    [RGFW_altL] = KEY_LEFT_ALT,
    [RGFW_superL] = KEY_LEFT_SUPER,
    #ifndef RGFW_MACOS
    [RGFW_shiftR] = KEY_RIGHT_SHIFT,
    [RGFW_controlR] = KEY_RIGHT_CONTROL,
    [RGFW_altR] = KEY_RIGHT_ALT,
    #endif
    [RGFW_space] = KEY_SPACE,

    [RGFW_a] = KEY_A,
    [RGFW_b] = KEY_B,
    [RGFW_c] = KEY_C,
    [RGFW_d] = KEY_D,
    [RGFW_e] = KEY_E,
    [RGFW_f] = KEY_F,
    [RGFW_g] = KEY_G,
    [RGFW_h] = KEY_H,
    [RGFW_i] = KEY_I,
    [RGFW_j] = KEY_J,
    [RGFW_k] = KEY_K,
    [RGFW_l] = KEY_L,
    [RGFW_m] = KEY_M,
    [RGFW_n] = KEY_N,
    [RGFW_o] = KEY_O,
    [RGFW_p] = KEY_P,
    [RGFW_q] = KEY_Q,
    [RGFW_r] = KEY_R,
    [RGFW_s] = KEY_S,
    [RGFW_t] = KEY_T,
    [RGFW_u] = KEY_U,
    [RGFW_v] = KEY_V,
    [RGFW_w] = KEY_W,
    [RGFW_x] = KEY_X,
    [RGFW_y] = KEY_Y,
    [RGFW_z] = KEY_Z,
    [RGFW_bracket] = KEY_LEFT_BRACKET,
    [RGFW_backSlash] = KEY_BACKSLASH,
    [RGFW_closeBracket] = KEY_RIGHT_BRACKET,
    [RGFW_semicolon] = KEY_SEMICOLON,
    [RGFW_insert] = KEY_INSERT,
    [RGFW_home] = KEY_HOME,
    [RGFW_pageUp] = KEY_PAGE_UP,
    [RGFW_delete] = KEY_DELETE,
    [RGFW_end] = KEY_END,
    [RGFW_pageDown] = KEY_PAGE_DOWN,
    [RGFW_right] = KEY_RIGHT,
    [RGFW_left] = KEY_LEFT,
    [RGFW_down] = KEY_DOWN,
    [RGFW_up] = KEY_UP,
    [RGFW_numLock] = KEY_NUM_LOCK,
    [RGFW_KP_Slash] = KEY_KP_DIVIDE,
    [RGFW_multiply] = KEY_KP_MULTIPLY,
    [RGFW_KP_Minus] = KEY_KP_SUBTRACT,
    [RGFW_KP_Return] = KEY_KP_ENTER,
    [RGFW_KP_1] = KEY_KP_1,
    [RGFW_KP_2] = KEY_KP_2,
    [RGFW_KP_3] = KEY_KP_3,
    [RGFW_KP_4] = KEY_KP_4,
    [RGFW_KP_5] = KEY_KP_5,
    [RGFW_KP_6] = KEY_KP_6,
    [RGFW_KP_7] = KEY_KP_7,
    [RGFW_KP_8] = KEY_KP_8,
    [RGFW_KP_9] = KEY_KP_9,
    [RGFW_KP_0] = KEY_KP_0,
    [RGFW_KP_Period] = KEY_KP_DECIMAL,
    [RGFW_scrollLock] = KEY_SCROLL_LOCK,
};

//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
bool InitGraphicsDevice(void);   // Initialize graphics device

//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
// NOTE: Functions declaration is provided by raylib.h

//----------------------------------------------------------------------------------
// Module Functions Definition: Window and Graphics Device
//----------------------------------------------------------------------------------

// Check if application should close
bool WindowShouldClose(void)
{
    if (CORE.Window.shouldClose == false)
        CORE.Window.shouldClose = RGFW_window_shouldClose(platform.window);
    if (CORE.Window.ready) return CORE.Window.shouldClose;
    else return true;
}

// Toggle fullscreen mode
void ToggleFullscreen(void)
{
    if (!CORE.Window.fullscreen)
    {
        // Store previous window position (in case we exit fullscreen)
        CORE.Window.previousPosition = CORE.Window.position;
        CORE.Window.previousScreen = CORE.Window.screen;

        platform.mon = RGFW_window_getMonitor(platform.window);
        CORE.Window.fullscreen = true;
        CORE.Window.flags |= FLAG_FULLSCREEN_MODE;

        RGFW_monitor_scaleToWindow(platform.mon, platform.window);
        RGFW_window_setFullscreen(platform.window, 1);
    }
    else
    {
        CORE.Window.fullscreen = false;
        CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;

        if (platform.mon.mode.area.w)
        {
            RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
            RGFW_monitor_requestMode(monitor, platform.mon.mode, RGFW_monitorScale);

            platform.mon.mode.area.w = 0;
        }

        // we update the window position right away
        CORE.Window.position = CORE.Window.previousPosition;
        RGFW_window_setFullscreen(platform.window, 0);
        RGFW_window_move(platform.window, RGFW_POINT(CORE.Window.position.x, CORE.Window.position.y));
        RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
    }

    // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
    // NOTE: V-Sync can be enabled by graphic driver configuration
    if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
}

// Toggle borderless windowed mode
void ToggleBorderlessWindowed(void)
{
    if (CORE.Window.fullscreen)
    {
        CORE.Window.previousPosition = CORE.Window.position;
        CORE.Window.previousScreen = CORE.Window.screen;

        RGFW_window_setBorder(platform.window, 0);

        RGFW_monitor mon = RGFW_window_getMonitor(platform.window);
        RGFW_window_resize(platform.window, mon.mode.area);
    }
    else
    {
        RGFW_window_setBorder(platform.window, 1);

        CORE.Window.position = CORE.Window.previousPosition;
        RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
    }

    CORE.Window.fullscreen = !CORE.Window.fullscreen;
}

// Set window state: maximized, if resizable
void MaximizeWindow(void)
{
    RGFW_window_maximize(platform.window);
}

// Set window state: minimized
void MinimizeWindow(void)
{
    RGFW_window_minimize(platform.window);
}

// Restore window from being minimized/maximized
void RestoreWindow(void)
{
    if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);

    RGFW_window_restore(platform.window);
}

// Set window configuration state using flags
void SetWindowState(unsigned int flags)
{
    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");

    CORE.Window.flags |= flags;

    if (flags & FLAG_VSYNC_HINT)
    {
        RGFW_window_swapInterval(platform.window, 1);
    }
    if (flags & FLAG_FULLSCREEN_MODE)
    {
        if (!CORE.Window.fullscreen) ToggleFullscreen();
    }
    if (flags & FLAG_WINDOW_RESIZABLE)
    {
        RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
        RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
    }
    if (flags & FLAG_WINDOW_UNDECORATED)
    {
        RGFW_window_setBorder(platform.window, 0);
    }
    if (flags & FLAG_WINDOW_HIDDEN)
    {
        RGFW_window_hide(platform.window);
    }
    if (flags & FLAG_WINDOW_MINIMIZED)
    {
        RGFW_window_minimize(platform.window);
    }
    if (flags & FLAG_WINDOW_MAXIMIZED)
    {
        RGFW_window_maximize(platform.window);
    }
    if (flags & FLAG_WINDOW_UNFOCUSED)
    {
        CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
        platform.window->_flags &= ~RGFW_windowFocusOnShow;
        RGFW_window_setFlags(platform.window, platform.window->_flags);
    }
    if (flags & FLAG_WINDOW_TOPMOST)
    {
        RGFW_window_setFloating(platform.window, RGFW_TRUE);
    }
    if (flags & FLAG_WINDOW_ALWAYS_RUN)
    {
        CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
    }
    if (flags & FLAG_WINDOW_TRANSPARENT)
    {
        TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
    }
    if (flags & FLAG_WINDOW_HIGHDPI)
    {
        TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
    }
    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
    {
        RGFW_window_setMousePassthrough(platform.window, 1);
    }
    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
    {
        ToggleBorderlessWindowed();
    }
    if (flags & FLAG_MSAA_4X_HINT)
    {
        RGFW_setGLHint(RGFW_glSamples, 4);
    }
    if (flags & FLAG_INTERLACED_HINT)
    {
        TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
    }
}

// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
    CORE.Window.flags &= ~flags;

    if (flags & FLAG_VSYNC_HINT)
    {
        RGFW_window_swapInterval(platform.window, 0);
    }
    if (flags & FLAG_FULLSCREEN_MODE)
    {
        if (CORE.Window.fullscreen) ToggleFullscreen();
    }
    if (flags & FLAG_WINDOW_RESIZABLE)
    {
        RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
        RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
    }
    if (flags & FLAG_WINDOW_UNDECORATED)
    {
        RGFW_window_setBorder(platform.window, 1);
    }
    if (flags & FLAG_WINDOW_HIDDEN)
    {
        if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);

        RGFW_window_show(platform.window);
    }
    if (flags & FLAG_WINDOW_MINIMIZED)
    {
        if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);

        RGFW_window_restore(platform.window);
    }
    if (flags & FLAG_WINDOW_MAXIMIZED)
    {
        if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);

        RGFW_window_restore(platform.window);
    }
    if (flags & FLAG_WINDOW_UNFOCUSED)
    {
        RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow);
        CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
    }
    if (flags & FLAG_WINDOW_TOPMOST)
    {
        RGFW_window_setFloating(platform.window, RGFW_FALSE);
    }
    if (flags & FLAG_WINDOW_ALWAYS_RUN)
    {
        CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
    }
    if (flags & FLAG_WINDOW_TRANSPARENT)
    {
        TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
    }
    if (flags & FLAG_WINDOW_HIGHDPI)
    {
        TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
    }
    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
    {
        RGFW_window_setMousePassthrough(platform.window, 0);
    }
    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
    {
        if (CORE.Window.fullscreen) ToggleBorderlessWindowed();
    }
    if (flags & FLAG_MSAA_4X_HINT)
    {
        RGFW_setGLHint(RGFW_glSamples, 0);
    }
    if (flags & FLAG_INTERLACED_HINT)
    {
        TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
    }
}

int RGFW_formatToChannels(int format)
{
    switch (format)
    {
        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
        case PIXELFORMAT_UNCOMPRESSED_R16:           // 16 bpp (1 channel - half float)
        case PIXELFORMAT_UNCOMPRESSED_R32:           // 32 bpp (1 channel - float)
            return 1;
        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:    // 8*2 bpp (2 channels)
        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:        // 16 bpp
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8:        // 24 bpp
        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:      // 16 bpp (1 bit alpha)
        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:      // 16 bpp (4 bit alpha)
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:      // 32 bpp
            return 2;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32:     // 32*3 bpp (3 channels - float)
        case PIXELFORMAT_UNCOMPRESSED_R16G16B16:     // 16*3 bpp (3 channels - half float)
        case PIXELFORMAT_COMPRESSED_DXT1_RGB:        // 4 bpp (no alpha)
        case PIXELFORMAT_COMPRESSED_ETC1_RGB:        // 4 bpp
        case PIXELFORMAT_COMPRESSED_ETC2_RGB:        // 4 bpp
        case PIXELFORMAT_COMPRESSED_PVRT_RGB:        // 4 bpp
            return 3;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:  // 32*4 bpp (4 channels - float)
        case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:  // 16*4 bpp (4 channels - half float)
        case PIXELFORMAT_COMPRESSED_DXT1_RGBA:       // 4 bpp (1 bit alpha)
        case PIXELFORMAT_COMPRESSED_DXT3_RGBA:       // 8 bpp
        case PIXELFORMAT_COMPRESSED_DXT5_RGBA:       // 8 bpp
        case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:   // 8 bpp
        case PIXELFORMAT_COMPRESSED_PVRT_RGBA:       // 4 bpp
        case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA:   // 8 bpp
        case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA:   // 2 bpp
            return 4;
        default: return 4;
    }
}

// Set icon for window
void SetWindowIcon(Image image)
{
    RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), RGFW_formatToChannels(image.format));
}

// Set icon for window
void SetWindowIcons(Image *images, int count)
{
    if ((images == NULL) || (count <= 0))
    {
        RGFW_window_setIcon(platform.window, NULL, RGFW_AREA(0, 0), 0);
    }
    else
    {
        Image *bigIcon = NULL;
        Image *smallIcon = NULL;

        for (int i = 0; i < count; i++)
        {
            if ((bigIcon == NULL) || ((images[i].width > bigIcon->width) && (images[i].height > bigIcon->height))) bigIcon = &images[i];
            if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i];
        }

        if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), RGFW_formatToChannels(smallIcon->format), RGFW_iconWindow);
        if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), RGFW_formatToChannels(bigIcon->format), RGFW_iconTaskbar);
    }
}

// Set title for window
void SetWindowTitle(const char *title)
{
    RGFW_window_setName(platform.window, (char *)title);
    CORE.Window.title = title;
}

// Set window position on screen (windowed mode)
void SetWindowPosition(int x, int y)
{
    RGFW_window_move(platform.window, RGFW_POINT(x, y));
}

// Set monitor for the current window
void SetWindowMonitor(int monitor)
{
    RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]);
}

// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMinSize(int width, int height)
{
    RGFW_window_setMinSize(platform.window, RGFW_AREA(width, height));
    CORE.Window.screenMin.width = width;
    CORE.Window.screenMin.height = height;
}

// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMaxSize(int width, int height)
{
    RGFW_window_setMaxSize(platform.window, RGFW_AREA(width, height));
    CORE.Window.screenMax.width = width;
    CORE.Window.screenMax.height = height;
}

// Set window dimensions
void SetWindowSize(int width, int height)
{
    CORE.Window.screen.width = width;
    CORE.Window.screen.height = height;

    RGFW_window_resize(platform.window, RGFW_AREA(width, height));
}

// Set window opacity, value opacity is between 0.0 and 1.0
void SetWindowOpacity(float opacity)
{
    RGFW_window_setOpacity(platform.window, opacity);
}

// Set window focused
void SetWindowFocused(void)
{
    RGFW_window_focus(platform.window);
}

// Get native window handle
void *GetWindowHandle(void)
{
    if (platform.window == NULL) return NULL;
#ifdef RGFW_WASM
    return (void *)platform.window->src.ctx;
#else
    return (void *)platform.window->src.window;
#endif
}

// Get number of monitors
int GetMonitorCount(void)
{
    #define MAX_MONITORS_SUPPORTED 6

    int count = MAX_MONITORS_SUPPORTED;
    RGFW_monitor *mons = RGFW_getMonitors();

    for (int i = 0; i < 6; i++)
    {
        if (!mons[i].x && !mons[i].y && !mons[i].mode.area.w && mons[i].mode.area.h)
        {
            count = i;
            break;
        }
    }

    return count;
}

// Get current monitor where window is placed
int GetCurrentMonitor(void)
{
    RGFW_monitor *mons = RGFW_getMonitors();
    RGFW_monitor mon = { 0 };

    if (platform.window) mon = RGFW_window_getMonitor(platform.window);
    else mon = RGFW_getPrimaryMonitor();

    for (int i = 0; i < 6; i++)
    {
        if ((mons[i].x ==  mon.x) && (mons[i].y ==  mon.y)) return i;
    }

    return 0;
}

// Get selected monitor position
Vector2 GetMonitorPosition(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y };
}

// Get selected monitor width (currently used by monitor)
int GetMonitorWidth(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return mons[monitor].mode.area.w;
}

// Get selected monitor height (currently used by monitor)
int GetMonitorHeight(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return mons[monitor].mode.area.h;
}

// Get selected monitor physical width in millimetres
int GetMonitorPhysicalWidth(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return mons[monitor].physW;
}

// Get selected monitor physical height in millimetres
int GetMonitorPhysicalHeight(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return (int)mons[monitor].physH;
}

// Get selected monitor refresh rate
int GetMonitorRefreshRate(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return (int)mons[monitor].mode.refreshRate;
}

// Get the human-readable, UTF-8 encoded name of the selected monitor
const char *GetMonitorName(int monitor)
{
    RGFW_monitor *mons = RGFW_getMonitors();

    return mons[monitor].name;
}

// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
    if (platform.window == NULL) return (Vector2){ 0.0f, 0.0f };
    return (Vector2){ (float)platform.window->r.x, (float)platform.window->r.y };
}

// Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void)
{
    RGFW_monitor monitor = { 0 };

    if (platform.window) monitor = RGFW_window_getMonitor(platform.window);
    else monitor = RGFW_getPrimaryMonitor();

    return (Vector2){ monitor.scaleX, monitor.scaleX };
}

// Set clipboard text content
void SetClipboardText(const char *text)
{
    RGFW_writeClipboard(text, strlen(text));
}

// Get clipboard text content
// NOTE: returned string is allocated and freed by RGFW
const char *GetClipboardText(void)
{
    return RGFW_readClipboard(NULL);
}

#if defined(SUPPORT_CLIPBOARD_IMAGE)
#if defined(_WIN32)
    #define WIN32_CLIPBOARD_IMPLEMENTATION
    #define WINUSER_ALREADY_INCLUDED
    #define WINBASE_ALREADY_INCLUDED
    #define WINGDI_ALREADY_INCLUDED
    #include "../external/win32_clipboard.h"
#endif
#endif

// Get clipboard image
Image GetClipboardImage(void)
{
    Image image = { 0 };
    unsigned long long int dataSize = 0;
    void *fileData = NULL;

#if defined(SUPPORT_CLIPBOARD_IMAGE)
#if defined(_WIN32)
    int width = 0;
    int height = 0;
    fileData  = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);

    if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data");
    else image = LoadImageFromMemory(".bmp", fileData, dataSize);
#else
    TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS");
#endif
#endif // SUPPORT_CLIPBOARD_IMAGE

    return image;
}

// Show mouse cursor
void ShowCursor(void)
{
    RGFW_window_showMouse(platform.window, true);
    CORE.Input.Mouse.cursorHidden = false;
}

// Hides mouse cursor
void HideCursor(void)
{
    RGFW_window_showMouse(platform.window, false);
    CORE.Input.Mouse.cursorHidden = true;
}

// Enables cursor (unlock cursor)
void EnableCursor(void)
{
    RGFW_disableCursor = false;
    RGFW_window_mouseUnhold(platform.window);

    // Set cursor position in the middle
    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
    RGFW_window_showMouse(platform.window, true);
    CORE.Input.Mouse.cursorHidden = false;
}

// Disables cursor (lock cursor)
void DisableCursor(void)
{
    RGFW_disableCursor = true;
    RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
    HideCursor();
}

// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
    RGFW_window_swapBuffers(platform.window);
}

//----------------------------------------------------------------------------------
// Module Functions Definition: Misc
//----------------------------------------------------------------------------------

// Get elapsed time measure in seconds since InitTimer()
double GetTime(void)
{
    return RGFW_getTime();
}

// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// Ref: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
    // Security check to (partially) avoid malicious code on target platform
    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
    else
    {
        // TODO: Open URL implementation
    }
}

//----------------------------------------------------------------------------------
// Module Functions Definition: Inputs
//----------------------------------------------------------------------------------

// Set internal gamepad mappings
int SetGamepadMappings(const char *mappings)
{
    TRACELOG(LOG_WARNING, "SetGamepadMappings() unsupported on target platform");
    return 0;
}

// Set gamepad vibration
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
{
    TRACELOG(LOG_WARNING, "SetGamepadVibration() unsupported on target platform");
}

// Set mouse position XY
void SetMousePosition(int x, int y)
{
    RGFW_window_moveMouse(platform.window, RGFW_POINT(x, y));
    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}

// Set mouse cursor
void SetMouseCursor(int cursor)
{
    RGFW_window_setMouseStandard(platform.window, cursor);
}

// Get physical key name.
const char *GetKeyName(int key)
{
    TRACELOG(LOG_WARNING, "GetKeyName() unsupported on target platform");

    return "";
}

static KeyboardKey ConvertScancodeToKey(u32 keycode);

int RGFW_gpConvTable[18] = {
    [RGFW_gamepadY] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
    [RGFW_gamepadB] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
    [RGFW_gamepadA] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
    [RGFW_gamepadX] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
    [RGFW_gamepadL1] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
    [RGFW_gamepadR1] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
    [RGFW_gamepadL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
    [RGFW_gamepadR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
    [RGFW_gamepadSelect] = GAMEPAD_BUTTON_MIDDLE_LEFT,
    [RGFW_gamepadHome] = GAMEPAD_BUTTON_MIDDLE,
    [RGFW_gamepadStart] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
    [RGFW_gamepadUp] = GAMEPAD_BUTTON_LEFT_FACE_UP,
    [RGFW_gamepadRight] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
    [RGFW_gamepadDown] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
    [RGFW_gamepadLeft] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
    [RGFW_gamepadL3] = GAMEPAD_BUTTON_LEFT_THUMB,    
    [RGFW_gamepadR3] = GAMEPAD_BUTTON_RIGHT_THUMB,
};

// Register all input events
void PollInputEvents(void)
{
#if defined(SUPPORT_GESTURES_SYSTEM)
    // NOTE: Gestures update must be called every frame to reset gestures correctly
    // because ProcessGestureEvent() is just called on an event, not every frame
    UpdateGestures();
#endif
    
    // Reset keys/chars pressed registered
    CORE.Input.Keyboard.keyPressedQueueCount = 0;
    CORE.Input.Keyboard.charPressedQueueCount = 0;

    // Reset mouse wheel
    CORE.Input.Mouse.currentWheelMove.x = 0;
    CORE.Input.Mouse.currentWheelMove.y = 0;

    // Register previous mouse position

    // Reset last gamepad button/axis registered state
    for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
    {
        // Check if gamepad is available
        if (CORE.Input.Gamepad.ready[i])
        {
            // Register previous gamepad button states
            for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
            {
                CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
            }
        }
    }

    // Register previous touch states
    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];

    // Map touch position to mouse position for convenience
    CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;

    int touchAction = -1;       // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
    bool realTouch = false;     // Flag to differentiate real touch gestures from mouse ones

    // Register previous keys states
    // NOTE: Android supports up to 260 keys
    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
    {
        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
    }

    // Register previous mouse states
    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];

    // Poll input events for current platform
    //-----------------------------------------------------------------------------
    CORE.Window.resizedLastFrame = false;

    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
    if (platform.window->_flags & RGFW_HOLD_MOUSE)
    {
        CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
        CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
    }
    else
    {
        CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
    }

    if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)))
    {
        RGFW_window_eventWait(platform.window, 0); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state
        CORE.Time.previous = GetTime();
    }

    while (RGFW_window_checkEvent(platform.window))
    {
        RGFW_event *event = &platform.window->event;
        // All input events can be processed after polling

        switch (event->type)
        {
            case RGFW_mouseEnter: CORE.Input.Mouse.cursorOnScreen = true; break;
            case RGFW_mouseLeave: CORE.Input.Mouse.cursorOnScreen = false; break;
            case RGFW_quit:
                event->type = 0;
                CORE.Window.shouldClose = true;
                return;
            case RGFW_DND:      // Dropped file
            {
                for (int i = 0; i < event->droppedFilesCount; i++)
                {
                    if (CORE.Window.dropFileCount == 0)
                    {
                        // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files
                        // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed
                        // TODO: Pointers should probably be reallocated for any new file added...
                        CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *));

                        CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
                        strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]);
                        
                        CORE.Window.dropFileCount++;
                    }
                    else if (CORE.Window.dropFileCount < 1024)
                    {
                        CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
                        strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]);

                        CORE.Window.dropFileCount++;
                    }
                    else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!");
                }
            } break;

            // Window events are also polled (Minimized, maximized, close...)
            case RGFW_windowResized:
            {
                SetupViewport(platform.window->r.w, platform.window->r.h);

                // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
                if (IsWindowState(FLAG_WINDOW_HIGHDPI))
                {
                    CORE.Window.screen.width = (int)(platform.window->r.w / GetWindowScaleDPI().x);
                    CORE.Window.screen.height = (int)(platform.window->r.h / GetWindowScaleDPI().y);
                }
                else
                {
                    CORE.Window.screen.width = platform.window->r.w;
                    CORE.Window.screen.height = platform.window->r.h;
                }

                CORE.Window.currentFbo.width = platform.window->r.w;
                CORE.Window.currentFbo.height = platform.window->r.h;
                CORE.Window.resizedLastFrame = true;
            } break;
            case RGFW_windowMaximized:
            {
                CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;  // The window was maximized
            } break;
            case RGFW_windowMinimized:
            {
                CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;  // The window was iconified
            } break;
            case RGFW_windowRestored:
            {
                if (RGFW_window_isMaximized(platform.window))
                    CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;           // The window was restored
                if (RGFW_window_isMinimized(platform.window))
                    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;           // The window was restored
            } break;
            case RGFW_windowMoved:
            {
                CORE.Window.position.x = platform.window->r.x;
                CORE.Window.position.y = platform.window->r.x;
            } break;

            // Keyboard events
            case RGFW_keyPressed:
            {
                KeyboardKey key = ConvertScancodeToKey(event->key);
                if (key != KEY_NULL)
                {
                    // If key was up, add it to the key pressed queue
                    if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
                    {
                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
                        CORE.Input.Keyboard.keyPressedQueueCount++;
                    }

                    CORE.Input.Keyboard.currentKeyState[key] = 1;
                }

                // TODO: Put exitKey verification outside the switch?
                if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
                {
                    CORE.Window.shouldClose = true;
                }

                // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
                // Check if there is space available in the queue
                if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
                {
                    // Add character (codepoint) to the queue
                    CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = event->keyChar;
                    CORE.Input.Keyboard.charPressedQueueCount++;
                }
            } break;
            case RGFW_keyReleased:
            {
                KeyboardKey key = ConvertScancodeToKey(event->key);
                if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
            } break;

            // Check mouse events
            case RGFW_mouseButtonPressed:
            {
                if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
                {
                    CORE.Input.Mouse.currentWheelMove.y = event->scroll;
                    break;
                }
                else CORE.Input.Mouse.currentWheelMove.y = 0;

                int btn = event->button;
                if (btn == RGFW_mouseLeft) btn = 1;
                else if (btn == RGFW_mouseRight) btn = 2;
                else if (btn == RGFW_mouseMiddle) btn = 3;

                CORE.Input.Mouse.currentButtonState[btn - 1] = 1;
                CORE.Input.Touch.currentTouchState[btn - 1] = 1;

                touchAction = 1;
            } break;
            case RGFW_mouseButtonReleased:
            {
                if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
                {
                    CORE.Input.Mouse.currentWheelMove.y = event->scroll;
                    break;
                }
                else CORE.Input.Mouse.currentWheelMove.y = 0;

                int btn = event->button;
                if (btn == RGFW_mouseLeft) btn = 1;
                else if (btn == RGFW_mouseRight) btn = 2;
                else if (btn == RGFW_mouseMiddle) btn = 3;

                CORE.Input.Mouse.currentButtonState[btn - 1] = 0;
                CORE.Input.Touch.currentTouchState[btn - 1] = 0;

                touchAction = 0;
            } break;
            case RGFW_mousePosChanged:
            {
                if (platform.window->_flags & RGFW_HOLD_MOUSE)
                {
                    CORE.Input.Mouse.currentPosition.x += (float)event->vector.x;
                    CORE.Input.Mouse.currentPosition.y += (float)event->vector.y;
                }
                else
                {
                    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
                    CORE.Input.Mouse.currentPosition.x = (float)event->point.x;
                    CORE.Input.Mouse.currentPosition.y = (float)event->point.y;
                }

                CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
                touchAction = 2;
            } break;
            case RGFW_gamepadConnected:
            {
                CORE.Input.Gamepad.ready[platform.window->event.gamepad] = true;
                CORE.Input.Gamepad.axisCount[platform.window->event.gamepad] = platform.window->event.axisesCount;
                CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
                CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;

                strcpy(CORE.Input.Gamepad.name[platform.window->event.gamepad], RGFW_getGamepadName(platform.window, platform.window->event.gamepad));
            } break;
            case RGFW_gamepadDisconnected:
            {
                CORE.Input.Gamepad.ready[platform.window->event.gamepad] = false;
            } break;
            case RGFW_gamepadButtonPressed:
            {
                int button = RGFW_gpConvTable[event->button];

                if (button >= 0)
                {
                    CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 1;
                    CORE.Input.Gamepad.lastButtonPressed = button;
                }
            } break;
            case RGFW_gamepadButtonReleased:
            {
                int button = RGFW_gpConvTable[event->button];

                CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 0;
                if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
            } break;
            case RGFW_gamepadAxisMove:
            {
                int axis = -1;
                float value = 0;

                switch(event->whichAxis)
                {
                    case 0:
                    {
                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_X] = event->axis[0].x / 100.0f;
                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_Y] = event->axis[0].y / 100.0f;
                    } break;
                    case 1:
                    {
                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_X] = event->axis[1].x / 100.0f;
                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_Y] = event->axis[1].y / 100.0f;
                    } break;
                    case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER;
                    case 3:
                    {
                        if (axis == -1) axis = GAMEPAD_AXIS_RIGHT_TRIGGER;

                        int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
                        int pressed = (value > 0.1f);
                        CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = pressed;
                        
                        if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
                        else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
                    }
                    default: break;
                }
            } break;
            default: break;
        }

#if defined(SUPPORT_GESTURES_SYSTEM)
        if (touchAction > -1)
        {
            // Process mouse events as touches to be able to use mouse-gestures
            GestureEvent gestureEvent = { 0 };

            // Register touch actions
            gestureEvent.touchAction = touchAction;

            // Assign a pointer ID
            gestureEvent.pointId[0] = 0;

            // Register touch points count
            gestureEvent.pointCount = 1;

            // Register touch points position, only one point registered
            if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0];
            else gestureEvent.position[0] = GetMousePosition();

            // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
            gestureEvent.position[0].x /= (float)GetScreenWidth();
            gestureEvent.position[0].y /= (float)GetScreenHeight();

            // Gesture data is sent to gestures-system for processing
            ProcessGestureEvent(gestureEvent);

            touchAction = -1;
        }
#endif
    }
    //-----------------------------------------------------------------------------
}

//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------

// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
    // Initialize RGFW internal global state, only required systems
    unsigned int flags = RGFW_windowCenter | RGFW_windowAllowDND;

    // Check window creation flags
    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
    {
        CORE.Window.fullscreen = true;
        flags |= RGFW_windowFullscreen;
    }

    if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
    {
        CORE.Window.fullscreen = true;
        flags |= RGFW_windowedFullscreen;
    }

    if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_windowNoBorder;
    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_windowNoResize;
    if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_windowTransparent;
    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_windowFullscreen;
    if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) flags |= RGFW_windowHide;
    if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= RGFW_windowMaximize;

    // NOTE: Some OpenGL context attributes must be set before window creation
    // Check selection OpenGL version
    if (rlGetVersion() == RL_OPENGL_21)
    {
        RGFW_setGLHint(RGFW_glMajor, 2);
        RGFW_setGLHint(RGFW_glMinor, 1);
    }
    else if (rlGetVersion() == RL_OPENGL_33)
    {
        RGFW_setGLHint(RGFW_glMajor, 3);
        RGFW_setGLHint(RGFW_glMinor, 3);
    }
    else if (rlGetVersion() == RL_OPENGL_43)
    {
        RGFW_setGLHint(RGFW_glMajor, 4);
        RGFW_setGLHint(RGFW_glMinor, 3);
    }

    if (CORE.Window.flags & FLAG_MSAA_4X_HINT) RGFW_setGLHint(RGFW_glSamples, 4);

    if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) flags |= RGFW_windowFocusOnShow | RGFW_windowFocus;

    platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
    platform.mon.mode.area.w = 0;

#ifndef PLATFORM_WEB_RGFW
    RGFW_area screenSize = RGFW_getScreenSize();
    CORE.Window.display.width = screenSize.w;
    CORE.Window.display.height = screenSize.h;
#else
    CORE.Window.display.width = CORE.Window.screen.width;
    CORE.Window.display.height = CORE.Window.screen.height;
#endif
    // TODO: Is this needed by raylib now?
    // If so, rcore_desktop_sdl should be updated too
    //SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
    
    if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
    RGFW_window_makeCurrent(platform.window);

    // Check surface and context activation
    if (platform.window != NULL)
    {
        CORE.Window.ready = true;

        CORE.Window.render.width = CORE.Window.screen.width;
        CORE.Window.render.height = CORE.Window.screen.height;
        CORE.Window.currentFbo.width = CORE.Window.render.width;
        CORE.Window.currentFbo.height = CORE.Window.render.height;
    }
    else
    {
        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
        return -1;
    }
    //----------------------------------------------------------------------------

    // If everything work as expected, we can continue
    CORE.Window.position.x = platform.window->r.x;
    CORE.Window.position.y = platform.window->r.y;
    CORE.Window.render.width = CORE.Window.screen.width;
    CORE.Window.render.height = CORE.Window.screen.height;
    CORE.Window.currentFbo.width = CORE.Window.render.width;
    CORE.Window.currentFbo.height = CORE.Window.render.height;

    TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
    TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
    TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
    TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
    TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);

    // Load OpenGL extensions
    // NOTE: GL procedures address loader is required to load extensions
    //----------------------------------------------------------------------------
    rlLoadExtensions((void *)RGFW_getProcAddress);
    //----------------------------------------------------------------------------

    // Initialize timing system
    //----------------------------------------------------------------------------
    InitTimer();
    //----------------------------------------------------------------------------

    // Initialize storage system
    //----------------------------------------------------------------------------
    CORE.Storage.basePath = GetWorkingDirectory();
    //----------------------------------------------------------------------------

#if defined(RGFW_WAYLAND)
    if (RGFW_useWaylandBool) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully");
    else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully");
#elif defined(RGFW_X11)
    #if defined(__APPLE__)
        TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (MacOS)): Initialized successfully");
    #else
        TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully");
    #endif
#elif defined (RGFW_WINDOWS)
    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully");
#elif defined(RGFW_WASM)
    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully");
#elif defined(RGFW_MACOS)
    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully");
#endif

    return 0;
}

// Close platform
void ClosePlatform(void)
{
    RGFW_window_close(platform.window);
}

// Keycode mapping
static KeyboardKey ConvertScancodeToKey(u32 keycode)
{
    if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return 0;

    return keyMappingRGFW[keycode];
}
