/*
  WebUI Library
  https://webui.me
  https://github.com/webui-dev/webui
  Copyright (c) 2020-2024 Hassan Draga.
  Licensed under MIT License.
  All rights reserved.
  Canada.
*/

// 64Mb max dynamic memory allocation
#define WEBUI_MAX_BUF (64000000)

// -- Includes ------------------------
#include "../bridge/webui_bridge.h" // WebUI Bridge (JavaScript)
#include "webui.h"                  // WebUI Header

// -- WebView -------------------------
#ifdef _WIN32
    #include "webview/WebView2.h"
#elif __linux__
    #include <dlfcn.h>
#else
    // ...
#endif

// -- Third-party ---------------------
#ifdef WEBUI_TLS
    #ifdef _WIN32
        #ifndef WIN32_LEAN_AND_MEAN
            #define WIN32_LEAN_AND_MEAN
        #endif
    #endif
    // OpenSSL
    #include <openssl/bio.h>
    #include <openssl/evp.h>
    #include <openssl/pem.h>
    #include <openssl/rsa.h>
    #include <openssl/ssl.h>
    #include <openssl/x509.h>
    #ifdef WEBUI_LOG
        #include <openssl/err.h>
    #endif
#endif
#define MG_BUF_LEN (WEBUI_MAX_BUF)
#include "civetweb/civetweb.h"

// -- Disable Non-critical warnings ---
#ifdef _MSC_VER
    #pragma warning(push, 0)
    // Disable spectre warnings
    #pragma warning(disable: 5045)
#elif defined(__clang__)
    // ...
#elif defined(__GNUC__)
    // ...
#endif

// -- Defines -------------------------
#define WEBUI_SIGNATURE      0xDD    // All packets should start with this 8bit
#define WEBUI_CMD_JS         0xFE    // Command: JavaScript result in frontend
#define WEBUI_CMD_JS_QUICK   0xFD    // Command: JavaScript result in frontend
#define WEBUI_CMD_CLICK      0xFC    // Command: Click event
#define WEBUI_CMD_NAVIGATION 0xFB    // Command: Frontend navigation
#define WEBUI_CMD_CLOSE      0xFA    // Command: Close window
#define WEBUI_CMD_CALL_FUNC  0xF9    // Command: Backend function call
#define WEBUI_CMD_SEND_RAW   0xF8    // Command: Send raw binary data to the UI
#define WEBUI_CMD_ADD_ID     0xF7    // Command: Add new bind ID
#define WEBUI_CMD_MULTI      0xF6    // Command: Multi packet data
#define WEBUI_CMD_CHECK_TK   0xF5    // Command: Check validity of a client's token
#define WEBUI_PROTOCOL_SIZE  (8)     // Protocol header size in bytes
#define WEBUI_PROTOCOL_SIGN  (0)     // Protocol byte position: Signature (1 Byte)
#define WEBUI_PROTOCOL_TOKEN (1)     // Protocol byte position: Token (4 Bytes)
#define WEBUI_PROTOCOL_ID    (5)     // Protocol byte position: ID (2 Bytes)
#define WEBUI_PROTOCOL_CMD   (7)     // Protocol byte position: Command (1 Byte)
#define WEBUI_PROTOCOL_DATA  (8)     // Protocol byte position: Data (n Byte)
#define WEBUI_MUTEX_NONE     (0)     // Check boolen mutex without update
#define WEBUI_MUTEX_TRUE     (1)     // Check boolen mutex and update to true
#define WEBUI_MUTEX_FALSE    (2)     // Check boolen mutex and update to false
#define WEBUI_WS_DATA        (1)     // Internal WS Event (Data received)
#define WEBUI_WS_OPEN        (2)     // Internal WS Event (New connection)
#define WEBUI_WS_CLOSE       (3)     // Internal WS Event (Connection close)
#define WEBUI_SHOW_HTML      (1)     // Show window using HTML
#define WEBUI_SHOW_FILE      (2)     // Show window using a local file
#define WEBUI_SHOW_URL       (3)     // Show window using a URL
#define WEBUI_SHOW_FOLDER    (4)     // Show window using a Folder
#define WEBUI_MIN_PORT       (10000) // Minimum socket port
#define WEBUI_MAX_PORT       (65500) // Should be less than 65535
#define WEBUI_STDOUT_BUF     (10240) // Command STDOUT output buffer size
#define WEBUI_DEFAULT_PATH   "."     // Default root path
#define WEBUI_DEF_TIMEOUT    (15)    // Default startup timeout in seconds
#define WEBUI_RELOAD_TIMEOUT (1500)  // Default reload page timeout in milliseconds
#define WEBUI_MAX_TIMEOUT    (60)    // Maximum startup timeout in seconds the user can set
#define WEBUI_MIN_WIDTH      (100)   // Minimal window width
#define WEBUI_MIN_HEIGHT     (100)   // Minimal window height
#define WEBUI_MAX_WIDTH      (3840)  // Maximal window width (4K Monitor)
#define WEBUI_MAX_HEIGHT     (2160)  // Maximal window height (4K Monitor)
#define WEBUI_MIN_X          (0)     // Minimal window X
#define WEBUI_MIN_Y          (0)     // Minimal window Y
#define WEBUI_MAX_X          (3000)  // Maximal window X (4K Monitor)
#define WEBUI_MAX_Y          (1800)  // Maximal window Y (4K Monitor)
#define WEBUI_PROFILE_NAME   "WebUI" // Default browser profile name (Used only for Firefox)
#define WEBUI_COOKIES_LEN    (32)    // Authentification cookies len
#define WEBUI_COOKIES_BUF    (64)    // Authentification cookies buffer size

#ifdef WEBUI_TLS
#define WEBUI_SECURE         "TLS-Encryption"
#define WEBUI_SSL_SIZE       (4096)         // SSL Max PEM Size
#define WEBUI_SSL_EXPIRE     (72 * 60 * 60) // SSL Expires (Integer)
#define WEBUI_SSL_EXPIRE_STR "259201"       // SSL Expires (String)
#define WEBUI_HTTP_PROTOCOL  "https://"
#define WEBUI_WS_PROTOCOL    "wss://"
#else
#define WEBUI_SECURE        "Non-Encrypted"
#define WEBUI_HTTP_PROTOCOL "http://"
#define WEBUI_WS_PROTOCOL   "ws://"
#endif

#ifdef WEBUI_DYNAMIC
#define WEBUI_LIB_TYPE "Dynamic"
#else
#define WEBUI_LIB_TYPE "Static"
#endif

#ifdef _WIN32
#define WEBUI_OS "Microsoft Windows"
#elif __APPLE__
#define WEBUI_OS "Apple macOS"
#else
#define WEBUI_OS "GNU/Linux"
#endif

// Mutex
#ifdef _WIN32
typedef CRITICAL_SECTION webui_mutex_t;
typedef CONDITION_VARIABLE webui_condition_t;
#else
typedef pthread_mutex_t webui_mutex_t;
typedef pthread_cond_t webui_condition_t;
#endif

// Compiler
#if defined(_MSC_VER)
    #define WEBUI_COMPILER "MSVC"
#elif defined(__GNUC__) && !defined(__clang__)
    #define WEBUI_COMPILER "GCC"
#elif defined(__clang__)
    #define WEBUI_COMPILER "Clang"
#else
    #define WEBUI_COMPILER "Unknown"
#endif

// Verbose Log
#ifdef WEBUI_LOG
    #define WEBUI_LOG_VERBOSE
#endif

// Timer
typedef struct _webui_timer_t {
    struct timespec start;
    struct timespec now;
} _webui_timer_t;

// Event data
typedef struct webui_event_inf_t {
    char* event_data[WEBUI_MAX_ARG + 1]; // Event data (string | num | bool | raw)
    size_t event_size[WEBUI_MAX_ARG + 1]; // Event data size (in bytes)
    char* response; // Event response (string)
    size_t count; // Event arguments count
} webui_event_inf_t;

// WebView
#ifdef _WIN32
    typedef struct _webui_wv_win32_t {
        // Win32 WebView
        ICoreWebView2Environment* webviewEnvironment;
        ICoreWebView2Controller* webviewController;
        ICoreWebView2* webviewWindow;
        HWND hwnd;
        void* titleChangedHandler;
        void* titleChangedHandler_lpVtbl;
        void* createWebViewEnvironmentHandler;
        void* createWebViewEnvironmentHandler_lpVtbl;
        void* createWebViewControllerHandler;
        void* createWebViewControllerHandler_lpVtbl;
        // WebUI Window
        wchar_t* url;
        bool navigate;
        bool size;
        bool position;
        int width;
        int height;
        int x;
        int y;
        bool stop;
    } _webui_wv_win32_t;
#elif __linux__
    void* libgtk;
    void* libwebkit;
    // GTK Symbol Addresses
    typedef void *(*gtk_init_func)(int *argc, char ***argv);
    typedef void (*gtk_widget_show_all_func)(void *);
    typedef void (*gtk_main_iteration_do_func)(int);
    typedef int (*gtk_events_pending_func)(void);
    typedef void (*gtk_container_add_func)(void *, void *);
    typedef void *(*gtk_window_new_func)(int);
    typedef void (*gtk_window_set_default_size_func)(void *, int, int);
    typedef void (*gtk_window_set_title_func)(void *, const char *);
    typedef void (*gtk_window_move_func)(void *, int, int);
    typedef void (*gtk_window_close_func)(void *);
    typedef void (*gtk_window_resize_func)(void *, int, int);
    typedef void (*gtk_window_set_position_func)(void *, int);
    typedef int (*g_idle_add_func)(int (*function)(void*), void*);
    typedef void (*g_signal_connect_data_func)(void *, const char *,
        void (*callback)(void), void *, void *, int);
    gtk_init_func gtk_init = NULL;
    gtk_widget_show_all_func gtk_widget_show_all = NULL;
    gtk_main_iteration_do_func gtk_main_iteration_do = NULL;
    gtk_events_pending_func gtk_events_pending = NULL;
    gtk_container_add_func gtk_container_add = NULL;
    gtk_window_new_func gtk_window_new = NULL;
    gtk_window_set_default_size_func gtk_window_set_default_size = NULL;
    gtk_window_set_title_func gtk_window_set_title = NULL;
    gtk_window_move_func gtk_window_move = NULL;
    gtk_window_close_func gtk_window_close = NULL;
    gtk_window_resize_func gtk_window_resize = NULL;
    gtk_window_set_position_func gtk_window_set_position = NULL;
    g_signal_connect_data_func g_signal_connect_data = NULL;
    g_idle_add_func g_idle_add = NULL;
    // WebKit Symbol Addresses
    typedef void *(*webkit_web_view_new_func)(void);
    typedef void (*webkit_web_view_load_uri_func)(void *, const char *);
    typedef const char *(*webkit_web_view_get_title_func)(void *);
    webkit_web_view_new_func webkit_web_view_new = NULL;
    webkit_web_view_load_uri_func webkit_web_view_load_uri = NULL;
    webkit_web_view_get_title_func webkit_web_view_get_title = NULL;
    
    typedef struct _webui_wv_linux_t {
        // Linux WebView
        void* gtk_win;
        void* gtk_wv;
        bool open;
        // WebUI Window
        char* url;
        bool navigate;
        bool size;
        bool position;
        unsigned int width;
        unsigned int height;
        unsigned int x;
        unsigned int y;
        bool stop;
    } _webui_wv_linux_t;
#else
    extern bool _webui_macos_wv_new(int index);
    extern bool _webui_macos_wv_show(int index, const char* urlString, int x, int y, int width, int height);
    extern bool _webui_macos_wv_close(int index);
    extern bool _webui_macos_wv_set_position(int index, int x, int y);
    extern bool _webui_macos_wv_set_size(int index, int width, int height);
    extern bool _webui_macos_wv_navigate(int index, const char* urlString);
    extern void _webui_macos_wv_process();
    extern void _webui_macos_wv_stop();
    extern void _webui_macos_wv_set_close_cb(void (*cb)(int index));
    extern void _webui_macos_wv_new_thread_safe(int index);

    typedef struct _webui_wv_macos_t {
        // macOS WebView
        int index;
        // WebUI Window
        char* url;
        bool navigate;
        bool size;
        bool position;
        unsigned int width;
        unsigned int height;
        unsigned int x;
        unsigned int y;
        bool stop;
    } _webui_wv_macos_t;
#endif

// Window
typedef struct _webui_window_t {
    // Client
    size_t clients_count;
    struct mg_connection* single_client; // Single client
    bool single_client_token_check;
    // Server
    bool wait; // Let server thread wait more time for websocket
    bool server_running; // Slow check
    bool connected; // Fast check
    size_t server_port;
    char* url;
    const char* html;
    char* server_root_path;
    #ifdef _WIN32
    HANDLE server_thread;
    #else
    pthread_t server_thread;
    #endif
    // Window
    uint32_t token;
    size_t num; // Window number
    const char* html_elements[WEBUI_MAX_IDS];
    bool has_all_events;
    void(*cb[WEBUI_MAX_IDS])(webui_event_t* e);
    void(*cb_interface[WEBUI_MAX_IDS])(size_t, size_t, char* , size_t, size_t);
    bool ws_block;
    bool is_embedded_html;
    bool is_closed;
    size_t custom_server_port;
    const char* icon;
    const char* icon_type;
    size_t current_browser;
    char* browser_path;
    bool custom_profile;
    bool default_profile;
    char* profile_path;
    char* profile_name;
    size_t runtime;
    bool kiosk_mode;
    bool disable_browser_high_contrast;
    bool hide;
    int width;
    int height;
    bool size_set;
    int minimum_width;
    int minimum_height;
    bool minimum_size_set;
    int x;
    int y;
    bool position_set;
    size_t process_id;
    const void*(*files_handler)(const char* filename, int* length);
    const void*(*files_handler_window)(size_t window, const char* filename, int* length);
    webui_event_inf_t* events[WEBUI_MAX_IDS];
    size_t events_count;
    bool is_public;
    bool proxy_set;
    char *proxy_server;
    // WebView
    bool allow_webview;
    bool update_webview;
    #ifdef _WIN32
    _webui_wv_win32_t* webView;
    #elif __linux__
    _webui_wv_linux_t* webView;
    #else
    _webui_wv_macos_t* webView;
    #endif
}
_webui_window_t;

// Core
typedef struct _webui_core_t {
    struct {
        bool show_wait_connection;
        bool show_auto_js_inject;
        bool ws_block;
        bool folder_monitor;
        bool multi_client;
        bool use_cookies;
    } config;
    struct mg_connection* clients[WEBUI_MAX_IDS];
    size_t clients_win_num[WEBUI_MAX_IDS];
    bool clients_token_check[WEBUI_MAX_IDS];
    char* cookies[WEBUI_MAX_IDS];
    bool cookies_single_set[WEBUI_MAX_IDS];
    size_t servers;
    size_t used_ports[WEBUI_MAX_IDS];
    size_t startup_timeout;
    size_t cb_count;
    bool exit_now;
    bool run_done[WEBUI_MAX_IDS]; // 2 Bytes ID
    char* run_userBuffer[WEBUI_MAX_IDS];
    size_t run_userBufferLen[WEBUI_MAX_IDS];
    bool run_error[WEBUI_MAX_IDS];
    uint16_t run_last_id;
    bool initialized;
    char* executable_path;
    void * ptr_list[WEBUI_MAX_IDS * 2];
    size_t ptr_position;
    size_t ptr_size[WEBUI_MAX_IDS * 2];
    size_t current_browser;
    _webui_window_t* wins[WEBUI_MAX_IDS];
    bool wins_reserved[WEBUI_MAX_IDS];
    webui_mutex_t mutex_server_start;
    webui_mutex_t mutex_send;
    webui_mutex_t mutex_receive;
    webui_mutex_t mutex_wait;
    webui_mutex_t mutex_bridge;
    webui_mutex_t mutex_js_run;
    webui_mutex_t mutex_win_connect;
    webui_mutex_t mutex_exit_now;
    webui_mutex_t mutex_webview_stop;
    webui_mutex_t mutex_http_handler;
    webui_mutex_t mutex_client;
    webui_condition_t condition_wait;
    char* default_server_root_path;
    bool ui;
    #ifdef WEBUI_TLS
    char* root_cert;
    char* root_key;
    char* ssl_cert;
    char* ssl_key;
    #endif
    // WebView
    bool is_browser_main_run;
    bool is_webview;
    #ifdef _WIN32
    char* webview_cacheFolder;
    HMODULE webviewLib;
    #elif __linux__
    bool is_gtk_main_run;
    #else
    bool is_wkwebview_main_run;
    #endif
}
_webui_core_t;

typedef struct _webui_cb_arg_t {
    // Event
    _webui_window_t* window;
    size_t event_type;
    char* element;
    char* data;
    size_t event_number;
}
_webui_cb_arg_t;

typedef struct _webui_recv_arg_t {
    _webui_window_t* win;
    void * ptr;
    size_t len;
    size_t recvNum;
    int event_type;
    struct mg_connection* client;
    size_t connection_id;
}
_webui_recv_arg_t;

typedef struct _webui_cmd_async_t {
    _webui_window_t* win;
    char* cmd;
}
_webui_cmd_async_t;

// -- Definitions ---------------------
#ifdef _WIN32
static const char* os_sep = "\\";
static DWORD WINAPI _webui_run_browser_task(LPVOID _arg);
static int _webui_system_win32(_webui_window_t* win, char* cmd, bool show);
static int _webui_system_win32_out(const char* cmd, char ** output, bool show);
static bool _webui_socket_test_listen_win32(size_t port_num);
static bool _webui_get_windows_reg_value(HKEY key, LPCWSTR reg, LPCWSTR value_name, char value[WEBUI_MAX_PATH]);
static bool _webui_str_to_wide(const char *s, wchar_t **w);
#define WEBUI_THREAD_SERVER_START DWORD WINAPI _webui_server_thread(LPVOID arg)
#define WEBUI_THREAD_RECEIVE DWORD WINAPI _webui_ws_process_thread(LPVOID _arg)
#define WEBUI_THREAD_WEBVIEW DWORD WINAPI _webui_webview_thread(LPVOID arg)
#define WEBUI_THREAD_MONITOR DWORD WINAPI _webui_folder_monitor_thread(LPVOID arg)
#define WEBUI_THREAD_RETURN return 0;
#else
static const char* os_sep = "/";
static void * _webui_run_browser_task(void * _arg);
#define WEBUI_THREAD_SERVER_START void * _webui_server_thread(void * arg)
#define WEBUI_THREAD_RECEIVE void * _webui_ws_process_thread(void * _arg)
#define WEBUI_THREAD_WEBVIEW void * _webui_webview_thread(void * arg)
#define WEBUI_THREAD_MONITOR void * _webui_folder_monitor_thread(void * arg)
#define WEBUI_THREAD_RETURN pthread_exit(NULL);
#endif
static void _webui_init(void);
static bool _webui_show(_webui_window_t* win, struct mg_connection* client, const char* content, size_t browser);
static bool _webui_get_cb_index(_webui_window_t* win, const char* element, size_t* id);
static size_t _webui_get_free_port(void); 
static void _webui_free_port(size_t port);
static char* _webui_get_current_path(void);
static void _webui_send_client_ws(_webui_window_t* win, struct mg_connection* client,
    size_t connection_id, char* packet, size_t packets_size);
static void _webui_window_event(
    _webui_window_t* win, size_t connection_id, int event_type, char* element, size_t event_number,
    size_t client_id, const char* cookies
);
static int _webui_cmd_sync(_webui_window_t* win, char* cmd, bool show);
static int _webui_cmd_async(_webui_window_t* win, char* cmd, bool show);
static int _webui_run_browser(_webui_window_t* win, char* cmd);
static void _webui_clean(void);
static bool _webui_browser_exist(_webui_window_t* win, size_t browser);
static const char* _webui_get_temp_path();
static bool _webui_folder_exist(const char* folder);
static void _webui_delete_folder(char* folder);
static bool _webui_browser_create_new_profile(_webui_window_t* win, size_t browser);
static bool _webui_browser_start_chrome(_webui_window_t* win, const char* address);
static bool _webui_browser_start_edge(_webui_window_t* win, const char* address);
static bool _webui_browser_start_epic(_webui_window_t* win, const char* address);
static bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address);
static bool _webui_browser_start_brave(_webui_window_t* win, const char* address);
static bool _webui_browser_start_firefox(_webui_window_t* win, const char* address);
static bool _webui_browser_start_yandex(_webui_window_t* win, const char* address);
static bool _webui_browser_start_chromium(_webui_window_t* win, const char* address);
static bool _webui_browser_start(_webui_window_t* win, const char* address, size_t _browser);
static long _webui_timer_diff(struct timespec * start, struct timespec* end);
static void _webui_timer_start(_webui_timer_t* t);
static bool _webui_timer_is_end(_webui_timer_t* t, size_t ms);
static void _webui_timer_clock_gettime(struct timespec * spec);
static bool _webui_set_root_folder(_webui_window_t* win, const char* path);
static const char* _webui_generate_js_bridge(_webui_window_t* win, struct mg_connection* client);
static void _webui_free_mem(void * ptr);
static size_t _webui_mb(size_t size);
static bool _webui_file_exist_mg(_webui_window_t* win, struct mg_connection* client);
static bool _webui_file_exist(const char* path);
static void _webui_free_all_mem(void);
static bool _webui_show_window(_webui_window_t* win, struct mg_connection* client,
    const char* content, int type, size_t browser);
static bool _webui_is_empty(const char* s);
static size_t _webui_strlen(const char* s);
static uint16_t _webui_get_run_id(void);
static void * _webui_malloc(size_t size);
static void _webui_sleep(long unsigned int ms);
static size_t _webui_find_the_best_browser(_webui_window_t* win);
static bool _webui_is_process_running(const char* process_name);
static void _webui_panic(char* msg);
static void _webui_kill_pid(size_t pid);
static _webui_window_t* _webui_dereference_win_ptr(void * ptr);
static int _webui_get_browser_args(_webui_window_t* win, size_t browser, char* buffer, size_t len);
static void _webui_mutex_init(webui_mutex_t* mutex);
static void _webui_mutex_lock(webui_mutex_t* mutex);
static void _webui_mutex_unlock(webui_mutex_t* mutex);
static void _webui_mutex_destroy(webui_mutex_t* mutex);
static bool _webui_mutex_is_connected(_webui_window_t* win, int update);
static bool _webui_mutex_is_exit_now(int update);
static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update);
static void _webui_condition_init(webui_condition_t* cond);
static void _webui_condition_wait(webui_condition_t* cond, webui_mutex_t* mutex);
static void _webui_condition_signal(webui_condition_t* cond);
static void _webui_condition_destroy(webui_condition_t* cond);
static void _webui_http_send(_webui_window_t* win, struct mg_connection* client,
    const char* mime_type, const char* body, size_t body_len, bool cache);
static void _webui_http_send_file(_webui_window_t* win, struct mg_connection* client,
    const char* mime_type, const char* path, bool cache);
static void _webui_http_send_header(_webui_window_t* win, struct mg_connection* client,
    const char* mime_type, size_t body_len, bool cache);
static void _webui_http_send_error(struct mg_connection* client, const char* body, int status);
static int _webui_http_handler(struct mg_connection* client, void * _win);
static int _webui_ws_connect_handler(const struct mg_connection* client, void * _win);
static void _webui_ws_ready_handler(struct mg_connection* client, void * _win);
static int _webui_ws_data_handler(struct mg_connection* client, int opcode, char* data, size_t datasize, void * _win);
static void _webui_ws_close_handler(const struct mg_connection* client, void * _win);
static void _webui_receive(_webui_window_t* win, struct mg_connection* client, int event_type, void * data, size_t len);
static void _webui_ws_process(_webui_window_t* win, struct mg_connection* client, size_t connection_id, 
    void* ptr, size_t len, size_t recvNum, int event_type);
static bool _webui_connection_save(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
static bool _webui_connection_get_id(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
static void _webui_connection_remove(_webui_window_t* win, struct mg_connection* client);
static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name);
static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name);
static void _webui_send_client(_webui_window_t* win, struct mg_connection *client, 
    uint16_t id, unsigned char cmd, const char* data, size_t len, bool token_bypass);
static void _webui_send_all(_webui_window_t* win, uint16_t id, unsigned char cmd, const char* data, size_t len);
static uint16_t _webui_get_id(const char* data);
static uint32_t _webui_get_token(const char* data);
static uint32_t _webui_generate_random_uint32();
static const char* _webui_url_encode(const char* str);
static bool _webui_open_url_native(const char* url);
static bool _webui_is_valid_url(const char* url);
static bool _webui_port_is_used(size_t port_num);
static char* _webui_str_dup(const char* src);
static void _webui_bridge_api_handler(webui_event_t* e);
// static size_t _webui_hash_djb2(const char* s);
static size_t _webui_new_event_inf(_webui_window_t* win, webui_event_inf_t** event_inf);
static void _webui_free_event_inf(_webui_window_t* win, size_t event_num);
static const char* _webui_get_cookies_full(const struct mg_connection* client);
static void _webui_get_cookies(const struct mg_connection* client, char* buffer);
static bool _webui_client_cookies_save(_webui_window_t* win, const char* cookies, size_t* client_id);
static bool _webui_client_cookies_get_id(_webui_window_t* win, const char* cookies, size_t* client_id);
// static void _webui_client_cookies_free_all(_webui_window_t* win);
// static void _webui_client_cookies_free(_webui_window_t* win, struct mg_connection* client);
static size_t _webui_client_get_id(_webui_window_t* win, struct mg_connection* client);
static void _webui_generate_cookies(char* cookies, size_t length);
static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client, size_t client_id);
static int _webui_external_file_handler(_webui_window_t* win, struct mg_connection* client, size_t client_id);
static int _webui_interpret_file(_webui_window_t* win, struct mg_connection* client, char* index, size_t client_id);
// WebView
#ifdef _WIN32
// Microsoft Windows
static void _webui_wv_free(_webui_wv_win32_t* webView);
static void _webui_wv_close(_webui_wv_win32_t *webView);
static bool _webui_wv_navigate(_webui_wv_win32_t* webView, wchar_t* url);
static bool _webui_wv_set_position(_webui_wv_win32_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_win32_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(_webui_window_t* win);
#elif __linux__
// Linux
static void _webui_wv_free();
static void _webui_wv_close(_webui_wv_linux_t *webView);
static bool _webui_wv_navigate(_webui_wv_linux_t* webView, char* url);
static bool _webui_wv_set_position(_webui_wv_linux_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_linux_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(void *widget, void *arg);
#else
// macOS
static void _webui_wv_free(_webui_wv_macos_t* webView);
static void _webui_wv_close(_webui_wv_macos_t *webView);
static bool _webui_wv_navigate(_webui_wv_macos_t* webView, char* url);
static bool _webui_wv_set_position(_webui_wv_macos_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_macos_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(int index);
#endif

#ifdef WEBUI_TLS
static int _webui_tls_initialization(void * ssl_ctx, void * ptr);
static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key, char* ssl_cert, char* ssl_key);
static bool _webui_check_certificate(const char* certificate_pem, const char* private_key_pem);
#endif
#ifdef WEBUI_LOG
static void _webui_print_hex(const char* data, size_t len);
static void _webui_print_ascii(const char* data, size_t len);
static int _webui_http_log(const struct mg_connection* client, const char* message);
#endif
static WEBUI_THREAD_SERVER_START;
static WEBUI_THREAD_RECEIVE;
static WEBUI_THREAD_WEBVIEW;
static WEBUI_THREAD_MONITOR;

// Safe C STD
#ifdef _WIN32
#define WEBUI_STR_TOK(str, delim, context) strtok_s(str, delim, context)
#define WEBUI_FILE_OPEN(file, filename, mode) fopen_s(&file, filename, mode)
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, _webui_mb(buffer_size), format, ##__VA_ARGS__)
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strcpy_s(dest, _webui_mb(dest_size), src)
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strcpy_s(dest, dest_size, src)
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strcat_s(dest, _webui_mb(dest_size), src)
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strcat_s(dest, dest_size, src)
#else
#define WEBUI_STR_TOK(str, delim, context) strtok_r(str, delim, context)
#define WEBUI_FILE_OPEN(file, filename, mode) ((file) = fopen(filename, mode))
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, _webui_mb(buffer_size), format, ##__VA_ARGS__)
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strncpy(dest, src, _webui_mb(dest_size))
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strncpy(dest, src, dest_size)
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strncat(dest, src, _webui_mb(dest_size))
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strncat(dest, src, dest_size)
#endif

// Assert
#define WEBUI_ASSERT(s) _webui_panic(s);assert(0 && s);

// -- Heap ----------------------------
static _webui_core_t _webui;
static const char* webui_html_served = "<html><head><title>Access Denied</title><script src=\"/webui.js\"></script><style>"
"body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-image:linear-"
"gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}</style>"
"</head><body><h2>&#9888; Access Denied</h2><p>You can't access this content<br>because it's already in use in<br>another "
"window.<br><br>Note: Multi-client mode is disabled.</p><br><a href=\"https://www.webui.me\">"
"<small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
static const char* webui_html_res_not_available = "<html><head><title>Resource Not Available</title><script src=\"/webui.js\">"
"</script><style>body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-"
"image:linear-gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}"
"</style></head><body><h2>&#9888; Resource Not Available</h2><p>The requested resource is not available.</p><br><a href=\""
"https://www.webui.me\"><small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
static const char* webui_def_icon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" zoomAndPan=\"magnify\" viewBox=\"0 0 375 374.999991\" height=\"500\" preserveAspectRatio=\"xMidYMid meet\" version=\"1.0\"><defs><clipPath id=\"3530adfd62\"><path d=\"M 22.375 22.558594 L 352.625 22.558594 L 352.625 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" clip-rule=\"nonzero\"/></clipPath></defs><g clip-path=\"url(#3530adfd62)\"><path fill=\"#2a6699\" d=\"M 22.375 22.558594 L 352.257812 22.558594 L 352.257812 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" fill-opacity=\"1\" fill-rule=\"nonzero\"/></g></svg>"; static const char* webui_def_icon_type = "image/svg+xml";

// -- Functions -----------------------
void webui_run_client(webui_event_t* e, const char* script) {

    #ifdef WEBUI_LOG
    printf("[User] webui_run_client([%zu])\n", e->window);
    printf("[User] webui_run_client([%zu]) -> Script: [%s]\n", e->window, script);
    #endif

    // Initialization
    _webui_init();

    size_t js_len = _webui_strlen(script);
    if (js_len < 1)
        return;

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
        return;

    // Packet Protocol Format:
    // [...]
    // [CMD]
    // [Script]

    // Send the packet to a single client
    _webui_send_client(win, _webui.clients[e->connection_id], 0, WEBUI_CMD_JS_QUICK, script, js_len, false);
}

void webui_run(size_t window, const char* script) {

    #ifdef WEBUI_LOG
    printf("[User] webui_run([%zu])\n", window);
    printf("[User] webui_run([%zu]) -> Script: [%s]\n", window, script);
    #endif

    // Initialization
    _webui_init();

    size_t js_len = _webui_strlen(script);
    if (js_len < 1)
        return;

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
        return;

    // Packet Protocol Format:
    // [...]
    // [CMD]
    // [Script]

    // Send the packet to all clients
    _webui_send_all(win, 0, WEBUI_CMD_JS_QUICK, script, js_len);
}

void webui_set_file_handler(size_t window, const void*(*handler)(const char* filename, int* length)) {

    if (handler == NULL)
        return;

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Set the new `files_handler`
    win->files_handler = handler;
    // And reset any previous `files_handler_window`
    win->files_handler_window = NULL;
}

void webui_set_file_handler_window(size_t window, const void*(*handler)(size_t window, const char* filename, int* length)) {

    if (handler == NULL)
        return;

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Reset any previous `files_handler`
    win->files_handler = NULL;
    // And set `files_handler_window`
    win->files_handler_window = handler;
}

bool webui_script_client(webui_event_t* e, const char* script, size_t timeout,
    char* buffer, size_t buffer_length) {

    #ifdef WEBUI_LOG
    printf("[User] webui_script_client([%zu])\n", e->window);
    printf("[User] webui_script_client([%zu]) -> Script [%s] \n", e->window, script);
    printf("[User] webui_script_client([%zu]) -> Response Buffer @ 0x%p \n", e->window, buffer);
    printf("[User] webui_script_client([%zu]) -> Response Buffer Size %zu bytes \n", e->window, buffer_length);
    #endif

    // Initializing response buffer
    if (buffer_length > 0) {
        memset(buffer, 0, buffer_length); 
    }

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[e->window];

    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
        return false;

    size_t js_len = _webui_strlen(script);

    if (js_len < 1)
        return false;

    // Initializing pipe
    _webui_mutex_lock(&_webui.mutex_js_run);
    uint16_t run_id = _webui_get_run_id();
    _webui.run_done[run_id] = false;
    _webui.run_error[run_id] = false;
    _webui.run_userBuffer[run_id] = buffer;
    _webui.run_userBufferLen[run_id] = buffer_length;
    _webui_mutex_unlock(&_webui.mutex_js_run);

    // Packet Protocol Format:
    // [...]
    // [CMD]
    // [Script]

    // Send the packet to a single client
    _webui_send_client(win, _webui.clients[e->connection_id], run_id, WEBUI_CMD_JS, script, js_len, false);

    bool js_status = false;

    // Wait for UI response
    if (timeout < 1 || timeout > 86400) {

        // Wait forever
        for (;;) {
            _webui_sleep(1);
            _webui_mutex_lock(&_webui.mutex_js_run);
            js_status = _webui.run_done[run_id];
            _webui_mutex_unlock(&_webui.mutex_js_run);
            if (js_status)
                break;
        }
    } else {

        // Using timeout
        _webui_timer_t timer;
        _webui_timer_start(&timer);
        for (;;) {
            _webui_sleep(1);
            _webui_mutex_lock(&_webui.mutex_js_run);
            js_status = _webui.run_done[run_id];
            _webui_mutex_unlock(&_webui.mutex_js_run);
            if (js_status)
                break;
            if (_webui_timer_is_end(&timer, (timeout * 1000)))
                break;
        }
    }

    if (js_status) {

        #ifdef WEBUI_LOG
        printf(
            "[User] webui_script -> Response found. User buffer len: %zu bytes \n",
            _webui.run_userBufferLen[run_id]
        );
        printf(
            "[User] webui_script -> Response found. User buffer data: [%s] \n",
            _webui.run_userBuffer[run_id]
        );
        #endif

        return !_webui.run_error[run_id];
    } else {

        #ifdef WEBUI_LOG
        printf("[User] webui_script -> No Response is found. \n");
        #endif
    }

    return false;
}

bool webui_script(size_t window, const char* script, size_t timeout,
    char* buffer, size_t buffer_length) {

    #ifdef WEBUI_LOG
    printf("[User] webui_script([%zu])\n", window);
    #endif

    // Stop if multi-client mode is enabled.
    // we can't send and receive from all clients
    if (_webui.config.multi_client) {
        #ifdef WEBUI_LOG
        printf("[User] webui_script() -> Multi-client mode is enabled, stop.\n");
        #endif
        // Initializing response buffer
        if (buffer_length > 0) {
            memset(buffer, 0, buffer_length); 
        }
        return false;
    }

    // Event
    webui_event_t e;
    e.window = window;
    e.connection_id = 0; // Single client

    return webui_script_client(&e, script, timeout, buffer, buffer_length);
}

static uint32_t _webui_generate_random_uint32() {
    uint32_t timestamp = (uint32_t) time(NULL);
    // Get the higher 16 bits
    uint32_t high = ((uint32_t) rand()&0xFFFF) + ((timestamp >> 16)&0xFFFF);
    // Get the lower 16 bits
    uint32_t low = ((uint32_t) rand()&0xFFFF) + (timestamp&0xFFFF);
    // Combine
    return (high << 16) | low;
}

size_t webui_new_window(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_new_window()\n");
    #endif

    // Create a new window
    return webui_new_window_id(webui_get_new_window_id());
}

size_t webui_new_window_id(size_t num) {

    #ifdef WEBUI_LOG
    printf("[User] webui_new_window_id([%zu])\n", num);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return 0;

    // Check window ID
    if (num < 1 || num > WEBUI_MAX_IDS)
        return 0;

    // Destroy the window if already exist
    if (_webui.wins[num] != NULL)
        webui_destroy(num);

    // Create a new window
    _webui_window_t* win = (_webui_window_t* ) _webui_malloc(sizeof(_webui_window_t));
    _webui.wins[num] = win;

    // Initialisation
    win->ws_block = _webui.config.ws_block;
    win->num = num;
    win->browser_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
    win->server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
    if (_webui_is_empty(_webui.default_server_root_path))
        WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", WEBUI_DEFAULT_PATH);
    else
        WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", _webui.default_server_root_path);

    // Auto bind JavaScript-Bridge Core API Handler
    webui_bind(num, "__webui_core_api__", _webui_bridge_api_handler);

    #ifdef WEBUI_LOG
    printf("[User] webui_new_window_id() -> New window #%zu @ 0x%p\n", num, win);
    #endif

    return num;
}

size_t webui_get_new_window_id(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_new_window_id()\n");
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return 0;

    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (_webui.wins[i] == NULL && !_webui.wins_reserved[i]) {
            _webui.wins_reserved[i] = true;
            return i;
        }
    }

    // We should never reach here
    WEBUI_ASSERT("webui_get_new_window_id() failed");
    return 0;
}

void webui_set_kiosk(size_t window, bool status) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_kiosk([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    win->kiosk_mode = status;
}

void webui_set_high_contrast(size_t window, bool status) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_high_contrast([%zu], [%d])\n", window, status);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    win->disable_browser_high_contrast = !status;
}

bool webui_browser_exist(size_t browser) {

    #ifdef WEBUI_LOG
    printf("[User] webui_browser_exist([%zu])\n", browser);
    #endif

    return _webui_browser_exist(NULL, browser);
}

bool webui_is_high_contrast(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_is_high_contrast()\n");
    #endif

    // Initialization
    _webui_init();

    bool is_enabled = false;

    #ifdef _WIN32
        char hc[WEBUI_MAX_PATH];
        if (_webui_get_windows_reg_value(
            HKEY_CURRENT_USER,
            L"Control Panel\\Accessibility\\HighContrast",
            L"Flags",
            hc))
        {
            long flags = strtol(hc, NULL, 10);
            if (flags != LONG_MIN && flags != LONG_MAX) {
                is_enabled = ((flags&0x01) == 1);
            }
        }
    #elif __linux__
        FILE* process_output;
        char buf[128];
        process_output = popen("gsettings get org.gnome.desktop.a11y.interface high-contrast", "r");
        if (process_output != NULL) {
            if (fgets(buf, sizeof(buf), process_output) != NULL) {
                is_enabled = (strstr(buf, "true") != NULL);
            }
            pclose(process_output);
        }
    #else
        FILE* process_output;
        char buf[128];
        process_output = popen("defaults read -g AppleInterfaceStyle", "r");
        if (process_output != NULL) {
            if (fgets(buf, sizeof(buf), process_output) != NULL) {
                is_enabled = (strstr(buf, "Dark") != NULL);
            }
            pclose(process_output);
        }
    #endif

    #ifdef WEBUI_LOG
    printf("[User] webui_is_high_contrast() -> %d\n", is_enabled);
    #endif

    return is_enabled;
}

void webui_close_client(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_close_client([%zu])\n", e->window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Remove cookies
    // _webui_client_cookies_free(win, _webui.clients[e->connection_id]);

    // Packet Protocol Format:
    // [...]
    // [CMD]

    // Send the packet to a single client
    _webui_send_client(win, _webui.clients[e->connection_id], 0, WEBUI_CMD_CLOSE, NULL, 0, false);

    // Forced close
    mg_close_connection(_webui.clients[e->connection_id]);
}

void webui_close(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_close([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Close
    if (!win->webView) {
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            // Packet Protocol Format:
            // [...]
            // [CMD]
            // Send the packet
            _webui_send_all(win, 0, WEBUI_CMD_CLOSE, NULL, 0);
        }
    }
    else {
        // Stop WebView thread if any
        if (win->webView) {
            win->webView->stop = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
        }
    }
}

void webui_destroy(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_destroy([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (win->server_running) {

        // Freindly close
        webui_close(window);

        // Wait for server threads to stop
        _webui_timer_t timer_1;
        _webui_timer_start(&timer_1);
        for (;;) {
            _webui_sleep(10);
            if (!win->server_running)
                break;
            if (_webui_timer_is_end(&timer_1, 2500))
                break;
        }

        if (win->server_running) {

            #ifdef WEBUI_LOG
            printf("[User] webui_destroy([%zu]) -> Forced close\n", window);
            #endif

            // Forced close
            _webui_mutex_is_connected(win, WEBUI_MUTEX_FALSE);

            // Wait for server threads to stop
            _webui_timer_t timer_2;
            _webui_timer_start(&timer_2);
            for (;;) {
                _webui_sleep(10);
                if (!win->server_running)
                    break;
                if (_webui_timer_is_end(&timer_2, 1500))
                    break;
            }
        }
    }

    // Free memory resources
    _webui_free_mem((void*)win->url);
    _webui_free_mem((void*)win->html);
    _webui_free_mem((void*)win->icon);
    _webui_free_mem((void*)win->icon_type);
    _webui_free_mem((void*)win->browser_path);
    _webui_free_mem((void*)win->profile_path);
    _webui_free_mem((void*)win->profile_name);
    _webui_free_mem((void*)win->server_root_path);

    // Free events
    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (win->events[i] != NULL)
            _webui_free_mem((void*)win->events[i]);
    }

    // Free window struct
    _webui_free_mem((void*)_webui.wins[window]);
    _webui.wins[window] = NULL;
    _webui.wins_reserved[window] = false;
}

bool webui_is_shown(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_is_shown([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    return _webui_mutex_is_connected(win, WEBUI_MUTEX_NONE);
}

void webui_set_icon(size_t window, const char* icon, const char* icon_type) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_icon([%zu], [%s], [%s])\n", window, icon, icon_type);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Some wrappers do not guarantee pointers stay valid,
    // so, let's make our copy.

    // Icon
    size_t len = _webui_strlen(icon);
    const char* icon_cpy = (const char*)_webui_malloc(len);
    memcpy((char*)icon_cpy, icon, len);

    // Icon Type
    len = _webui_strlen(icon_type);
    const char* icon_type_cpy = (const char*)_webui_malloc(len);
    memcpy((char*)icon_type_cpy, icon_type, len);

    // Clean old sets if any
    if (win->icon != NULL)
        _webui_free_mem((void*)win->icon);
    if (win->icon_type != NULL)
        _webui_free_mem((void*)win->icon_type);

    win->icon = icon_cpy;
    win->icon_type = icon_type_cpy;
}

void webui_navigate_client(webui_event_t* e, const char* url) {

    #ifdef WEBUI_LOG
    printf("[User] webui_navigate_client([%zu], [%s])\n", e->window, url);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Web-Browser Window
    if (!win->webView) {

        if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
            return;

        // Packet Protocol Format:
        // [...]
        // [CMD]
        // [URL]

        // Send the packet to a single client
        _webui_send_client(win, _webui.clients[e->connection_id], 
            0, WEBUI_CMD_NAVIGATION, url, _webui_strlen(url), false
        );
    }
}

void webui_navigate(size_t window, const char* url) {

    #ifdef WEBUI_LOG
    printf("[User] webui_navigate([%zu], [%s])\n", window, url);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Web-Browser Window
    if (!win->webView) {

        if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
            return;

        // Packet Protocol Format:
        // [...]
        // [CMD]
        // [URL]

        // Send the packet
        _webui_send_all(win, 0, WEBUI_CMD_NAVIGATION, url, _webui_strlen(url));
    }
    else {
        // WebView
        _webui_free_mem((void*) win->webView->url);

        #ifdef _WIN32
        wchar_t* wURL = NULL;
        _webui_str_to_wide(url, &wURL);
        win->webView->url = wURL;
        #else
        char* url_cp = _webui_str_dup(url);
        win->webView->url = url_cp;
        #endif

        win->webView->navigate = true;
        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
    }
}

void webui_clean(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_clean()\n");
    #endif

    // Initialization
    _webui_init();

    // Final memory cleaning
    _webui_clean();
}

void webui_delete_all_profiles(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_delete_all_profiles()\n");
    #endif

    // Initialization
    _webui_init();

    // Loop trough all windows
    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (_webui.wins[i] != NULL) {
            webui_delete_profile(i);
        }
    }
}

static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_is_firefox_ini_profile_exist([%s], [%s])\n", path, profile_name);
    #endif

    // Parse home environments in the path
    #ifdef _WIN32
    // Windows
    char full_path[WEBUI_MAX_PATH];
    ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
    #else
    // Linux&macOS
    char full_path[WEBUI_MAX_PATH];
    if (path[0] == '~') {
        const char* home = getenv("HOME");
        if (home) {
            WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home, &path[1]);
        } else {
            // If for some reason HOME isn't set
            // fall back to the original path.
            strncpy(full_path, path, sizeof(full_path));
        }
    } else {
        strncpy(full_path, path, sizeof(full_path));
    }
    #endif

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Opening [%s]\n", full_path);
    #endif

    // Open
    FILE* file;
    WEBUI_FILE_OPEN(file, full_path, "r");
    if (!file)
        return false;

    char target[128] = {
        0x00
    };
    WEBUI_SN_PRINTF_STATIC(target, sizeof(target), "Name=%s", profile_name);

    char line[1024];
    while(fgets(line, sizeof(line), file)) {
        if (strstr(line, target) != NULL) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Target found\n");
            #endif
            fclose(file);
            return true;
        }
    }

    fclose(file);
    return false;
}

static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_remove_firefox_profile_ini([%s], [%s])\n", path, profile_name);
    #endif

    // Parse home environments in the path
    #ifdef _WIN32
    // Windows
    char full_path[WEBUI_MAX_PATH];
    ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
    #else
    // Linux&macOS
    char full_path[WEBUI_MAX_PATH];
    if (path[0] == '~') {
        const char* home = getenv("HOME");
        if (home) {
            WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home,&path[1]);
        } else {
            // If for some reason HOME isn't set
            // fall back to the original path.
            strncpy(full_path, path, sizeof(full_path));
        }
    } else {
        strncpy(full_path, path, sizeof(full_path));
    }
    #endif

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Opening [%s]\n", full_path);
    #endif

    // Open
    FILE * file;
    WEBUI_FILE_OPEN(file, full_path, "r");
    if (!file)
        return;

    char buffer[4096] = {0};
    char output[4096] = {0};
    char target[128] = {0};

    WEBUI_SN_PRINTF_STATIC(target, sizeof(target), "Name=%s", profile_name);

    bool skip = false;
    while(fgets(buffer, sizeof(buffer), file)) {
        if (strncmp(buffer, "[Profile", 8) == 0) {
            if (skip)
                skip = false;
        }
        if (!skip) {
            if (strstr(buffer, target) != NULL) {

                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Target found\n");
                #endif
                skip = true;
                continue;
            } else
                WEBUI_STR_CAT_DYN(output, sizeof(output), buffer);
        }
    }

    fclose(file);

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Saving\n");
    #endif

    // Save
    WEBUI_FILE_OPEN(file, full_path, "w");
    if (!file)
        return;
    fputs(output, file);
    fclose(file);
}

void webui_delete_profile(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_delete_profile([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (_webui_folder_exist(win->profile_path)) {

        if (win->current_browser == Firefox) {

            // Delete Firefox profile

            #ifdef _WIN32
            // Windows
            _webui_remove_firefox_profile_ini(
                "%APPDATA%\\Mozilla\\Firefox\\profiles.ini", win->profile_name
            );
            _webui_delete_folder(win->profile_path);
            #elif __linux__
            // Linux
            _webui_remove_firefox_profile_ini("~/.mozilla/firefox/profiles.ini", win->profile_name);
            _webui_remove_firefox_profile_ini(
                "~/snap/firefox/common/.mozilla/firefox/profiles.ini", win->profile_name
            );
            _webui_delete_folder(win->profile_path);
            #else
            // macOS
            _webui_remove_firefox_profile_ini(
                "~/Library/Application Support/Firefox/profiles.ini", win->profile_name
            );
            _webui_delete_folder(win->profile_path);
            #endif
        } else {

            // Delete Chromium-based profile
            _webui_delete_folder(win->profile_path);
        }
    }
}

const char* webui_start_server(size_t window, const char* content) {

    #ifdef WEBUI_LOG
    printf("[User] webui_start_server([%zu])\n", window);
    #endif

    if (_webui_is_empty(content))
        return "";
    
    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return "";
    _webui_window_t* win = _webui.wins[window];

    // Check
    if (win->server_running)
        return "";

    // Make `wait()` waits forever
    webui_set_timeout(0);

    // Start the window without any GUI
    if (webui_show_browser(window, content, NoBrowser)) {
        return webui_get_url(window);
    }

    return "";
}

bool webui_show_client(webui_event_t* e, const char* content) {

    #ifdef WEBUI_LOG
    printf("[User] webui_show_client([%zu])\n", e->window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[e->window];

    // Show the window using WebView or using any browser
    win->allow_webview = true;

    // Show for single a client
    return _webui_show(win, _webui.clients[e->connection_id], content, AnyBrowser);
}

bool webui_show(size_t window, const char* content) {

    #ifdef WEBUI_LOG
    printf("[User] webui_show([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    // Show the window using WebView or using any browser
    win->allow_webview = true;

    // Show for all connected clients
    return _webui_show(win, NULL, content, AnyBrowser);
}

bool webui_show_wv(size_t window, const char* content) {

    #ifdef WEBUI_LOG
    printf("[User] webui_show_wv([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    // Show the window using WebView only
    win->allow_webview = true;

    // Show for all connected clients
    return _webui_show(win, NULL, content, NoBrowser);
}

bool webui_show_browser(size_t window, const char* content, size_t browser) {

    #ifdef WEBUI_LOG
    printf("[User] webui_show_browser([%zu], [%zu])\n", window, browser);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    // Show the window using a specific browser only
    win->allow_webview = (browser == Webview ? true : false);

    // Show for all connected clients
    return _webui_show(win, NULL, content, browser);
}

size_t webui_bind(size_t window, const char* element, void(*func)(webui_event_t* e)) {

    #ifdef WEBUI_LOG
    printf("[User] webui_bind([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    // Search
    size_t cb_index = 0;
    bool exist = _webui_get_cb_index(win, element, &cb_index);    

    // All events
    if (_webui_is_empty(element)) {
        win->has_all_events = true;
        size_t index = (exist ? cb_index : _webui.cb_count++);
        win->html_elements[index] = "";
        win->cb[index] = func;
        #ifdef WEBUI_LOG
        printf("[User] webui_bind() -> Save bind (all events) at %zu\n", index);
        #endif
        return index;
    }

    // New bind
    const char* element_cpy = (const char*)_webui_str_dup(element);
    size_t index = (exist ? cb_index : _webui.cb_count++);
    win->html_elements[index] = element_cpy;
    win->cb[index] = func;
    #ifdef WEBUI_LOG
    printf("[User] webui_bind() -> Save bind at %zu\n", index);
    #endif

    // Send to all connected clients the new binding ID
    // Packet Protocol Format:

    // [...]
    // [CMD]
    // [NewElement]
    // Send the packet

    _webui_send_all(
        win, 0, WEBUI_CMD_ADD_ID,
        element, _webui_strlen(element_cpy)
    );

    return index;
}

size_t webui_get_best_browser(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_best_browser([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 1; // 1. Default recommended web browser
    _webui_window_t* win = _webui.wins[window];
    
    return _webui_find_the_best_browser(win);
}

const char* webui_get_string_at(webui_event_t* e, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_string_at([%zu])\n", index);
    #endif

    // Initialization
    _webui_init();

    if (index > WEBUI_MAX_ARG)
        return NULL;

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return NULL;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return NULL;

    if (event_inf->event_data[index] != NULL) {
        size_t len = _webui_strlen(event_inf->event_data[index]);
        if (len > 0 && len <= WEBUI_MAX_BUF)
            return (const char*)event_inf->event_data[index];
    }

    return "";
}

size_t webui_get_count(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_count()\n");
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return 0;

    return event_inf->count;
}

long long int webui_get_int_at(webui_event_t* e, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_int_at([%zu])\n", index);
    #endif

    // Initialization&Dereference
    // are done by webui_get_string()

    if (index > WEBUI_MAX_ARG)
        return 0;

    const char* str = webui_get_string_at(e, index);
    if (str == NULL)
        return 0;

    size_t len = _webui_strlen(str);
    if (len > 0 && len <= 20) {
        // 64-bit max is -9,223,372,036,854,775,808 (20 character)
        char* endptr;
        return strtoll((const char*)str, &endptr, 10);
    }

    return 0;
}

double webui_get_float_at(webui_event_t* e, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_float_at([%zu])\n", index);
    #endif

    // Initialization&Dereference
    // are done by webui_get_string()

    if (index > WEBUI_MAX_ARG)
        return 0.0;

    const char* str = webui_get_string_at(e, index);
    if (str == NULL)
        return 0.0;

    size_t len = _webui_strlen(str);
    if (len > 0 && len <= 20) {
        // 64-bit max is -9,223,372,036,854,775,808 (20 character)
        char* endptr;
        return strtod(str, &endptr);
    }

    return 0.0;
}

bool webui_get_bool_at(webui_event_t* e, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_bool_at([%zu])\n", index);
    #endif

    // Initialization&Dereference
    // are done by webui_get_string()

    if (index > WEBUI_MAX_ARG)
        return false;

    const char* str = webui_get_string_at(e, index);
    if (str == NULL)
        return false;

    if (str[0] == '0' || str[0] == 'f' || str[0] == 'F') // `0` | `false` | `FALSE`
        return false;

    return true;
}

size_t webui_get_size_at(webui_event_t* e, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_size_at([%zu])\n", index);
    #endif

    // Initialization
    _webui_init();

    if (index > WEBUI_MAX_ARG)
        return 0;

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return 0;

    return event_inf->event_size[index];
}

const char* webui_get_string(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_string()\n");
    #endif

    return webui_get_string_at(e, 0);
}

long long int webui_get_int(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_int()\n");
    #endif

    return webui_get_int_at(e, 0);
}

double webui_get_float(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_float()\n");
    #endif

    return webui_get_float_at(e, 0);
}

bool webui_get_bool(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_bool()\n");
    #endif

    return webui_get_bool_at(e, 0);
}

size_t webui_get_size(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_size()\n");
    #endif

    return webui_get_size_at(e, 0);
}

void webui_return_int(webui_event_t* e, long long int n) {

    #ifdef WEBUI_LOG
    printf("[User] webui_return_int([%lld])\n", n);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return;

    // Free
    if (event_inf->response != NULL)
        _webui_free_mem((void*)event_inf->response);

    // Int to Str
    // 64-bit max is -9,223,372,036,854,775,808 (20 character)
    char* buf = (char*)_webui_malloc(20);
    WEBUI_SN_PRINTF_DYN(buf, 20, "%lld", n);

    // Set response
    event_inf->response = buf;
}

void webui_return_float(webui_event_t* e, double f) {

    #ifdef WEBUI_LOG
    printf("[User] webui_return_float([%f])\n", f);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return;

    // Free
    if (event_inf->response != NULL)
        _webui_free_mem((void*)event_inf->response);

    // Float to Str
    // 64-bit max is -9,223,372,036,854,775,808 (20 character)
    char* buf = (char*)_webui_malloc(20);
    WEBUI_SN_PRINTF_DYN(buf, 20, "%lf", f);

    // Set response
    event_inf->response = buf;
}

void webui_return_string(webui_event_t* e, const char* s) {

    #ifdef WEBUI_LOG
    printf("[User] webui_return_string([%s])\n", s);
    #endif

    if (_webui_is_empty(s))
        return;

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return;

    // Free
    if (event_inf->response != NULL)
        _webui_free_mem((void*)event_inf->response);

    // Copy Str
    size_t len = _webui_strlen(s);
    char* buf = (char*)_webui_malloc(len);
    memcpy(buf, s, len);

    // Set response
    event_inf->response = buf;
}

void webui_return_bool(webui_event_t* e, bool b) {

    #ifdef WEBUI_LOG
    printf("[User] webui_return_bool([%d])\n", b);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf == NULL)
        return;

    // Free
    if (event_inf->response != NULL)
        _webui_free_mem((void*)event_inf->response);

    // Bool to Str
    int len = 1;
    char* buf = (char*)_webui_malloc(len);
    WEBUI_SN_PRINTF_DYN(buf, len, "%d", b);

    // Set response
    event_inf->response = buf;
}

size_t webui_get_parent_process_id(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_parent_process_id([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    return win->process_id;
}

const char* webui_get_mime_type(const char* file) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_mime_type([%s])\n", file);
    #endif

    // Initialization
    _webui_init();

    return mg_get_builtin_mime_type(file);
}

size_t webui_get_free_port(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_free_port()\n");
    #endif

    // Initialization
    _webui_init();

    return _webui_get_free_port();
}

size_t webui_get_port(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_port([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    return win->server_port;
}

#ifdef WEBUI_TLS
static bool _webui_check_certificate(const char* certificate_pem, const char* private_key_pem) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_check_certificate()\n");
    #endif

    OpenSSL_add_all_algorithms();

    // SSL Context
    SSL_CTX * ctx;
    SSL_library_init();
    ctx = SSL_CTX_new(TLS_client_method());
    if (!ctx)
        return false;

    // Disable security levels. It is The end-user
    // responsability to provide a high encryption
    // level certificate. While WebUI should just
    // use the end-user's certificates.
    SSL_CTX_set_security_level(ctx, 0);

    // Load Certificate
    BIO * bio_cert = BIO_new_mem_buf((void*)certificate_pem, -1);
    X509 * cert = PEM_read_bio_X509(bio_cert, NULL, 0, NULL);
    if (cert == NULL) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_check_certificate() -> PEM_read_bio_X509 failed\n");
        #endif
        BIO_free_all(bio_cert);
        X509_free(cert);
        SSL_CTX_free(ctx);
        return false;
    }

    // Use certificate
    if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_check_certificate() -> SSL_CTX_use_certificate "
            "failed\n");
        #endif
        BIO_free_all(bio_cert);
        X509_free(cert);
        SSL_CTX_free(ctx);
        return false;
    }

    // Load Key
    BIO * bio_key = BIO_new_mem_buf((void*)private_key_pem, -1);
    EVP_PKEY * private_key = PEM_read_bio_PrivateKey(bio_key, NULL, 0, NULL);
    if (private_key == NULL) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_check_certificate() -> PEM_read_bio_PrivateKey "
            "failed\n");
        #endif
        EVP_PKEY_free(private_key);
        BIO_free_all(bio_key);
        BIO_free_all(bio_cert);
        X509_free(cert);
        SSL_CTX_free(ctx);
        EVP_cleanup();
        return false;
    }

    // Use key
    if (SSL_CTX_use_PrivateKey(ctx, private_key) <= 0) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_check_certificate() -> SSL_CTX_use_PrivateKey "
            "failed\n");
        #endif
        EVP_PKEY_free(private_key);
        BIO_free_all(bio_key);
        BIO_free_all(bio_cert);
        X509_free(cert);
        SSL_CTX_free(ctx);
        EVP_cleanup();
        return false;
    }

    // Free
    EVP_PKEY_free(private_key);
    BIO_free_all(bio_key);
    BIO_free_all(bio_cert);
    X509_free(cert);
    SSL_CTX_free(ctx);
    EVP_cleanup();

    return true;
}
#endif

bool webui_set_tls_certificate(const char* certificate_pem, const char* private_key_pem) {
    (void)certificate_pem;
    (void)private_key_pem;
    #ifdef WEBUI_LOG
    printf("[User] webui_set_tls_certificate()\n");
    #endif

    // Initialization
    _webui_init();

    #ifdef WEBUI_TLS
    if (!_webui_is_empty(certificate_pem) && !_webui_is_empty(private_key_pem)) {

        // Check size
        size_t certificate_len = _webui_strlen(certificate_pem);
        size_t private_key_len = _webui_strlen(private_key_pem);
        if (certificate_len >= WEBUI_SSL_SIZE || private_key_len >= WEBUI_SSL_SIZE)
            return false;

        // Check certificate validity
        if (!_webui_check_certificate(certificate_pem, private_key_pem)) {
            #ifdef WEBUI_LOG
            unsigned long err = ERR_get_error();
            char err_buf[1024];
            ERR_error_string_n(err, err_buf, sizeof(err_buf));
            printf("[User] webui_set_tls_certificate() -> Invalid certificate:\n%s\n", err_buf);
            #endif
            return false;
        }

        // Free generated self-signed
        _webui_free_mem((void*)_webui.root_cert);
        _webui_free_mem((void*)_webui.root_key);
        _webui_free_mem((void*)_webui.ssl_cert);
        _webui_free_mem((void*)_webui.ssl_key);

        // Set user TLS
        char* ssl_cert = (char*)_webui_malloc(certificate_len);
        char* ssl_key = (char*)_webui_malloc(private_key_len);
        WEBUI_SN_PRINTF_DYN(ssl_cert, certificate_len, "%s", certificate_pem);
        WEBUI_SN_PRINTF_DYN(ssl_key, private_key_len, "%s", private_key_pem);
        _webui.ssl_cert = ssl_cert;
        _webui.ssl_key = ssl_key;

        #ifdef WEBUI_LOG
        printf("[User] webui_set_tls_certificate() -> SSL/TLS Certificate:\n");
        printf("%s\n", (const char*)_webui.ssl_cert);
        printf("%s\n", (const char*)_webui.ssl_key);
        #endif

        return true;
    }
    #else
        WEBUI_ASSERT("SSL/TLS is not available in this library. "
            "Please use the secure version of WebUI library");
    #endif

    return false;
}

void webui_set_config(webui_config option, bool status) {

    #ifdef WEBUI_LOG
    printf("[User] webui_config([%d], [%d])\n", option, status);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return;

    switch (option) {
        case show_wait_connection:
            _webui.config.show_wait_connection = status;
            break;
        case folder_monitor:
            _webui.config.folder_monitor = status;
            break;
        case multi_client:
            _webui.config.multi_client = status;
            break;
        case use_cookies:
            _webui.config.use_cookies = status;
            break;
        case ui_event_blocking:
            _webui.config.ws_block = status;
            // Update all created windows
            for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
                if (_webui.wins[i] != NULL) {
                    _webui.wins[i]->ws_block = status;
                }
            }
            break;
        #ifdef WEBUI_LOG
        default:
            printf("[User] webui_config -> Unknown option [%d]\n", option);
            break;
        #endif
    }
}

void webui_set_event_blocking(size_t window, bool status) {
    #ifdef WEBUI_LOG
    printf("[User] webui_set_event_blocking([%zu], [%d])\n", window, status);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    win->ws_block = status;
}

bool webui_set_port(size_t window, size_t port) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_port([%zu], [%zu])\n", window, port);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    if (_webui_port_is_used(port))
        return false;

    win->custom_server_port = port;
    return true;
}

size_t webui_get_child_process_id(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_child_process_id([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    #ifdef _WIN32
    DWORD childProcessId = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hSnapshot,&pe32)) {
            do {
                if (pe32.th32ParentProcessID == win->process_id) {
                    childProcessId = pe32.th32ProcessID;
                }
            } while(Process32Next(hSnapshot,&pe32));
        }
        CloseHandle(hSnapshot);
    }
    return childProcessId;
    #elif __linux__
    DIR * dir;
    struct dirent* entry;
    pid_t lastChildPid = (pid_t) win->process_id;
    dir = opendir("/proc");
    if (!dir)
        return win->process_id;
    while((entry = readdir(dir)) != NULL) {
        // Ensure we're looking at a process directory (directories that are just
        // numbers)
        if (entry->d_type == DT_DIR && strspn(entry->d_name, "0123456789") == _webui_strlen(entry->d_name)) {
            char statFilepath[1024];
            WEBUI_SN_PRINTF_STATIC(statFilepath, sizeof(statFilepath), "/proc/%s/stat", entry->d_name);
            FILE * f;
            WEBUI_FILE_OPEN(f, statFilepath, "r");
            if (f) {
                pid_t pid, ppid;
                char comm[1024];
                char state;
                // Extract data from the stat file;fields are space-delimited
                if (fscanf(f, "%d %s %c %d",&pid, comm,&state,&ppid) == 4) {
                    if ((intmax_t) ppid == (intmax_t) win->process_id) {
                        // Convert directory name (string) to integer PID
                        lastChildPid = atoi(entry->d_name);
                    }
                }
                fclose(f);
            }
        }
    }
    closedir(dir);
    return lastChildPid;
    #else
    pid_t lastChildPid = -1;
    // Get the size required to hold all process information
    int mib[4] = {
        CTL_KERN,
        KERN_PROC,
        KERN_PROC_ALL,
        0
    };
    size_t size;
    if (sysctl(mib, 3, NULL,&size, NULL, 0) < 0) {
        return win->process_id;
    }
    // Allocate memory and get all process information
    struct kinfo_proc * procList = (struct kinfo_proc * ) malloc(size);
    if (!procList) {
        return win->process_id;
    }
    if (sysctl(mib, 3, procList,&size, NULL, 0) < 0) {
        free(procList);
        return win->process_id;
    }
    // Calculate the number of processes from the returned data
    int procCount = size / sizeof(struct kinfo_proc);
    // Search for the last child process
    for (int i = 0; i < procCount; i++) {
        if ((intmax_t) procList[i].kp_eproc.e_ppid == (intmax_t) win->process_id) {
            lastChildPid = procList[i].kp_proc.p_pid;
        }
    }
    free(procList);
    return (size_t) lastChildPid;
    #endif
}

void webui_set_hide(size_t window, bool status) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_hide(%zu, %d)\n", window, status);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    win->hide = status;
}

void webui_set_size(size_t window, unsigned int width, unsigned int height) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_size(%zu, %u, %u)\n", window, width, height);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (width < WEBUI_MIN_WIDTH || width > WEBUI_MAX_WIDTH || height < WEBUI_MIN_HEIGHT ||
        height > WEBUI_MAX_HEIGHT) {

        win->size_set = false;
        return;
    }

    win->width = width;
    win->height = height;
    win->size_set = true;

    // Resize the current window
    if (!win->webView) {

        // web-browser window
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            char script[128];
            WEBUI_SN_PRINTF_STATIC(script, sizeof(script), "window.resizeTo(%u, %u);", width, height);
            webui_run(window, script);
        }
    }
    else {

        // webView window
        if (win->webView) {
            win->webView->width = width;
            win->webView->height = height;
            win->webView->size = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
        }
    }
}

void webui_set_minimum_size(size_t window, unsigned int width, unsigned int height) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_minimum_size(%zu, %u, %u)\n", window, width, height);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (width < WEBUI_MIN_WIDTH || width > WEBUI_MAX_WIDTH || height < WEBUI_MIN_HEIGHT ||
        height > WEBUI_MAX_HEIGHT) {

        win->minimum_size_set = false;
        return;
    }

    win->minimum_width = width;
    win->minimum_height = height;
    win->minimum_size_set = true;
}

void webui_set_position(size_t window, unsigned int x, unsigned int y) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_position(%zu, %u, %u)\n", window, x, y);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    int X = x;
    int Y = y;

    if (X < WEBUI_MIN_X || X > WEBUI_MAX_X || Y < WEBUI_MIN_Y || Y > WEBUI_MAX_Y) {

        win->position_set = false;
        return;
    }

    win->x = X;
    win->y = Y;
    win->position_set = true;

    // Positioning the current window
    if (!win->webView) {

        // web-browser window
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            char script[128];
            WEBUI_SN_PRINTF_STATIC(script, sizeof(script), "window.moveTo(%u, %u);", X, Y);
            webui_run(window, script);
        }
    }
    else {

        // WebView window
        if (win->webView) {
            win->webView->x = X;
            win->webView->y = Y;
            win->webView->position = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
        }
    }
}

void webui_set_profile(size_t window, const char* name, const char* path) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_profile([%s], [%s])\n", name, path);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Some wrappers do not guarantee pointers stay valid,
    // so, let's make our copy.

    char* name_cpy = NULL;
    size_t len = _webui_strlen(name);
    if (len > 0) {
        name_cpy = (char*)_webui_malloc(len);
        memcpy((char*)name_cpy, name, len);
    }

    char* path_cpy = NULL;
    len = _webui_strlen(path);
    if (len > 0) {
        path_cpy = (char*)_webui_malloc(len);
        memcpy((char*)path_cpy, path, len);
    }

    // Free
    if (win->profile_name != NULL)
        _webui_free_mem((void*)win->profile_name);
    if (win->profile_path != NULL)
        _webui_free_mem((void*)win->profile_path);

    // Save
    win->profile_name = name_cpy;
    win->profile_path = path_cpy;
    win->custom_profile = true;

    // Default local machine profile
    if (name_cpy == NULL && path_cpy == NULL)
        win->default_profile = true;
    else
        win->default_profile = false;
}


void webui_set_proxy(size_t window, const char* proxy_server) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_proxy([%s])\n", proxy_server);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Some wrappers do not guarantee pointers stay valid,
    // so, let's make our copy.

    char* proxy_server_cpy = NULL;
    size_t len = _webui_strlen(proxy_server);
    if (len > 0) {
        proxy_server_cpy = (char*)_webui_malloc(len);
        memcpy((char*)proxy_server_cpy, proxy_server, len);
    }

    // Free
    if (win->proxy_server != NULL)
        _webui_free_mem((void*)win->proxy_server);

    // Save
    win->proxy_server = proxy_server_cpy;

    // Set
    if (_webui_is_empty(win->proxy_server))
        // Disable the proxy because the server
        // address is an empty string
        win->proxy_set = false;
    else
        // Enable proxy
        win->proxy_set = true;
}

void webui_open_url(const char* url) {

    #ifdef WEBUI_LOG
    printf("[User] webui_open_url([%s])\n", url);
    #endif

    // Initialization
    _webui_init();

    _webui_open_url_native(url);
}

const char* webui_get_url(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_get_url([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return NULL;
    _webui_window_t* win = _webui.wins[window];

    // Check if server is started
    if (_webui_is_empty(win->url)) {
        // Start server
        webui_show_browser(window, "<html><head><script src=\"webui.js\"></script></head></html>", NoBrowser);
    }

    return (const char*) win->url;
}

void webui_set_public(size_t window, bool status) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_public([%zu])\n", window);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    win->is_public = status;
}

void webui_send_raw_client(webui_event_t* e, const char* function, const void* raw, size_t size) {

    #ifdef WEBUI_LOG
    printf("[User] webui_send_raw_client(%zu bytes)\n", size);
    #endif

    if ((size < 1) || (_webui_strlen(function) < 1) || (raw == NULL))
        return;

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Generate data
    size_t data_len = _webui_strlen(function) + 1 + size;
    char* buf = (char*)_webui_malloc(data_len);

    // Add Function
    size_t p = 0;
    for (size_t i = 0; i < _webui_strlen(function); i++)
        buf[p++] = function [i];

    // Add Null
    buf[p] = 0x00;
    p++;

    // Add Data
    char* ptr = (char*)raw;
    for (size_t i = 0; i < size; i++) {
        buf[p++] = * ptr;
        ptr++;
    }

    // Packet Protocol Format:
    // [...]
    // [CMD]
    // [Function, Null, RawData]

    // Send the packet to single a client
    _webui_send_client(win, _webui.clients[e->connection_id], 
        0, WEBUI_CMD_SEND_RAW, (const char*)buf, data_len, false
    );

    _webui_free_mem((void*)buf);
}

void webui_send_raw(size_t window, const char* function, const void * raw, size_t size) {

    #ifdef WEBUI_LOG
    printf("[User] webui_send_raw(%zu bytes)\n", size);
    #endif

    if ((size < 1) || (_webui_strlen(function) < 1) || (raw == NULL))
        return;

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Generate data
    size_t data_len = _webui_strlen(function) + 1 + size;
    char* buf = (char*)_webui_malloc(data_len);

    // Add Function
    size_t p = 0;
    for (size_t i = 0; i < _webui_strlen(function); i++)
        buf[p++] = function [i];

    // Add Null
    buf[p] = 0x00;
    p++;

    // Add Data
    char* ptr = (char*)raw;
    for (size_t i = 0; i < size; i++) {
        buf[p++] = *ptr;
        ptr++;
    }

    // Packet Protocol Format:
    // [...]
    // [CMD]
    // [Function, Null, RawData]

    // Send the packet
    _webui_send_all(win, 0, WEBUI_CMD_SEND_RAW, (const char*)buf, data_len);
    _webui_free_mem((void*)buf);
}

char* webui_encode(const char* str) {

    #ifdef WEBUI_LOG
    printf("[User] webui_encode()\n");
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return NULL;

    size_t len = _webui_strlen(str);
    if (len < 1)
        return NULL;

    #ifdef WEBUI_LOG
    printf("[User] webui_encode() -> Text: [%s]\n", str);
    #endif

    size_t buf_len = (((len + 2) / 3) * 4) + 8;
    char* buf = (char*)_webui_malloc(buf_len);

    int ret = mg_base64_encode((const unsigned char*)str, len, buf,&buf_len);

    if (ret > (-1)) {

        // Failed
        #ifdef WEBUI_LOG
        printf("[User] webui_encode() -> Failed (%d).\n", ret);
        #endif
        _webui_free_mem((void*)buf);
        return NULL;
    }

    #ifdef WEBUI_LOG
    printf("[User] webui_encode() -> Encoded: [%s]\n", buf);
    #endif

    // Success
    return buf;
}

char* webui_decode(const char* str) {

    #ifdef WEBUI_LOG
    printf("[User] webui_decode()\n");
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return NULL;

    size_t len = _webui_strlen(str);
    if (len < 1)
        return NULL;

    #ifdef WEBUI_LOG
    printf("[User] webui_decode() -> Encoded: [%s]\n", str);
    #endif

    size_t buf_len = (((len + 2) / 3) * 4) + 8;
    unsigned char* buf = (unsigned char*)_webui_malloc(buf_len);

    int ret = mg_base64_decode(str, len, buf,&buf_len);

    if (ret > (-1)) {

        // Failed
        #ifdef WEBUI_LOG
        printf("[User] webui_decode() -> Failed (%d).\n", ret);
        #endif
        _webui_free_mem((void*)buf);
        return NULL;
    }

    #ifdef WEBUI_LOG
    printf("[User] webui_decode() -> Decoded: [%s]\n", buf);
    #endif

    // Success
    return (char*)buf;
}

void webui_free(void * ptr) {

    #ifdef WEBUI_LOG
    printf("[User] webui_free([0x%p])\n", ptr);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return;

    _webui_free_mem(ptr);
}

void * webui_malloc(size_t size) {

    #ifdef WEBUI_LOG
    printf("[User] webui_malloc(%zu bytes)\n", size);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return NULL;

    return _webui_malloc(size);
}

void webui_exit(void) {

    #ifdef WEBUI_LOG
    printf("[User] webui_exit()\n");
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return;

    // Close all windows
    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (_webui.wins[i] != NULL) {
            if (_webui_mutex_is_connected(_webui.wins[i], WEBUI_MUTEX_NONE)) {

                if (!_webui.wins[i]->webView) {

                    // Web browser

                    // Packet Protocol Format:
                    // [...]
                    // [CMD]

                    // Send the packet
                    _webui_send_all(
                        _webui.wins[i], 0, WEBUI_CMD_CLOSE, NULL, 0
                    );
                }
                else {

                    // WebView

                    // Stop WebView thread if any
                    if (_webui.wins[i]->webView) {
                        _webui.wins[i]->webView->stop = true;
                        _webui_mutex_is_webview_update(_webui.wins[i], WEBUI_MUTEX_TRUE);
                    }        
                }
            }
        }
    }

    // Stop all threads
    _webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);

    // Let's give other threads more time to
    // safely exit and finish cleaning up.
    _webui_sleep(500);

    // Fire the mutex condition for wait()
    _webui_condition_signal(&_webui.condition_wait);
    #ifdef __APPLE__
    _webui_macos_wv_stop();
    #endif
}

void webui_wait(void) {

    #ifdef WEBUI_LOG
    printf("[Loop] webui_wait()\n");
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return;

    if (_webui.startup_timeout > 0) {

        // Check if there is atleast one window (UI)
        // is running. Otherwise the mutex condition
        // signal will never come
        if (!_webui.ui) {

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> No window is found. Stop\n");
            #endif
            return;
        }

        #ifdef WEBUI_LOG
        printf("[Loop] webui_wait() -> Waiting (Timeout in %zu seconds)\n", 
            _webui.startup_timeout);
        #endif

        // The mutex conditional signal will
        // be fired when no more UI (servers)
        // is running.
    } else {

        #ifdef WEBUI_LOG
        printf("[Loop] webui_wait() -> Infinite waiting\n");
        #endif

        // The mutex conditional signal will
        // be fired when `webui_exit()` is
        // called by the user.
    }

    // Main loop
    #ifdef _WIN32
        if (!_webui.is_webview) {
            // Windows Web browser main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> Windows web browser loop\n");
            #endif

            _webui.is_browser_main_run = true;
            _webui_mutex_lock(&_webui.mutex_wait);
            _webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
            _webui.is_browser_main_run = false;
        }
        else {
            // Windows WebView main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> Windows WebView loop\n");
            #endif

            _webui_mutex_lock(&_webui.mutex_wait);
            _webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
        }
    #elif __linux__
        if (!_webui.is_webview) {
            // Linux Web browser main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> Linux web browser loop\n");
            #endif

            _webui.is_browser_main_run = true;
            _webui_mutex_lock(&_webui.mutex_wait);
            _webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
            _webui.is_browser_main_run = false;
        }
        else {
            // Linux WebView main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> Linux WebView loop\n");
            #endif

            _webui.is_gtk_main_run = true;

            while (true) {

                while (gtk_events_pending()) {
                    gtk_main_iteration_do(0);
                }
                
                if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
                    break;
            }

            _webui.is_gtk_main_run = false;
        }
    #else
        if (!_webui.is_webview) {
            // macOS Web browser main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> macOS web browser loop\n");
            #endif

            _webui.is_browser_main_run = true;
            _webui_mutex_lock(&_webui.mutex_wait);
            _webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
            _webui.is_browser_main_run = false;
        }
        else {
            // macOS WebView main loop

            #ifdef WEBUI_LOG
            printf("[Loop] webui_wait() -> macOS WebView loop\n");
            #endif

            _webui.is_wkwebview_main_run = true;

            while (true) {

                _webui_macos_wv_process();

                if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
                    break;
            }

            _webui.is_wkwebview_main_run = false;
        }
    #endif

    #ifdef WEBUI_LOG
    printf("[Loop] webui_wait() -> Cleaning\n");
    #endif

    // Clean
    #ifdef _WIN32
        if (!_webui.is_webview) {
            // Windows Web browser Clean

            // ...
        }
        else {
            // Windows WebView Clean

            PostQuitMessage(0);
            if (_webui.webview_cacheFolder) {
                _webui_delete_folder(_webui.webview_cacheFolder);
                _webui_free_mem((void*) _webui.webview_cacheFolder);
                _webui.webview_cacheFolder = NULL;
            }
            _webui_sleep(750);
        }
    #elif __linux__
        if (!_webui.is_webview) {
            // Linux Web browser Clean

            // ...
        }
        else {
            // Linux WebView Clean

            // Close all GTK windows if any
            for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
                if (_webui.wins[i] != NULL) {
                    if (_webui.wins[i]->webView) {
                        _webui.wins[i]->webView->stop = true;
                        _webui_mutex_is_webview_update(_webui.wins[i], WEBUI_MUTEX_TRUE);
                    }
                }
                // Process drawing events if any
                while (gtk_events_pending()) {
                    gtk_main_iteration_do(0);
                }
            }
            _webui_sleep(750);
            // Process drawing events if any
            while (gtk_events_pending()) {
                gtk_main_iteration_do(0);
            }
            _webui_wv_free();
        }
    #else
        if (!_webui.is_webview) {
            // macOS Web browser Clean

            // ...
        }
        else {
            // macOS WebView Clean

            _webui_sleep(750);
        }
    #endif

    #ifdef WEBUI_LOG
    printf("[Loop] webui_wait() -> Main loop exit successfully\n");
    #endif

    _webui_mutex_unlock(&_webui.mutex_wait);
}

void webui_set_timeout(size_t second) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_timeout([%zu])\n", second);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return;

    if (second > WEBUI_MAX_TIMEOUT)
        second = WEBUI_MAX_TIMEOUT;

    _webui.startup_timeout = second;
}

void webui_set_runtime(size_t window, size_t runtime) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_runtime([%zu], [%zu])\n", window, runtime);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    if (runtime != Deno && runtime != NodeJS && runtime != Bun)
        win->runtime = None;
    else
        win->runtime = runtime;
}

bool webui_set_root_folder(size_t window, const char* path) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_root_folder([%zu], [%s])\n", window, path);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    // Fix path
    char full_path[WEBUI_MAX_PATH] = {0};
    #if defined(_WIN32)
    // ...
    #else
    if ((path[0] != '.') && (path[0] != '/')) {
        WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "./%s", path);
        path = full_path;
    }
    #endif
    
    if (win->server_running || 
        _webui_is_empty(path) || 
        (_webui_strlen(path) > WEBUI_MAX_PATH) ||
        !_webui_folder_exist(path)) {

        WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", WEBUI_DEFAULT_PATH);
        #ifdef WEBUI_LOG
        printf("[User] webui_set_root_folder() -> failed\n");
        #endif
        return false;
    }

    #ifdef WEBUI_LOG
    printf("[User] webui_set_root_folder() -> Success\n");
    #endif
    WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", path);
    return true;
}

bool webui_set_default_root_folder(const char* path) {

    #ifdef WEBUI_LOG
    printf("[User] webui_set_default_root_folder([%s])\n", path);
    #endif

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return false;

    if (_webui_is_empty(path) || (_webui_strlen(path) > WEBUI_MAX_PATH) || !_webui_folder_exist((char*)path)) {

        _webui.default_server_root_path[0] = '\0';
        #ifdef WEBUI_LOG
        printf("[User] webui_set_default_root_folder() -> failed\n");
        #endif
        return false;
    }

    #ifdef WEBUI_LOG
    printf("[User] webui_set_default_root_folder() -> Success\n");
    #endif
    WEBUI_SN_PRINTF_DYN(_webui.default_server_root_path, WEBUI_MAX_PATH, "%s", path);

    // Update all windows. This will works only
    // for non-running windows.
    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (_webui.wins[i] != NULL) {
            WEBUI_SN_PRINTF_DYN(_webui.wins[i]->server_root_path, WEBUI_MAX_PATH, 
                "%s", _webui.default_server_root_path);
        }
    }

    return true;
}

// -- Interface's Functions ----------------
static void _webui_interface_bind_handler(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_interface_bind_handler()\n");
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[e->window];

    // Check for all events-bind functions
    if (win->has_all_events) {
        size_t events_cb_index = 0;
        bool exist = _webui_get_cb_index(win, "", &events_cb_index); 
        if (exist && win->cb_interface[events_cb_index] != NULL) {
            // Call user all-events cb
            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_interface_bind_handler() -> User all-events callback @ 0x%p\n",
                win->cb_interface[events_cb_index]
            );
            printf("[Core]\t\t_webui_interface_bind_handler() -> User all-events e->event_type [%zu]\n", e->event_type);
            printf("[Core]\t\t_webui_interface_bind_handler() -> User all-events e->element [%s]\n", e->element);
            printf("[Core]\t\t_webui_interface_bind_handler() -> User all-events e->event_number %zu\n", e->event_number);
            printf("[Core]\t\t_webui_interface_bind_handler() -> User all-events e->bind_id %zu\n", e->bind_id);
            #endif
            // Call all-events cb
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_interface_bind_handler() -> Calling user "
                "all-events callback\n[Call]\n");
            #endif
            win->cb_interface[events_cb_index](e->window, e->event_type, e->element, e->event_number, e->bind_id);
        }
    }

    // Check for the regular bind functions
    if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) && !_webui_is_empty(e->element)) {
        size_t cb_index = 0;
        bool exist = _webui_get_cb_index(win, e->element, &cb_index);
        if (exist && win->cb_interface[cb_index] != NULL) {
            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_interface_bind_handler() -> User callback @ 0x%p\n",
                win->cb_interface[cb_index]
            );
            printf("[Core]\t\t_webui_interface_bind_handler() -> e->event_type [%zu]\n", e->event_type);
            printf("[Core]\t\t_webui_interface_bind_handler() -> e->element [%s]\n", e->element);
            printf("[Core]\t\t_webui_interface_bind_handler() -> e->event_number %zu\n", e->event_number);
            printf("[Core]\t\t_webui_interface_bind_handler() -> e->bind_id %zu\n", e->bind_id);
            #endif
            // Call cb
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_interface_bind_handler() -> Calling user "
                "callback\n[Call]\n");
            #endif
            win->cb_interface[cb_index](e->window, e->event_type, e->element, e->event_number, e->bind_id);
        }
    }

    #ifdef WEBUI_LOG
    // Print cb response
    char* response = NULL;
    // Get event inf
    webui_event_inf_t* event_inf = win->events[e->event_number];
    if (event_inf != NULL) {
        if (event_inf->response != NULL)
            response = event_inf->response;
    }
    printf(
        "[Core]\t\t_webui_interface_bind_handler() -> user-callback response [%s]\n",
        response
    );
    #endif
}

const char* webui_interface_get_string_at(size_t window, size_t event_number, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_string_at([%zu], [%zu], [%zu])\n", window, event_number, index);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return NULL;
    _webui_window_t* win = _webui.wins[window];

    // New Event (Wrapper)
    webui_event_t e;
    e.window = window;
    e.event_number = event_number;

    return webui_get_string_at(&e, index);
}

long long int webui_interface_get_int_at(size_t window, size_t event_number, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_int_at([%zu], [%zu], [%zu])\n", window, event_number, index);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    // New Event (Wrapper)
    webui_event_t e;
    e.window = window;
    e.event_number = event_number;

    return webui_get_int_at(&e, index);
}

double webui_interface_get_float_at(size_t window, size_t event_number, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_float_at([%zu], [%zu], [%zu])\n", window, event_number, index);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return ((double)(0.0));
    _webui_window_t* win = _webui.wins[window];

    // New Event (Wrapper)
    webui_event_t e;
    e.window = window;
    e.event_number = event_number;

    return webui_get_float_at(&e, index);
}

bool webui_interface_get_bool_at(size_t window, size_t event_number, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_bool_at([%zu], [%zu], [%zu])\n", window, event_number, index);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return false;
    _webui_window_t* win = _webui.wins[window];

    // New Event (Wrapper)
    webui_event_t e;
    e.window = window;
    e.event_number = event_number;

    return webui_get_bool_at(&e, index);
}

size_t webui_interface_get_size_at(size_t window, size_t event_number, size_t index) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_size_at([%zu], [%zu], [%zu])\n", window, event_number, index);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    // New Event (Wrapper)
    webui_event_t e;
    e.window = window;
    e.event_number = event_number;

    return webui_get_size_at(&e, index);
}

size_t webui_interface_bind(size_t window, const char* element, void(*func)(size_t, size_t, char* , size_t, size_t)) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_bind([%zu], [%s], [0x%p])\n", window, element, func);
    #endif

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    // Bind
    size_t cb_index = webui_bind(window, element, _webui_interface_bind_handler);
    win->cb_interface[cb_index] = func;
    return cb_index;
}

void webui_interface_set_response(size_t window, size_t event_number, const char* response) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_set_response()\n");
    printf("[User] webui_interface_set_response() -> event_number %zu \n", event_number);
    printf("[User] webui_interface_set_response() -> Response [%s] \n", response);
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return;
    _webui_window_t* win = _webui.wins[window];

    // Get event inf
    webui_event_inf_t* event_inf = win->events[event_number];
    if (event_inf == NULL)
        return;

    // Free
    if (event_inf->response != NULL)
        _webui_free_mem((void*)event_inf->response);

    // Set the response
    size_t len = _webui_strlen(response);
    event_inf->response = (char*)_webui_malloc(len);
    WEBUI_STR_COPY_DYN(event_inf->response, len, response);

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_set_response() -> Internal buffer [%s] \n", event_inf->response);
    #endif
}

bool webui_interface_is_app_running(void) {

    #ifdef WEBUI_LOG
    // printf("[User] webui_is_app_running()\n");
    #endif

    // Stop if already flagged
    static bool app_is_running = true;
    if (!app_is_running)
        return false;

    // Initialization
    _webui_init();
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return false;

    // Get app status
    if (_webui.startup_timeout > 0) {
        if (_webui.servers < 1)
            app_is_running = false;
    }

    #ifdef WEBUI_LOG
    if (!app_is_running)
        printf("[User] webui_is_app_running() -> App Stopped\n");
    #endif

    return app_is_running;
}

size_t webui_interface_get_window_id(size_t window) {

    #ifdef WEBUI_LOG
    printf("[User] webui_interface_get_window_id()\n");
    #endif

    // Initialization
    _webui_init();

    // Dereference
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
        return 0;
    _webui_window_t* win = _webui.wins[window];

    return win->num;
}

// -- Core's Functions ----------------
static bool _webui_ptr_exist(void * ptr) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_ptr_exist()\n");
    #endif

    if (ptr == NULL)
        return false;

    for (size_t i = 0; i < _webui.ptr_position; i++) {

        if (_webui.ptr_list[i] == ptr)
            return true;
    }

    return false;
}

static void _webui_ptr_add(void * ptr, size_t size) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_ptr_add(0x%p)\n", ptr);
    #endif

    if (ptr == NULL)
        return;

    if (!_webui_ptr_exist(ptr)) {

        for (size_t i = 0; i < _webui.ptr_position; i++) {

            if (_webui.ptr_list[i] == NULL) {

                #ifdef WEBUI_LOG_VERBOSE
                printf("[Core]\t\t_webui_ptr_add(0x%p) -> Allocate %zu bytes\n", ptr, size);
                #endif

                _webui.ptr_list[i] = ptr;
                _webui.ptr_size[i] = size;
                return;
            }
        }

        #ifdef WEBUI_LOG_VERBOSE
        printf("[Core]\t\t_webui_ptr_add(0x%p) -> Allocate %zu bytes\n", ptr, size);
        #endif

        _webui.ptr_list[_webui.ptr_position] = ptr;
        _webui.ptr_size[_webui.ptr_position] = size;
        _webui.ptr_position++;
        if (_webui.ptr_position >= WEBUI_MAX_IDS)
            _webui.ptr_position = (WEBUI_MAX_IDS - 1);
    }
}

static void _webui_free_mem(void * ptr) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_free_mem(0x%p)\n", ptr);
    #endif

    if (ptr == NULL)
        return;

    for (size_t i = 0; i < _webui.ptr_position; i++) {

        if (_webui.ptr_list[i] == ptr) {

            #ifdef WEBUI_LOG_VERBOSE
            printf("[Core]\t\t_webui_free_mem(0x%p) -> Free %zu bytes\n", ptr, _webui.ptr_size[i]);
            #endif

            free(ptr);

            _webui.ptr_size[i] = 0;
            _webui.ptr_list[i] = NULL;
        }
    }

    for (int i = _webui.ptr_position; i >= 0;i--) {

        if (_webui.ptr_list[i] == NULL) {

            _webui.ptr_position = i;
            break;
        }
    }
}

static void _webui_free_all_mem(void) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_free_all_mem()\n");
    #endif

    // Makes sure we run this once
    static bool freed = false;
    if (freed)
        return;
    freed = true;

    void * ptr = NULL;
    for (size_t i = 0; i < _webui.ptr_position; i++) {

        ptr = _webui.ptr_list[i];

        if (ptr != NULL) {

            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_free_all_mem() -> Free %zu bytes @ 0x%p\n", _webui.ptr_size[i], ptr
            );
            #endif

            free(ptr);
        }
    }
}

static void _webui_panic(char* msg) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_panic() -> %s.\n", msg);
    #endif

    fprintf(stderr, "WebUI Error: %s.\n", msg);
    webui_exit();
}

static size_t _webui_mb(size_t size) {

    // Round `size` to fit a memory block
    // 07 -> 8
    // 14 -> 16
    // 29 -> 32
    // 55 -> 64
    // ...

    // Make sure we have the null
    // terminator if it's a string
    size++;

    // If size is negative
    if (size < 4)
        size = 4;

    // If size is already a full block
    // we should return the same block
    size--;

    size_t block_size = 4;
    while(block_size <= size)
        block_size *= 2;

    return block_size;
}

static void * _webui_malloc(size_t size) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_malloc([%zu])\n", size);
    #endif

    size = _webui_mb(size);

    void * block = NULL;
    for (size_t i = 0; i < 8; i++) {

        if (size > WEBUI_MAX_BUF)
            size = WEBUI_MAX_BUF;

        block = malloc(size);

        if (block == NULL)
            size++;
        else
            break;
    }

    if (block == NULL) {

        WEBUI_ASSERT("malloc() failed");
        return NULL;
    }

    memset(block, 0, size);

    _webui_ptr_add((void*)block, size);

    return block;
}

static _webui_window_t* _webui_dereference_win_ptr(void * ptr) {

    #ifdef WEBUI_LOG_VERBOSE
    //printf("[Core]\t\t_webui_dereference_win_ptr()\n");
    #endif

    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
        return NULL;

    _webui_window_t* win = (_webui_window_t* ) ptr;

    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if (_webui.wins[i] == win)
            return win;
    }

    // This pointer is not a valid
    // webui window struct
    return NULL;
}

static void _webui_sleep(long unsigned int ms) {

    #ifdef WEBUI_LOG_VERBOSE
    // printf("[Core]\t\t_webui_sleep([%zu])\n", ms);
    #endif

    #ifdef _WIN32
    Sleep(ms);
    #else
    struct timespec req;
    req.tv_sec = ms / 1000; // Convert ms to seconds
    req.tv_nsec = (ms % 1000) * 1000000L; // Convert remainder to nanoseconds
    nanosleep(&req, NULL);
    #endif
}

static long _webui_timer_diff(struct timespec * start, struct timespec* end) {

    #ifdef WEBUI_LOG_VERBOSE
    // printf("[Core]\t\t_webui_timer_diff()\n");
    #endif

    return ((long)(end->tv_sec * 1000) + (long)(end->tv_nsec / 1000000)) -
        ((long)(start->tv_sec * 1000) + (long)(start->tv_nsec / 1000000));
}

static void _webui_timer_clock_gettime(struct timespec * spec) {

    #ifdef WEBUI_LOG_VERBOSE
    // printf("[Core]\t\t_webui_timer_clock_gettime()\n");
    #endif

    #ifdef _WIN32
    __int64 wintime;
    GetSystemTimeAsFileTime((FILETIME * )&wintime);
    wintime -= ((__int64) 116444736000000000);
    spec->tv_sec = wintime / ((__int64) 10000000);
    spec->tv_nsec = wintime % ((__int64) 10000000) * 100;
    #else
    clock_gettime(CLOCK_MONOTONIC, spec);
    #endif
}

static void _webui_timer_start(_webui_timer_t* t) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_timer_start()\n");
    #endif

    _webui_timer_clock_gettime(&t->start);
}

static bool _webui_timer_is_end(_webui_timer_t* t, size_t ms) {

    #ifdef WEBUI_LOG_VERBOSE
    // printf("[Core]\t\t_webui_timer_is_end()\n");
    #endif

    _webui_timer_clock_gettime(&t->now);

    size_t def = (size_t) _webui_timer_diff(&t->start,&t->now);
    if (def > ms)
        return true;
    return false;
}

static bool _webui_is_empty(const char* s) {

    #ifdef WEBUI_LOG_VERBOSE
    //printf("[Core]\t\t_webui_is_empty()\n");
    #endif

    if ((s != NULL) && (s[0] != '\0'))
        return false;
    return true;
}

static size_t _webui_strlen(const char* s) {

    #ifdef WEBUI_LOG_VERBOSE
    //printf("[Core]\t\t_webui_strlen()\n");
    #endif

    if (_webui_is_empty(s))
        return 0;

    size_t length = 0;

    while((s[length] != '\0') && (length < WEBUI_MAX_BUF)) {
        length++;
    }

    return length;
}

static bool _webui_file_exist_mg(_webui_window_t* win, struct mg_connection* client) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_file_exist_mg()\n");
    #endif

    char* file;
    char* full_path;

    const struct mg_request_info * ri = mg_get_request_info(client);
    const char* url = ri->local_uri;
    size_t url_len = _webui_strlen(url);

    // Get file name
    file = (char*)_webui_malloc(url_len);
    const char* p = url;
    p++; // Skip "/"
    WEBUI_SN_PRINTF_DYN(file, url_len, "%.*s", (int)(url_len - 1), p);

    // Get full path
    // [current folder][/][file]
    size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + _webui_strlen(file));
    full_path = (char*)_webui_malloc(bf_len);
    WEBUI_SN_PRINTF_DYN(full_path, bf_len, "%s%s%s", win->server_root_path, os_sep, file);

    bool exist = _webui_file_exist(full_path);

    _webui_free_mem((void*)file);
    _webui_free_mem((void*)full_path);

    return exist;
}

static bool _webui_is_valid_url(const char* url) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_is_valid_url([%.8s...])\n", url);
    #endif

    if ((_webui_is_empty(url)) || (url[0] != 'h'))
        return false;
    if (strncmp(url, "http://", 7) == 0 || strncmp(url, "https://", 8) == 0)
        return true;
    return false;
}

static bool _webui_open_url_native(const char* url) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_open_url_native([%s])\n", url);
    #endif

    #if defined(_WIN32)
    HINSTANCE result = ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
    return ((INT_PTR) result > 32);
    #elif defined(__APPLE__)
    char command[1024];
    WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "open \"%s\"", url);
    return (system(command) == 0);
    #else
    // Assuming Linux
    char command[1024];
    WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "xdg-open \"%s\"", url);
    return (system(command) == 0);
    #endif
}

static bool _webui_file_exist(const char* path) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_file_exist([%s])\n", path);
    #endif

    if (_webui_is_empty(path))
        return false;

    // Parse home environments in the path
    #ifdef _WIN32
    // Windows
    char full_path[WEBUI_MAX_PATH];
    ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
    #else
    // Linux / macOS
    char full_path[WEBUI_MAX_PATH];
    if (path[0] == '~') {
        const char* home = getenv("HOME");
        if (home) {
            WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home, &path[1]);
        } else {
            // If for some reason HOME isn't set
            // fall back to the original path.
            strncpy(full_path, path, sizeof(full_path));
        }
    } else {
        strncpy(full_path, path, sizeof(full_path));
    }
    #endif

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_file_exist() -> Parsed to [%s]\n", full_path);
    #endif

    #if defined(_WIN32)
    // Convert UTF-8 to wide string on Windows
    wchar_t* wfilePath;
    if (!_webui_str_to_wide(full_path, &wfilePath))
        return false;
    DWORD dwAttrib = GetFileAttributesW(wfilePath);
    _webui_free_mem((void*)wfilePath);
    return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib&FILE_ATTRIBUTE_DIRECTORY));
    #else
    // Linux / macOS
    return (WEBUI_FILE_EXIST(full_path, 0) == 0);
    #endif
}

static const char* _webui_get_extension(const char* f) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_extension()\n");
    #endif

    if (f == NULL)
        return "";

    const char* ext = strrchr(f, '.');

    if (ext == NULL || !ext || ext == f)
        return "";
    return ext + 1;
}

static uint16_t _webui_get_run_id(void) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_run_id()\n");
    #endif

    if (_webui.run_last_id >= WEBUI_MAX_IDS)
        _webui.run_last_id = 0;
    return _webui.run_last_id++;
}

static bool _webui_socket_test_listen_mg(size_t port_num) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_socket_test_listen_mg([%zu])\n", port_num);
    #endif

    // HTTP Port Test
    char* test_port = (char*)_webui_malloc(64);
    WEBUI_SN_PRINTF_DYN(test_port, 64, "127.0.0.1:%zu", port_num);

    // Start HTTP Server
    const char* http_options[] = {
        "listening_ports",
        test_port,
        NULL,
        NULL
    };
    struct mg_callbacks http_callbacks;
    struct mg_context * http_ctx;
    memset(&http_callbacks, 0, sizeof(http_callbacks));
    http_ctx = mg_start(&http_callbacks, 0, http_options);

    if (http_ctx == NULL) {

        // Cannot listen
        mg_stop(http_ctx);
        return false;
    }

    // Listening success
    mg_stop(http_ctx);

    return true;
}

static bool _webui_port_is_used(size_t port_num) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_port_is_used([%zu])\n", port_num);
    #endif

    #ifdef _WIN32
    // Listener test
    if (!_webui_socket_test_listen_win32(port_num))
        return true; // Port is already used
    return false; // Port is not in use
    #else
    // Listener test MG
    if (!_webui_socket_test_listen_mg(port_num))
        return true; // Port is already used
    return false; // Port is not in use
    #endif
}

static char* _webui_get_file_name_from_url(const char* url) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_file_name_from_url([%s])\n", url);
    #endif

    if (_webui_is_empty(url))
        return NULL;

    // Find the position of "://"
    const char* pos = strstr(url, "://");
    if (pos == NULL) {
        pos = url;
    } else {
        // Move the position after "://"
        pos += 3;
    }

    // Find the position of the first '/'
    pos = strchr(pos, '/');
    if (pos == NULL) {
        // Invalid URL
        return NULL;
    } else {
        // Move the position after "/"
        pos++;
    }

    // Copy the path to a new string
    char* file = _webui_str_dup(pos);

    // Find the position of the first '?'
    char* question_mark = strchr(file, '?');
    if (question_mark != NULL) {
        // Replace '?' with NULL
        * question_mark = '\0';
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_file_name_from_url() -> File name: [%s]\n", file);
    #endif

    return file;
}

static char* _webui_get_full_path(_webui_window_t* win, const char* file) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_full_path([%s])\n", file);
    #endif

    if (!file)
        return NULL;
    
    if (file[0] == '/')
        file++;

    // Get full path
    // [current folder][/][file]
    size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + _webui_strlen(file));
    char* full_path = (char*)_webui_malloc(bf_len);
    WEBUI_SN_PRINTF_DYN(full_path, bf_len, "%s%s%s", win->server_root_path, os_sep, file);

    #ifdef _WIN32
    // Replace `/` by `\`
    for (int i = 0; full_path[i] != '\0'; i++) {
        if (full_path[i] == '/') {
            full_path[i] = *os_sep;
        }
    }
    #endif

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_get_full_path() -> Full path: [%s]\n", full_path);
    #endif

    return full_path;
}

static size_t _webui_new_event_inf(_webui_window_t* win, webui_event_inf_t** event_inf) {
    (*event_inf) = (webui_event_inf_t*)_webui_malloc(sizeof(webui_event_inf_t));
    if (win->events_count > WEBUI_MAX_ARG)
        win->events_count = 0;
    size_t event_num = win->events_count++;
    win->events[event_num] = (*event_inf);
    return event_num;
}

static void _webui_free_event_inf(_webui_window_t* win, size_t event_num) {
    webui_event_inf_t* event_inf = win->events[event_num];
    for (size_t i = 0; i < (WEBUI_MAX_ARG + 1); i++) {
        if (event_inf->event_data[i] != NULL)
            webui_free((void*)event_inf->event_data[i]);
    }
    if (event_inf->response != NULL)
        webui_free((void*)event_inf->response);
    webui_free((void*)event_inf);
    win->events[event_num] = NULL;
}

static int _webui_external_file_handler(_webui_window_t* win, struct mg_connection* client, size_t client_id) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_external_file_handler()\n");
    #endif

    int http_status_code = 0;
    const struct mg_request_info * ri = mg_get_request_info(client);
    const char* url = ri->local_uri;

    if (win->files_handler != NULL || win->files_handler_window != NULL) {
        // Get file content from the external files handler
        size_t length = 0;

        // This is a breaking changes, should be
        // added in the next major WebUI version.
        // 
        // New Event (HTTP)
        // webui_event_t e;
        // e.window = win->num;
        // e.event_type = WEBUI_EVENT_HTTP_GET;
        // e.element = "";
        // e.event_number = 0;
        // e.bind_id = 0;
        // e.connection_id = 0;
        // e.client_id = client_id;
        // e.cookies = "";

        // Files handler callback
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_external_file_handler() -> Path [%s]\n", url);
        printf("[Core]\t\t_webui_external_file_handler() -> Calling custom files handler callback\n");
        printf("[Call]\n");
        #endif

        // True if we pass the window num to the handler, false otherwise.
        int is_file_handler_window = win->files_handler_window != NULL;
        const void* callback_resp = is_file_handler_window ? win->files_handler_window(win->num, url, (int*)&length) : win->files_handler(url, (int*)&length);

        if (callback_resp != NULL) {

            // File content found

            if (length == 0)
                length = _webui_strlen(callback_resp);

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_external_file_handler() -> Custom files handler found (%zu bytes)\n",
                length
            );
            #endif

            #ifdef WEBUI_LOG
            printf("---[ External File Handler ]--------\n");
            printf("%s\n", (char*)callback_resp);
            printf("------------------------------------\n");
            #endif

            // Send user data (Header + Body)
            mg_write(client, callback_resp, length);
            
            // Safely free resources if end-user allocated
            // using `webui_malloc()`. Otherwise just do nothing.
            _webui_free_mem((void*)callback_resp);

            http_status_code = 200;
        }
        else {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_external_file_handler() -> Custom files handler failed\n");
            #endif
        }

        // If `data == NULL` thats mean the external handler
        // did not find the requested file. So WebUI will try
        // looking for the file locally.
    }

    return http_status_code;
}

static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client, size_t client_id) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_serve_file()\n");
    #endif

    // Serve a normal text based file

    int http_status_code = 0;
    const struct mg_request_info * ri = mg_get_request_info(client);
    const char* url = ri->local_uri;

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_serve_file() -> Looking for file locally\n");
    #endif

    // Get full path
    char* file = _webui_get_file_name_from_url(url);
    char* full_path = _webui_get_full_path(win, file);

    if (_webui_file_exist(full_path)) {

        // 200 - File exist
        _webui_http_send_file(win, client, mg_get_builtin_mime_type(url), full_path, false);
        http_status_code = 200;
    }
    else {

        // 404 - File not exist
        _webui_http_send_error(client, webui_html_res_not_available, 404);
        http_status_code = 404;
    }

    _webui_free_mem((void*)full_path);
    _webui_free_mem((void*)file);

    return http_status_code;
}

static bool _webui_bun_exist(_webui_window_t* win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_bun_exist()\n");
    #endif

    static bool found = false;

    if (found)
        return true;

    if (_webui_cmd_sync(win, "bun -v", false) == 0) {

        found = true;
        return true;
    } else
        return false;
}

static bool _webui_deno_exist(_webui_window_t* win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_deno_exist()\n");
    #endif

    static bool found = false;

    if (found)
        return true;

    if (_webui_cmd_sync(win, "deno --version", false) == 0) {

        found = true;
        return true;
    } else
        return false;
}

static bool _webui_nodejs_exist(_webui_window_t* win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_nodejs_exist()\n");
    #endif

    static bool found = false;

    if (found)
        return true;

    if (_webui_cmd_sync(win, "node -v", false) == 0) {

        found = true;
        return true;
    } else
        return false;
}

static const char* _webui_interpret_command(const char* cmd) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_interpret_command([%s])\n", cmd);
    #endif

    // Run the command with redirection of errors to stdout
    // and return the output.

    // Output buffer
    char* out = NULL;

    #ifdef _WIN32
    // Redirect stderr to stdout
    char cmd_with_redirection[512] = {0};
    WEBUI_SN_PRINTF_STATIC(cmd_with_redirection, sizeof(cmd_with_redirection), "cmd.exe /c %s 2>&1", cmd);
    _webui_system_win32_out(cmd_with_redirection,&out, false);
    #else
    // Redirect stderr to stdout
    char cmd_with_redirection[512] = {0};
    WEBUI_SN_PRINTF_STATIC(cmd_with_redirection, sizeof(cmd_with_redirection), "%s 2>&1", cmd);

    FILE * pipe = WEBUI_POPEN(cmd_with_redirection, "r");

    if (pipe == NULL)
        return NULL;

    // Read STDOUT
    out = (char*)_webui_malloc(WEBUI_STDOUT_BUF);
    char* line = (char*)_webui_malloc(1024);
    while(fgets(line, 1024, pipe) != NULL)
        WEBUI_STR_CAT_DYN(out, WEBUI_STDOUT_BUF, line);
    WEBUI_PCLOSE(pipe);

    // Clean
    _webui_free_mem((void*)line);
    #endif

    return (const char*)out;
}

static void _webui_condition_init(webui_condition_t* cond) {

    #ifdef _WIN32
    InitializeConditionVariable(cond);
    #else
    pthread_cond_init(cond, NULL);
    #endif
}

static void _webui_condition_wait(webui_condition_t* cond, webui_mutex_t* mutex) {

    #ifdef _WIN32
    SleepConditionVariableCS(cond, mutex, INFINITE);
    #else
    pthread_cond_wait(cond, mutex);
    #endif
}

static void _webui_condition_signal(webui_condition_t* cond) {

    #ifdef _WIN32
    WakeConditionVariable(cond);
    #else
    pthread_cond_signal(cond);
    #endif
}

static void _webui_condition_destroy(webui_condition_t* cond) {

    #ifdef _WIN32
    // No need
    (void)cond;
    #else
    pthread_cond_destroy(cond);
    #endif
}

static void _webui_mutex_init(webui_mutex_t* mutex) {

    #ifdef _WIN32
    InitializeCriticalSection(mutex);
    #else
    pthread_mutex_init(mutex, NULL);
    #endif
}

static void _webui_mutex_lock(webui_mutex_t* mutex) {

    #ifdef _WIN32
    EnterCriticalSection(mutex);
    #else
    pthread_mutex_lock(mutex);
    #endif
}

static void _webui_mutex_unlock(webui_mutex_t* mutex) {

    #ifdef _WIN32
    LeaveCriticalSection(mutex);
    #else
    pthread_mutex_unlock(mutex);
    #endif
}

static void _webui_mutex_destroy(webui_mutex_t* mutex) {

    #ifdef _WIN32
    DeleteCriticalSection(mutex);
    #else
    pthread_mutex_destroy(mutex);
    #endif
}

static int _webui_interpret_file(_webui_window_t* win, struct mg_connection* client, char* index, size_t client_id) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_interpret_file()\n");
    #endif

    // Interpret the file using JavaScript/TypeScript runtimes
    // and send back the output. otherwise, send the file as a normal text based

    int interpret_http_stat = 200;
    char* file = NULL;
    char* full_path = NULL;
    const char* query = NULL;

    const struct mg_request_info * ri = mg_get_request_info(client);
    const char* url = ri->local_uri;

    // Get file full path
    if (index != NULL && !_webui_is_empty(index)) {

        // Parse as index file

        file = index;
        full_path = index;
    } else {

        // Parse as other non-index files

        // Get file name
        file = _webui_get_file_name_from_url(url);

        // Get full path
        full_path = _webui_get_full_path(win, file);
    }

    // Get file extension
    const char* extension = _webui_get_extension(file);

    if (strcmp(extension, "ts") == 0 || strcmp(extension, "js") == 0) {

        // TypeScript / JavaScript

        if (!_webui_file_exist(full_path)) {

            // File not exist - 404
            _webui_http_send_error(client, webui_html_res_not_available, 404);

            _webui_free_mem((void*)file);
            _webui_free_mem((void*)full_path);
            return 404;
        }

        // Get query
        query = ri->query_string;

        if (win->runtime == Deno) {

            // Use Deno
            if (_webui_deno_exist(win)) {

                // Set command
                // [disable coloring][deno][file][query]
                size_t bf_len = (128 + _webui_strlen(full_path) + _webui_strlen(query));
                char* cmd = (char*)_webui_malloc(bf_len);
                #ifdef _WIN32
                WEBUI_SN_PRINTF_DYN(
                    cmd, bf_len,
                    "Set NO_COLOR=1&Set DENO_NO_UPDATE_CHECK=1&deno run --quiet --allow-all --unstable-ffi --allow-ffi \"%s\" \"%s\"",
                    full_path, query
                );
                #else
                WEBUI_SN_PRINTF_DYN(
                    cmd, bf_len,
                    "NO_COLOR=1;DENO_NO_UPDATE_CHECK=1;deno run --quiet --allow-all --unstable-ffi --allow-ffi \"%s\" \"%s\"",
                    full_path, query
                );
                #endif

                // Run command
                const char* out = _webui_interpret_command(cmd);

                if (out != NULL) {

                    // Send Deno output 200
                    _webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
                }
                else {

                    // Deno interpretation failed.
                    // Send back an empty `200 OK`
                    _webui_http_send(win, client, "text/plain", "", 0, false);
                }

                _webui_free_mem((void*)cmd);
                _webui_free_mem((void*)out);
            }
            else {

                // Deno not installed
                // Send back an empty `200 OK`
                _webui_http_send(win, client, "text/plain", "", 0, false);
            }
        } else if (win->runtime == Bun) {

            // Use Bun
            if (_webui_bun_exist(win)) {

                // Set command
                // [bun][file][query]
                size_t bf_len = (32 + _webui_strlen(full_path) + _webui_strlen(query));
                char* cmd = (char*)_webui_malloc(bf_len);
                WEBUI_SN_PRINTF_DYN(cmd, bf_len, "bun \"%s\" \"%s\"", full_path, query);

                // Run command
                const char* out = _webui_interpret_command(cmd);

                if (out != NULL) {

                    // Send Bun output 200
                    _webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
                }
                else {

                    // Bun interpretation failed.
                    // Send back an empty `200 OK`
                    _webui_http_send(win, client, "text/plain", "", 0, false);
                }

                _webui_free_mem((void*)cmd);
                _webui_free_mem((void*)out);
            }
            else {

                // Bun not installed
                // Send back an empty `200 OK`
                _webui_http_send(win, client, "text/plain", "", 0, false);
            }
        } else if (win->runtime == NodeJS) {

            // Use Nodejs

            if (_webui_nodejs_exist(win)) {

                // Set command
                // [node][file]
                size_t bf_len = (32 + _webui_strlen(full_path) + _webui_strlen(query));
                char* cmd = (char*)_webui_malloc(bf_len);
                WEBUI_SN_PRINTF_DYN(cmd, bf_len, "node \"%s\" \"%s\"", full_path, query);

                // Run command
                const char* out = _webui_interpret_command(cmd);

                if (out != NULL) {

                    // Send Node.js output 200
                    _webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
                }
                else {

                    // Node.js interpretation failed.
                    // Send back an empty `200 OK`
                    _webui_http_send(win, client, "text/plain", "", 0, false);
                }

                _webui_free_mem((void*)cmd);
                _webui_free_mem((void*)out);
            } else {

                // Node.js not installed
                // Send back an empty `200 OK`
                _webui_http_send(win, client, "text/plain", "", 0, false);
            }
        } else {

            // Unknown runtime
            // Serve as a normal text-based file 200
            _webui_http_send_file(win, client, mg_get_builtin_mime_type(url), full_path, false);
        }
    } else {

        // Unknown file extension

        // Serve as a normal text-based file
        interpret_http_stat = _webui_serve_file(win, client, client_id);
    }

    _webui_free_mem((void*)file);
    _webui_free_mem((void*)full_path);

    return interpret_http_stat;
}

static const char* _webui_generate_js_bridge(_webui_window_t* win, struct mg_connection* client) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_generate_js_bridge()\n");
    #endif

    _webui_mutex_lock(&_webui.mutex_bridge);

    // Token
    uint32_t token = 0x00000000;
    if (!_webui.config.multi_client) {
        // Single client mode
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            // Non-authorized request to `webui.js` because
            // the single client already connected.
            _webui_mutex_unlock(&_webui.mutex_bridge);
            return NULL;
        }
        if (win->token == 0) {
            win->token = _webui_generate_random_uint32();
            token = win->token;
        }
    }
    else {
        // Multi client mode
        if (win->token == 0) {
            win->token = _webui_generate_random_uint32();
        }
        token = win->token;
    }

    // Generate the full WebUI Bridge
    #ifdef WEBUI_LOG
    const char* log = "true";
    #else
    const char* log = "false";
    #endif
    size_t len = 256 + _webui_strlen((const char*)webui_javascript_bridge);
    char* js = (char*)_webui_malloc(len);
    #ifdef WEBUI_TLS
    const char* TLS = "true";
    #else
    const char* TLS = "false";
    #endif
    int c = WEBUI_SN_PRINTF_DYN(
        js, len,
        "%s\n document.addEventListener(\"DOMContentLoaded\",function(){ globalThis.webui = "
        "new WebuiBridge({ secure: %s, token: %" PRIu32 ", port: %zu, log: %s, ",
        webui_javascript_bridge, TLS, token, win->server_port, log
    );
    // Window Size
    if (win->size_set)
        c += WEBUI_SN_PRINTF_DYN(js + c, len, "winW: %u, winH: %u, ", win->width, win->height);
    // Window Position
    if (win->position_set)
        c += WEBUI_SN_PRINTF_DYN(js + c, len, "winX: %u, winY: %u, ", win->x, win->y);
    // Close
    WEBUI_STR_CAT_DYN(js, len, "});});");

    // Free
    _webui_mutex_unlock(&_webui.mutex_bridge);
    return js;
}

static bool _webui_mutex_is_connected(_webui_window_t* win, int update) {

    bool status = false;
    _webui_mutex_lock(&_webui.mutex_win_connect);
    if (update == WEBUI_MUTEX_TRUE) win->connected = true;
    else if (update == WEBUI_MUTEX_FALSE) win->connected = false;
    status = ((win->clients_count > 0) && (win->connected));
    _webui_mutex_unlock(&_webui.mutex_win_connect);
    return status;
}

static bool _webui_mutex_is_exit_now(int update) {

    bool status = false;
    _webui_mutex_lock(&_webui.mutex_exit_now);
    if (update == WEBUI_MUTEX_TRUE) _webui.exit_now = true;
    else if (update == WEBUI_MUTEX_FALSE) _webui.exit_now = false;
    status = _webui.exit_now;
    _webui_mutex_unlock(&_webui.mutex_exit_now);
    return status;
}

static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update) {

    bool status = false;
    _webui_mutex_lock(&_webui.mutex_webview_stop);
    if (update == WEBUI_MUTEX_TRUE) win->update_webview = true;
    else if (update == WEBUI_MUTEX_FALSE) win->update_webview = false;
    status = win->update_webview;
    _webui_mutex_unlock(&_webui.mutex_webview_stop);
    return status;
}

static bool _webui_browser_create_new_profile(_webui_window_t* win, size_t browser) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_create_new_profile(%zu)\n", browser);
    #endif

    // Default local machine profile
    if (win->default_profile)
        return true;

    if (win->custom_profile) {

        // Custom profile
        if (_webui_is_empty(win->profile_path) || _webui_is_empty(win->profile_name))
            return false;
    } else {

        // WebUI profile
        if (win->profile_name != NULL)
            _webui_free_mem((void*)win->profile_name);
        if (win->profile_path != NULL)
            _webui_free_mem((void*)win->profile_path);
        win->profile_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
        win->profile_name = (char*)_webui_malloc(WEBUI_MAX_PATH);
        if(!win->disable_browser_high_contrast)
            WEBUI_STR_COPY_DYN(win->profile_name, WEBUI_MAX_PATH, WEBUI_PROFILE_NAME);
        else
            WEBUI_STR_COPY_DYN(win->profile_name, WEBUI_MAX_PATH, WEBUI_PROFILE_NAME "-NoHC");
    }

    #ifdef WEBUI_LOG
    printf(
        "[Core]\t\t_webui_browser_create_new_profile(%zu) -> Generating WebUI profile\n",
        browser
    );
    #endif

    // Temp folder
    const char* temp = _webui_get_temp_path();

    if (browser == Chrome) {

        // Google Chrome
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIChromeProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Edge) {

        // Edge
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIEdgeProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Epic) {

        // Epic
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIEpicProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Vivaldi) {

        // Vivaldi
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIVivaldiProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Brave) {

        // Brave
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIBraveProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Yandex) {

        // Yandex
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIYandexProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Chromium) {

        // Chromium
        if (!win->custom_profile)
            WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIChromiumProfile", temp, os_sep, os_sep);
        return true;
    } else if (browser == Firefox) {

        // Firefox (We need to create the profile folder)

        #ifdef _WIN32
        // Windows
        const char* path_ini = "%APPDATA%\\Mozilla\\Firefox\\profiles.ini";
        #elif __linux__
        // Linux
        bool snap = false;
        const char* path_ini = "";
        if (_webui_file_exist("~/.mozilla/firefox/profiles.ini"))
            path_ini = "~/.mozilla/firefox/profiles.ini";
        else if (_webui_file_exist("~/snap/firefox/common/.mozilla/firefox/profiles.ini")) {
            path_ini = "~/snap/firefox/common/.mozilla/firefox/profiles.ini";
            snap = true;
        }
        else return false;
        // Firefox linux snap version
        // ~/snap/firefox/common/.mozilla/firefox/WebUIFirefoxProfile
        char snap_path[WEBUI_MAX_PATH];
        const char* home = getenv("HOME");
        if (home) {
            WEBUI_SN_PRINTF_STATIC(snap_path, sizeof(snap_path), "%s/snap/firefox/common/.mozilla/firefox", home);
            temp = snap_path;
        } else {
            // Failed to get HOME
            return false;
        }
        #else
        // macOS
        const char* path_ini = "~/Library/Application Support/Firefox/profiles.ini";
        #endif

        if (!win->custom_profile){
            if(!win->disable_browser_high_contrast)
                WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIFirefoxProfile", temp, os_sep, os_sep);
            else
                WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIFirefoxProfile-NoHC", temp, os_sep, os_sep);
        }

        if (!_webui_folder_exist(win->profile_path) ||
            !_webui_is_firefox_ini_profile_exist(path_ini, win->profile_name)) {

            char buf[2048] = {0};

            // There is a possibility that the profile name
            // does not exist in the INI file. or folder does not exist.
            // let's delete the profile name from the ini file, and folder.
            _webui_remove_firefox_profile_ini(path_ini, win->profile_name);
            _webui_delete_folder(win->profile_path);

            // Creating the Firefox profile
            WEBUI_SN_PRINTF_STATIC(
                buf, sizeof(buf), "%s -CreateProfile \"%s %s\"", win->browser_path, win->profile_name, win->profile_path
            );
            _webui_cmd_sync(win, buf, false);

            // Wait for Firefox profile to be created
            _webui_timer_t timer;
            _webui_timer_start(&timer);
            for (;;) {
                _webui_sleep(1000);
                if (_webui_folder_exist(win->profile_path))
                    break;
                if (_webui_timer_is_end(&timer, 10000))
                    break;
            }

            // Check if Firefox profile is created
            if (!_webui_folder_exist(win->profile_path) ||
                !_webui_is_firefox_ini_profile_exist(path_ini, win->profile_name))
                return false;

            // prefs.js
            FILE * file;
            WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s%sprefs.js", win->profile_path, os_sep);
            WEBUI_FILE_OPEN(file, buf, "a");
            if (file == NULL)
                return false;
            fputs(
                "user_pref(\"toolkit.legacyUserProfileCustomizations.stylesheets\","
                " true);",
                file
            );
            fputs("user_pref(\"browser.shell.checkDefaultBrowser\", false);", file);
            fputs("user_pref(\"browser.tabs.warnOnClose\", false);", file);
            fputs("user_pref(\"browser.tabs.inTitlebar\", 0);", file);
            if(win->disable_browser_high_contrast){
                fputs("user_pref(\"browser.display.document_color_use\", 1);", file);
            }
            fclose(file);

            // userChrome.css
            WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "\"%s%schrome%s\"", win->profile_path, os_sep, os_sep);
            if (!_webui_folder_exist(buf)) {

                WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "mkdir \"%s%schrome%s\"", win->profile_path, os_sep, os_sep);
                _webui_cmd_sync(win, buf, false); // Create directory
            }
            WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s%schrome%suserChrome.css", win->profile_path, os_sep, os_sep);
            WEBUI_FILE_OPEN(file, buf, "a");
            if (file == NULL)
                return false;
            fputs(
                "#navigator-toolbox{opacity:0 !important;height:0px !important;max-height:0px !important;"
                "width:0px !important;max-width:0px !important;}"
            , file);
            fclose(file);
        }

        return true;
    }

    return false;
}

static bool _webui_folder_exist(const char* folder) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_folder_exist([%s])\n", folder);
    #endif

    #if defined(_WIN32)
    DWORD attributes = GetFileAttributesA(folder);
    if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY))
        return true;
    #else
    DIR * dir = opendir(folder);
    if (dir) {
        closedir(dir);
        return true;
    }
    #endif

    return false;
}

static void _webui_delete_folder(char* folder) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_delete_folder([%s])\n", folder);
    #endif

    char command[1024];
    #if defined(_WIN32)
    WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "cmd /c \"rmdir /s /q \"%s\"\" > nul 2>&1", folder);
    #else
    WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "rm -rf \"%s\" >>/dev/null 2>>/dev/null", folder);
    #endif

    // Try 6 times in 3 seconds
    for (size_t i = 0; i < 6; i++) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_delete_folder() -> Running [%s] \n", command);
        #endif

        #if defined(_WIN32)
        _webui_system_win32(NULL, command, false);
        #else
        system(command);
        #endif
        
        if (!_webui_folder_exist(folder))
            break;
        _webui_sleep(500);
    }
}

static uint32_t _webui_get_token(const char* data) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_token()\n");
    #endif

    uint32_t token = 0;

    // Little-endian
    token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN]&0xFF);
    token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 1]&0xFF) << 8;
    token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 2]&0xFF) << 16;
    token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 3]&0xFF) << 24;

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_token() -> 0x%08X\n", token);
    #endif

    return token;
}

static uint16_t _webui_get_id(const char* data) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_id()\n");
    #endif

    uint16_t id = 0;

    // Little-endian
    id |= ((uint16_t) data[WEBUI_PROTOCOL_ID]&0xFF);
    id |= ((uint16_t) data[WEBUI_PROTOCOL_ID + 1]&0xFF) << 8;

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_id() -> 0x%04X\n", id);
    #endif

    return id;
}

static void _webui_send_all(_webui_window_t* win, uint16_t id, unsigned char cmd, const char* data, size_t len) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_send_all()\n");
    #endif

    // Send the WebSocket packet to a all connected clients if
    // `multi_client` mode is enabled, if not then send packet
    // to the only single connected client.
    
    // Send the packet
    if (_webui.config.multi_client) {
        // Loop trough all connected clients in this window
        for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
            if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) && (_webui.clients_token_check[i])) {
                _webui_send_client(win, _webui.clients[i], 0, cmd, data, len, false);
            }
        }
    } else {
        // Single client
        if ((win->single_client != NULL) && (win->single_client_token_check)) {
            _webui_send_client(win, win->single_client, 0, cmd, data, len, false);
        }
    }
}

static void _webui_send_client(
    _webui_window_t* win, struct mg_connection *client, 
    uint16_t id, unsigned char cmd, const char* data, size_t len, bool token_bypass) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_send_client()\n");
    #endif

    // Get connection id
    size_t connection_id = 0;
    if (!_webui_connection_get_id(win, client, &connection_id))
        return;
    if (_webui.clients[connection_id] == NULL)
        return;
    
    // Check Token
    if (!token_bypass) {
        if (!_webui.clients_token_check[connection_id])
            return;
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_send_client() -> ID = 0x%04X \n", id);
    printf("[Core]\t\t_webui_send_client() -> CMD = 0x%02x \n", cmd);
    printf("[Core]\t\t_webui_send_client() -> Data = %zu bytes \n", len);
    #endif

    // Protocol
    // 0: [SIGNATURE]
    // 1: [TOKEN]
    // 2: [ID]
    // 3: [CMD]
    // 4: [Data]

    // Prepare the packet
    size_t packet_len = WEBUI_PROTOCOL_SIZE + len + 1;
    char* packet = (char*)_webui_malloc(packet_len);

    // Signature (1 Byte)
    packet[WEBUI_PROTOCOL_SIGN] = WEBUI_SIGNATURE;

    // Little-endian
    // Token (4 Bytes)
    packet[WEBUI_PROTOCOL_TOKEN] = 0xFF;
    packet[WEBUI_PROTOCOL_TOKEN + 1] = 0xFF;
    packet[WEBUI_PROTOCOL_TOKEN + 2] = 0xFF;
    packet[WEBUI_PROTOCOL_TOKEN + 3] = 0xFF;

    // ID (2 Bytes)
    packet[WEBUI_PROTOCOL_ID] = id&0xFF;
    packet[WEBUI_PROTOCOL_ID + 1] = (id >> 8)&0xFF;

    // Command (1 Byte)
    packet[WEBUI_PROTOCOL_CMD] = cmd;

    // Data (n Bytes)
    if (len > 0) {
        size_t j = WEBUI_PROTOCOL_DATA;
        for (size_t i = 0; i < len; i++) {
            packet[j++] = data[i];
        }
    }

    // Send packet
    _webui_send_client_ws(win, client, connection_id, packet, packet_len);

    // Free
    _webui_free_mem((void*)packet);
}

static char* _webui_str_dup(const char* src) {
    size_t len = _webui_strlen(src);
    char* dst = (char*)_webui_malloc(len);
    WEBUI_STR_COPY_DYN(dst, len, src);
    return dst;
}

static const char* _webui_get_temp_path() {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_temp_path()\n");
    #endif

    #ifdef _WIN32
    // Resolve %USERPROFILE%
    #ifdef _MSC_VER
    char* WinUserProfile = NULL;
    size_t sz = 0;
    if (_dupenv_s(&WinUserProfile,&sz, "USERPROFILE") != 0 || WinUserProfile == NULL)
        return "";
    #else
    char* WinUserProfile = getenv("USERPROFILE");
    if (WinUserProfile == NULL)
        return "";
    #endif
    return WinUserProfile;
    #elif __APPLE__
    // Resolve $HOME
    char* MacUserProfile = getenv("HOME");
    if (MacUserProfile == NULL)
        return "";
    return MacUserProfile;
    #else
    // Resolve $HOME
    char* LinuxUserProfile = getenv("HOME");
    if (LinuxUserProfile == NULL)
        return "";
    return LinuxUserProfile;
    #endif
}

static bool _webui_is_google_chrome_folder(const char* folder) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_is_google_chrome_folder([%s])\n", folder);
    #endif

    char browser_full_path[WEBUI_MAX_PATH];

    // Make sure this folder is Google Chrome setup and not Chromium
    // by checking if `master_preferences` file exist or `initial_preferences`
    // Ref: https://support.google.com/chrome/a/answer/187948?hl=en

    WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\master_preferences", folder);
    if (!_webui_file_exist(browser_full_path)) {

        WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\initial_preferences", folder);
        if (!_webui_file_exist(browser_full_path))
            return false; // This is Chromium or something else
    }

    // Make sure the browser executable file exist
    WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\chrome.exe", folder);
    if (!_webui_file_exist(browser_full_path))
        return false;

    return true;
}

static bool _webui_browser_exist(_webui_window_t* win, size_t browser) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_exist([%zu])\n", browser);
    #endif

    // Check if a web browser is installed on this machine

    if (browser == Chrome) {

        // Google Chrome

        static bool ChromeExist = false;
        if(win) {
            if (ChromeExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Google Chrome on Windows

        char browser_folder[WEBUI_MAX_PATH];

        // Search in `HKEY_LOCAL_MACHINE` (If Google Chrome installed for
        // multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\chrome.exe",
                L"Path", browser_folder
            )) {

            // Make sure its Google Chrome and not Chromium
            if (_webui_is_google_chrome_folder(browser_folder)) {

                // Google Chrome Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
                ChromeExist = true;
                return true;
            }
        }

        // Search in `HKEY_CURRENT_USER` (If Google Chrome installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\chrome.exe",
                L"Path", browser_folder
            )) {

            // Make sure its Google Chrome and not Chromium
            if (_webui_is_google_chrome_folder(browser_folder)) {

                // Google Chrome Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
                ChromeExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Google Chrome on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Google Chrome\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Google Chrome.app\" --args");
            ChromeExist = true;
            return true;
        } else
            return false;
        #else

        // Google Chrome on Linux
        if (_webui_cmd_sync(win, "google-chrome --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "google-chrome");
            ChromeExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "google-chrome-stable --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "google-chrome-stable");
            ChromeExist = true;
            return true;
        } else
            return false;

        #endif
    } else if (browser == Edge) {

        // Edge

        static bool EdgeExist = false;
        if(win) {
            if (EdgeExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Edge on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_LOCAL_MACHINE` (If Edge installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\msedge.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Edge Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                EdgeExist = true;
                return true;
            }
        }

        // Search in `HKEY_CURRENT_USER` (If Edge installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\msedge.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Edge Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                EdgeExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Edge on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Microsoft Edge\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Microsoft Edge.app\" --args");
            EdgeExist = true;
            return true;
        } else
            return false;

        #else

        // Edge on Linux
        if (_webui_cmd_sync(win, "microsoft-edge-stable --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-stable");
            EdgeExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "microsoft-edge-beta --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-beta");
            EdgeExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "microsoft-edge-dev --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-dev");
            EdgeExist = true;
            return true;
        } else
            return false;

        #endif
    } else if (browser == Epic) {

        // Epic Privacy Browser

        static bool EpicExist = false;
        if(win) {
            if (EpicExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Epic on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_CURRENT_USER` (If Epic installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\epic.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Epic Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                EpicExist = true;
                return true;
            }
        }

        // Search in `HKEY_LOCAL_MACHINE` (If Epic installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\epic.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Epic Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                EpicExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Epic on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Epic\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Epic.app\" --args");
            EpicExist = true;
            return true;
        } else
            return false;
        #else

        // Epic on Linux
        if (_webui_cmd_sync(win, "epic --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "epic");
            EpicExist = true;
            return true;
        } else
            return false;
        #endif
    } else if (browser == Vivaldi) {

        // Vivaldi Browser

        static bool VivaldiExist = false;
        if(win) {
            if (VivaldiExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Vivaldi on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_LOCAL_MACHINE` (If Vivaldi installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\vivaldi.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Vivaldi Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                VivaldiExist = true;
                return true;
            }
        }

        // Search in `HKEY_CURRENT_USER` (If Vivaldi installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\vivaldi.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Vivaldi Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                VivaldiExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Vivaldi on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Vivaldi\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Vivaldi.app\" --args");
            VivaldiExist = true;
            return true;
        } else
            return false;
        #else

        // Vivaldi on Linux
        if (_webui_cmd_sync(win, "vivaldi --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi");
            VivaldiExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "vivaldi-stable --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi-stable");
            VivaldiExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "vivaldi-snapshot --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi-snapshot");
            VivaldiExist = true;
            return true;
        } else
            return false;
        #endif
    } else if (browser == Brave) {

        // Brave Browser

        static bool BraveExist = false;
        if(win) {
            if (BraveExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Brave on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_LOCAL_MACHINE` (If Brave installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\brave.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Brave Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                BraveExist = true;
                return true;
            }
        }

        // Search in `HKEY_CURRENT_USER` (If Brave installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\brave.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Brave Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                BraveExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Brave on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Brave Browser\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Brave Browser.app\" --args");
            BraveExist = true;
            return true;
        } else
            return false;
        #else

        // Brave on Linux
        if (_webui_cmd_sync(win, "brave --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "brave");
            BraveExist = true;
            return true;
        } else
            return false;
        #endif
    } else if (browser == Firefox) {

        // Firefox

        static bool FirefoxExist = false;
        if(win) {
            if (FirefoxExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Firefox on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_LOCAL_MACHINE` (If Firefox installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\firefox.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Firefox Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                FirefoxExist = true;
                return true;
            }
        }

        // Search in `HKEY_CURRENT_USER` (If Firefox installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\firefox.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Firefox Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                FirefoxExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Firefox on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Firefox\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Firefox.app\" --args");
            FirefoxExist = true;
            return true;
        } else
            return false;
        #else

        // Firefox on Linux

        if (_webui_cmd_sync(win, "firefox -v", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "firefox");
            FirefoxExist = true;
            return true;
        } else
            return false;

        #endif

    } else if (browser == Yandex) {

        // Yandex Browser

        static bool YandexExist = false;
        if(win) {
            if (YandexExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Yandex on Windows

        char browser_fullpath[WEBUI_MAX_PATH];

        // Search in `HKEY_CURRENT_USER` (If Yandex installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\browser.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Yandex Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                YandexExist = true;
                return true;
            }
        }

        // Search in `HKEY_LOCAL_MACHINE` (If Yandex installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\browser.exe",
                L"", browser_fullpath
            )) {

            // Make sure the browser executable file exist
            if (_webui_file_exist(browser_fullpath)) {

                // Yandex Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
                YandexExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Yandex on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Yandex\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Yandex.app\" --args");
            YandexExist = true;
            return true;
        } else
            return false;
        #else

        // Yandex on Linux
        if (_webui_cmd_sync(win, "yandex-browser --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "yandex-browser");
            YandexExist = true;
            return true;
        } else
            return false;
        #endif
    } else if (browser == Chromium) {

        // The Chromium Projects

        static bool ChromiumExist = false;
        if(win) {
            if (ChromiumExist && !_webui_is_empty(win->browser_path))
                return true;
        }

        #ifdef _WIN32

        // Chromium on Windows

        char browser_folder[WEBUI_MAX_PATH];

        // Search in `HKEY_CURRENT_USER` (If Chromium installed for one user)
        if (_webui_get_windows_reg_value(
                HKEY_CURRENT_USER,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\chrome.exe",
                L"Path", browser_folder
            )) {

            // Make sure its Chromium and not Google Chrome
            if (!_webui_is_google_chrome_folder(browser_folder)) {

                // Chromium Found (one user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
                ChromiumExist = true;
                return true;
            }
        }

        // Search in `HKEY_LOCAL_MACHINE` (If Chromium installed for multi-user)
        if (_webui_get_windows_reg_value(
                HKEY_LOCAL_MACHINE,
                L"Software\\Microsoft\\Windows\\CurrentVer"
                L"sion\\App Paths\\chrome.exe",
                L"Path", browser_folder
            )) {

            // Make sure its Chromium and not Google Chrome
            if (!_webui_is_google_chrome_folder(browser_folder)) {

                // Chromium Found (multi-user)
                if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
                ChromiumExist = true;
                return true;
            }
        }

        return false;

        #elif __APPLE__

        // Chromium on macOS
        if (_webui_cmd_sync(win, "open -R -a \"Chromium\"", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Chromium.app\" --args");
            ChromiumExist = true;
            return true;
        } else
            return false;
        #else

        // Chromium on Linux
        if (_webui_cmd_sync(win, "chromium-browser --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "chromium-browser");
            ChromiumExist = true;
            return true;
        } else if (_webui_cmd_sync(win, "chromium --version", false) == 0) {

            if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "chromium");
            ChromiumExist = true;
            return true;
        } else
            return false;
        #endif
    }

    return false;
}

static void _webui_clean(void) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_clean()\n");
    #endif

    static bool cleaned = false;
    if (cleaned)
        return;
    cleaned = true;

    // Stop all threads
    _webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);

    // Let's give other threads more time to safely exit
    // and finish cleaning up.    
    // _webui_sleep(500);

    // Clean all servers services
    mg_exit_library();

    // Free all non-freed memory allocations
    _webui_free_all_mem();

    // Destroy all mutex
    _webui_mutex_destroy(&_webui.mutex_server_start);
    _webui_mutex_destroy(&_webui.mutex_send);
    _webui_mutex_destroy(&_webui.mutex_receive);
    _webui_mutex_destroy(&_webui.mutex_wait);
    _webui_mutex_destroy(&_webui.mutex_js_run);
    _webui_mutex_destroy(&_webui.mutex_win_connect);
    _webui_mutex_destroy(&_webui.mutex_exit_now);
    _webui_mutex_destroy(&_webui.mutex_webview_stop);
    _webui_mutex_destroy(&_webui.mutex_http_handler);
    _webui_mutex_destroy(&_webui.mutex_client);
    _webui_condition_destroy(&_webui.condition_wait);

    #ifdef WEBUI_LOG
    printf("[Core]\t\tWebUI exit successfully\n");
    #endif
}

static int _webui_cmd_sync(_webui_window_t* win, char* cmd, bool show) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_sync()\n");
    #endif

    // Run sync command and
    // return the exit code

    char buf[2048] = {0};

    // Sync command
    #ifdef _WIN32
    // Using: _CMD_
    WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "cmd /c \"%s\" > nul 2>&1", cmd);
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf);
    #endif
    return _webui_system_win32(win, buf, show);
    #else
    // Using: _CMD_
    WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s >>/dev/null 2>>/dev/null", cmd);
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf);
    #endif
    int r = system(buf);
    r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1;
    return r;
    #endif
}

static int _webui_cmd_async(_webui_window_t* win, char* cmd, bool show) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_async()\n");
    #endif

    // Run a async command
    // and return immediately

    char buf[1024] = {0};

    // Asynchronous command
    #ifdef _WIN32
    // Using: START "" _CMD_
    WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "cmd /c \"START \"\" %s\" > nul 2>&1", cmd);
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_async() -> Running [%s] \n", buf);
    #endif
    return _webui_system_win32(win, buf, show);
    #else
    // Using: _CMD_ &
    WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s >>/dev/null 2>>/dev/null &", cmd);
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_cmd_async() -> Running [%s] \n", buf);
    #endif
    int r = system(buf);
    r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1;
    return r;
    #endif
}

static int _webui_run_browser(_webui_window_t* win, char* cmd) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_run_browser()\n");
    #endif

    // Run a async command
    return _webui_cmd_async(win, cmd, false);
}

static int _webui_get_browser_args(_webui_window_t* win, size_t browser, char* buffer, size_t len) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_browser_args([%zu])\n", browser);
    #endif

    const char* chromium_options[] = {
        "--no-first-run",
        // "--no-proxy-server",
        "--safe-mode",
        "--disable-extensions",
        "--disable-background-mode",
        "--disable-plugins",
        "--disable-plugins-discovery",
        "--disable-translate",
        "--disable-features=Translate",
        "--bwsi",
        "--disable-sync",
        "--disable-sync-preferences",
        "--disable-component-update",
        "--allow-insecure-localhost",
        "--auto-accept-camera-and-microphone-capture",
    };

    int c = 0;
    switch(browser) {
        case Chrome:
        case Edge:
        case Epic:
        case Vivaldi:
        case Brave:
        case Yandex:
        case Chromium:
            // Profile
            if (!_webui_is_empty(win->profile_path))
                c = WEBUI_SN_PRINTF_DYN(buffer, len, " --user-data-dir=\"%s\"", win->profile_path);
            // Basic
            for (int i = 0; i < (int)(sizeof(chromium_options) / sizeof(chromium_options[0])); i++) {
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", chromium_options[i]);
            }
            // Kiosk Mode
            if (win->kiosk_mode)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--chrome-frame --kiosk");
            // High Contrast Support
            if (win->disable_browser_high_contrast)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--disable-features=ForcedColors");
            // Hide Mode
            if (win->hide)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--headless --headless=new");
            // Window Size
            if (win->size_set)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --window-size=%u,%u", win->width, win->height);
            // Window Position
            if (win->position_set)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --window-position=%u,%u", win->x, win->y);
            // Proxy
            if (win->proxy_set)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --proxy-server=%s", win->proxy_server);
            else
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--no-proxy-server");

            // URL (END)
            c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--app=");
            return c;
        case Firefox:
            // Profile
            if (!_webui_is_empty(win->profile_name))
                c = WEBUI_SN_PRINTF_DYN(buffer, len, " -P %s", win->profile_name);
            // Basic
            c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -purgecaches");
            // Kiosk Mode
            if (win->kiosk_mode)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "-kiosk");
            // Hide Mode
            if (win->hide)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "-headless");
            // Window Size
            if (win->size_set)
                c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -width %u -height %u", win->width, win->height);
            // Window Position
            // Firefox does not support window positioning.
            // Proxy
            if (win->proxy_set) {
                // Server: `win->proxy_server`

                // TODO: Add proxy feature to Firefox
                // Method 1: modifying `prefs.js` / user.js
                // Method 2: use Proxy Auto-Configuration (PAC) file
            }

            // URL (END)
            c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -new-window ");
            return c;
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_browser_args() -> Unknown Browser (%zu)\n", browser);
    #endif
    WEBUI_STR_COPY_DYN(buffer, len, "");
    return 0;
}

static bool _webui_browser_start_chrome(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_chrome([%s])\n", address);
    #endif

    // -- Google Chrome ----------------------

    if (win->current_browser != 0 && win->current_browser != Chrome)
        return false;

    if (!_webui_browser_exist(win, Chrome))
        return false;

    if (!_webui_browser_create_new_profile(win, Chrome))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Chrome, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Chrome;
        _webui.current_browser = Chrome;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_edge(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_edge([%s])\n", address);
    #endif

    // -- Microsoft Edge ----------------------

    if (win->current_browser != 0 && win->current_browser != Edge)
        return false;

    if (!_webui_browser_exist(win, Edge))
        return false;

    if (!_webui_browser_create_new_profile(win, Edge))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Edge, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Edge;
        _webui.current_browser = Edge;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_epic(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_epic([%s])\n", address);
    #endif

    // -- Epic Privacy Browser ----------------------

    if (win->current_browser != 0 && win->current_browser != Epic)
        return false;

    if (!_webui_browser_exist(win, Epic))
        return false;

    if (!_webui_browser_create_new_profile(win, Epic))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Epic, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Epic;
        _webui.current_browser = Epic;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_vivaldi([%s])\n", address);
    #endif

    // -- Vivaldi Browser ----------------------

    if (win->current_browser != 0 && win->current_browser != Vivaldi)
        return false;

    if (!_webui_browser_exist(win, Vivaldi))
        return false;

    if (!_webui_browser_create_new_profile(win, Vivaldi))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Vivaldi, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Vivaldi;
        _webui.current_browser = Vivaldi;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_brave(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_brave([%s])\n", address);
    #endif

    // -- Brave Browser ----------------------

    if (win->current_browser != 0 && win->current_browser != Brave)
        return false;

    if (!_webui_browser_exist(win, Brave))
        return false;

    if (!_webui_browser_create_new_profile(win, Brave))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Brave, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Brave;
        _webui.current_browser = Brave;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_firefox(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_firefox([%s])\n", address);
    #endif

    // -- Mozilla Firefox ----------------------

    if (win->current_browser != 0 && win->current_browser != Firefox)
        return false;

    if (!_webui_browser_exist(win, Firefox))
        return false;

    if (!_webui_browser_create_new_profile(win, Firefox))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Firefox, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Firefox;
        _webui.current_browser = Firefox;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_yandex(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_yandex([%s])\n", address);
    #endif

    // -- Yandex Browser ----------------------

    if (win->current_browser != 0 && win->current_browser != Yandex)
        return false;

    if (!_webui_browser_exist(win, Yandex))
        return false;

    if (!_webui_browser_create_new_profile(win, Yandex))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Yandex, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Yandex;
        _webui.current_browser = Yandex;
        return true;
    } else
        return false;
}

static bool _webui_browser_start_chromium(_webui_window_t* win, const char* address) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start_chromium([%s])\n", address);
    #endif

    // -- The Chromium Projects -------------------

    if (win->current_browser != 0 && win->current_browser != Chromium)
        return false;

    if (!_webui_browser_exist(win, Chromium))
        return false;

    if (!_webui_browser_create_new_profile(win, Chromium))
        return false;

    char arg[1024] = {0};
    _webui_get_browser_args(win, Chromium, arg, sizeof(arg));

    char full[1024] = {0};
    WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);

    if (_webui_run_browser(win, full) == 0) {

        win->current_browser = Chromium;
        _webui.current_browser = Chromium;
        return true;
    } else
        return false;
}

static bool _webui_browser_start(_webui_window_t* win, const char* address, size_t _browser) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_browser_start([%s], [%zu])\n", address, _browser);
    #endif

    // Non existing browser
    if (_browser > 12)
        return false;

    // No browser mode
    if (_browser == NoBrowser)
        return true;

    // Find the best web browser to use
    size_t browser = _browser;
    if (browser == AnyBrowser) {
        browser = _webui.current_browser != 0 ? 
        _webui.current_browser : _webui_find_the_best_browser(win);
    }

    // Current browser used in the last opened window
    if (browser == AnyBrowser && _webui.current_browser != 0)
        browser = _webui.current_browser;

    // #1 - Chrome - Works perfectly
    // #2 - Edge - Works perfectly like Chrome
    // #3 - Epic - Works perfectly like Chrome
    // #4 - Vivaldi - Works perfectly like Chrome
    // #5 - Brave - Shows a policy notification in the first run
    // #6 - Firefox - Does not support App-Mode like Chrome (Looks not great)
    // #7 - Yandex - Shows a big welcome window in the first run
    // #8 - Chromium - Some Anti-Malware shows a false alert

    if (browser != AnyBrowser) {

        // Open the window using the user specified browser

        if (browser == Chrome)
            return _webui_browser_start_chrome(win, address);
        else if (browser == Edge)
            return _webui_browser_start_edge(win, address);
        else if (browser == Epic)
            return _webui_browser_start_epic(win, address);
        else if (browser == Vivaldi)
            return _webui_browser_start_vivaldi(win, address);
        else if (browser == Brave)
            return _webui_browser_start_brave(win, address);
        else if (browser == Firefox)
            return _webui_browser_start_firefox(win, address);
        else if (browser == Yandex)
            return _webui_browser_start_yandex(win, address);
        else if (browser == Chromium)
            return _webui_browser_start_chromium(win, address);
        else if (browser == ChromiumBased) {

            // Open the window using a Chromium-based browser

            if (!_webui_browser_start_chrome(win, address))
                if (!_webui_browser_start_edge(win, address))
                    if (!_webui_browser_start_epic(win, address))
                        if (!_webui_browser_start_vivaldi(win, address))
                            if (!_webui_browser_start_brave(win, address))
                                if (!_webui_browser_start_yandex(win, address))
                                    if (!_webui_browser_start_chromium(win, address))
                                        return false;
        } else
            return false;
    } else if (win->current_browser != 0) {

        // Open the window using the same web browser used for this current window

        if (win->current_browser == Chrome)
            return _webui_browser_start_chrome(win, address);
        else if (win->current_browser == Edge)
            return _webui_browser_start_edge(win, address);
        else if (win->current_browser == Epic)
            return _webui_browser_start_epic(win, address);
        else if (win->current_browser == Vivaldi)
            return _webui_browser_start_vivaldi(win, address);
        else if (win->current_browser == Brave)
            return _webui_browser_start_brave(win, address);
        else if (win->current_browser == Firefox)
            return _webui_browser_start_firefox(win, address);
        else if (win->current_browser == Yandex)
            return _webui_browser_start_yandex(win, address);
        else if (win->current_browser == Chromium)
            return _webui_browser_start_chromium(win, address);
        else
            return false;
    } else {

        // Open the window using the default OS browser

        #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
        // Windows
        if (!_webui_browser_start_chrome(win, address))
            if (!_webui_browser_start_edge(win, address))
                if (!_webui_browser_start_epic(win, address))
                    if (!_webui_browser_start_vivaldi(win, address))
                        if (!_webui_browser_start_brave(win, address))
                            if (!_webui_browser_start_firefox(win, address))
                                if (!_webui_browser_start_yandex(win, address))
                                    if (!_webui_browser_start_chromium(win, address))
                                        return false;
        #elif __APPLE__
        // macOS
        if (!_webui_browser_start_chrome(win, address))
            if (!_webui_browser_start_edge(win, address))
                if (!_webui_browser_start_epic(win, address))
                    if (!_webui_browser_start_vivaldi(win, address))
                        if (!_webui_browser_start_brave(win, address))
                            if (!_webui_browser_start_firefox(win, address))
                                if (!_webui_browser_start_yandex(win, address))
                                    if (!_webui_browser_start_chromium(win, address))
                                        return false;
        #else
        // Linux
        if (!_webui_browser_start_chrome(win, address))
            if (!_webui_browser_start_edge(win, address))
                if (!_webui_browser_start_epic(win, address))
                    if (!_webui_browser_start_vivaldi(win, address))
                        if (!_webui_browser_start_brave(win, address))
                            if (!_webui_browser_start_firefox(win, address))
                                if (!_webui_browser_start_yandex(win, address))
                                    if (!_webui_browser_start_chromium(win, address))
                                        return false;
        #endif
    }

    return true;
}

static bool _webui_is_process_running(const char* process_name) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_is_process_running([%s])\n", process_name);
    #endif

    bool isRunning = false;

    #ifdef _WIN32
    // Microsoft Windows
    HANDLE hSnapshot;
    PROCESSENTRY32 pe32;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
        return false;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hSnapshot,&pe32)) {
        CloseHandle(hSnapshot);
        return false;
    }
    do {
        if (strcmp(pe32.szExeFile, process_name) == 0) {
            isRunning = true;
            break;
        }
    } while(Process32Next(hSnapshot,&pe32));
    CloseHandle(hSnapshot);
    #elif __linux__
    // Linux
    DIR * dir;
    struct dirent* entry;
    char status_path[WEBUI_MAX_PATH];
    char line[WEBUI_MAX_PATH];
    dir = opendir("/proc");
    if (!dir)
        return false; // Unable to open /proc
    while((entry = readdir(dir))) {
        if (entry->d_type == DT_DIR && atoi(entry->d_name) > 0) {
            WEBUI_SN_PRINTF_STATIC(status_path, sizeof(status_path), "/proc/%s/status", entry->d_name);
            FILE * status_file;
            WEBUI_FILE_OPEN(status_file, status_path, "r");
            if (status_file) {
                while(fgets(line, sizeof(line), status_file)) {
                    if (strncmp(line, "Name:", 5) == 0) {
                        char proc_name[WEBUI_MAX_PATH];
                        sscanf(line, "Name: %s", proc_name);
                        if (strcmp(proc_name, process_name) == 0) {
                            isRunning = true;
                            fclose(status_file);
                            goto _close_dir;
                        }
                        break;
                    }
                }
                fclose(status_file);
            }
        }
    }
    _close_dir:
        closedir(dir);
    #else
    // macOS
    int mib[4] = {
        CTL_KERN,
        KERN_PROC,
        KERN_PROC_ALL,
        0
    };
    struct kinfo_proc * procs = NULL;
    size_t size;
    if (sysctl(mib, 4, NULL,&size, NULL, 0) < 0)
        return false; // Failed to get process list size
    procs = (struct kinfo_proc * ) malloc(size);
    if (!procs)
        return false; // Failed to allocate memory for process list
    if (sysctl(mib, 4, procs,&size, NULL, 0) < 0) {
        free(procs);
        return false; // Failed to get process list
    }
    size_t count = size / sizeof(struct kinfo_proc);
    for (size_t i = 0; i < count; i++) {
        if (strcmp(procs[i].kp_proc.p_comm, process_name) == 0) {
            isRunning = true;
            break;
        }
    }
    free(procs);
    #endif

    return isRunning;
}

static size_t _webui_find_the_best_browser(_webui_window_t* win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_find_the_best_browser()\n");
    #endif

    // #1 - Chrome - Works perfectly
    // #2 - Edge - Works perfectly like Chrome
    // #3 - Epic - Works perfectly like Chrome
    // #4 - Vivaldi - Works perfectly like Chrome
    // #5 - Brave - Shows a policy notification in the first run
    // #6 - Firefox - Does not support App-Mode like Chrome (Looks not great)
    // #7 - Yandex - Shows a big welcome window in the first run
    // #8 - Chromium - Some Anti-Malware shows a false alert

    // To save memory, let's search if a web browser is already running

    #ifdef _WIN32
    // Microsoft Windows
    if (_webui_is_process_running("chrome.exe") && _webui_browser_exist(win, Chrome))
        return Chrome;
    else if (_webui_is_process_running("msedge.exe") && _webui_browser_exist(win, Edge))
        return Edge;
    else if (_webui_is_process_running("epic.exe") && _webui_browser_exist(win, Epic))
        return Epic;
    else if (_webui_is_process_running("vivaldi.exe") && _webui_browser_exist(win, Vivaldi))
        return Vivaldi;
    else if (_webui_is_process_running("brave.exe") && _webui_browser_exist(win, Brave))
        return Brave;
    else if (_webui_is_process_running("firefox.exe") && _webui_browser_exist(win, Firefox))
        return Firefox;
    else if (_webui_is_process_running("browser.exe") && _webui_browser_exist(win, Yandex))
        return Yandex;
    // Chromium check is never reached if Google Chrome is installed
    // due to duplicate process name `chrome.exe`
    else if (_webui_is_process_running("chrome.exe") && _webui_browser_exist(win, Chromium))
        return Chromium;
    #elif __linux__
    // Linux
    if (_webui_is_process_running("chrome") && _webui_browser_exist(win, Chrome))
        return Chrome;
    else if (_webui_is_process_running("msedge") && _webui_browser_exist(win, Edge))
        return Edge;
    // Epic...
    else if (_webui_is_process_running("vivaldi-bin") && _webui_browser_exist(win, Vivaldi))
        return Vivaldi;
    else if (_webui_is_process_running("brave") && _webui_browser_exist(win, Brave))
        return Brave;
    else if (_webui_is_process_running("firefox") && _webui_browser_exist(win, Firefox))
        return Firefox;
    else if (_webui_is_process_running("yandex_browser") && _webui_browser_exist(win, Yandex))
        return Yandex;
    // Chromium check is never reached if Google Chrome is installed
    // due to duplicate process name `chrome`
    else if (_webui_is_process_running("chrome") && _webui_browser_exist(win, Chromium))
        return Chromium;
    #else
    // macOS
    if (_webui_is_process_running("Google Chrome") && _webui_browser_exist(win, Chrome))
        return Chrome;
    else if (_webui_is_process_running("Epic") && _webui_browser_exist(win, Epic))
        return Epic;
    else if (_webui_is_process_running("Vivaldi") && _webui_browser_exist(win, Vivaldi))
        return Vivaldi;
    else if (_webui_is_process_running("Brave") && _webui_browser_exist(win, Brave))
        return Brave;
    else if (_webui_is_process_running("Firefox") && _webui_browser_exist(win, Firefox))
        return Firefox;
    else if (_webui_is_process_running("Yandex") && _webui_browser_exist(win, Yandex))
        return Yandex;
    else if (_webui_is_process_running("Chromium") && _webui_browser_exist(win, Chromium))
        return Chromium;
    #endif

    return AnyBrowser;
}

static bool _webui_show(_webui_window_t* win, struct mg_connection* client, const char* content, size_t browser) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_show([%zu])\n", browser);
    #endif

    if (_webui_is_empty(content))
        return false;

    // Some wrappers do not guarantee pointers stay valid,
    // so, let's make our copy.
    size_t content_len = _webui_strlen(content);
    const char* content_cpy = (const char*)_webui_malloc(content_len);
    memcpy((char*)content_cpy, content, content_len);

    // URL
    if (_webui_is_valid_url(content_cpy)) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show() -> URL: [%s]\n", content_cpy);
        #endif
        return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_URL, browser);
    }
    // Embedded HTML
    else if (strstr(content_cpy, "<html") || 
            strstr(content_cpy, "<!DOCTYPE") || 
            strstr(content_cpy, "<!doctype") || 
            strstr(content_cpy, "<!Doctype")) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show() -> Embedded HTML:\n");
        printf("- - -[HTML]- - - - - - - - - -\n%s\n- - - - - - - - - - - - - - - -\n", content_cpy);
        #endif
        return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_HTML, browser);
    }
    // Folder
    else if (_webui_folder_exist(content_cpy)) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show() -> Folder: [%s]\n", content_cpy);
        #endif
        // Set root folder
        if (!webui_set_root_folder(win->num, content_cpy)) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_show() -> Failed to set folder root path\n");
            #endif
            _webui_free_mem((void*)content_cpy);
            return false;
        }
        return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_FOLDER, browser);
    }
    // File
    else {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show() -> File: [%s]\n", content_cpy);
        #endif
        if (content_len > WEBUI_MAX_PATH || strstr(content_cpy, "<"))
            return false;
        return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_FILE, browser);
    }
}

// TLS
#ifdef WEBUI_TLS
static int _webui_tls_initialization(void * ssl_ctx, void * ptr) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_tls_initialization()\n");
    #endif

    SSL_CTX * ctx = (SSL_CTX * ) ssl_ctx;

    // Disable security levels. It is The end-user
    // responsability to provide a high encryption
    // level certificate. While WebUI should just
    // use the end-user's certificates.
    SSL_CTX_set_security_level(ctx, 0);

    // Load Certificate
    BIO * bio_cert = BIO_new_mem_buf((void*)_webui.ssl_cert, -1);
    X509 * cert = PEM_read_bio_X509(bio_cert, NULL, 0, NULL);
    if (cert == NULL) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_tls_initialization() -> PEM_read_bio_X509 failed\n");
        #endif
        WEBUI_ASSERT("PEM_read_bio_X509 failed");
        return -1;
    }
    if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_tls_initialization() -> SSL_CTX_use_certificate failed\n");
        #endif
        WEBUI_ASSERT("SSL_CTX_use_certificate failed");
        return -1;
    }
    X509_free(cert);
    BIO_free(bio_cert);

    // Load Key
    BIO * bio_key = BIO_new_mem_buf((void*)_webui.ssl_key, -1);
    EVP_PKEY * private_key = PEM_read_bio_PrivateKey(bio_key, NULL, 0, NULL);
    if (private_key == NULL) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_tls_initialization() -> PEM_read_bio_PrivateKey failed\n");
        #endif
        WEBUI_ASSERT("PEM_read_bio_PrivateKey failed");
        return -1;
    }
    if (SSL_CTX_use_PrivateKey(ctx, private_key) <= 0) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_tls_initialization() -> SSL_CTX_use_PrivateKey failed\n");
        #endif
        WEBUI_ASSERT("SSL_CTX_use_PrivateKey failed");
        return -1;
    }
    EVP_PKEY_free(private_key);
    BIO_free(bio_key);

    return 0;
}

static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key, char* ssl_cert, char* ssl_key) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_tls_generate_self_signed_cert()\n");
    #endif

    int ret = 0;
    int bits = 2048;

    // ----- Create Root Certificate -----
    EVP_PKEY * root_pkey = NULL;
    EVP_PKEY_CTX * root_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!root_ctx)
        return false;

    if (EVP_PKEY_keygen_init(root_ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(root_ctx, bits) <= 0 ||
        EVP_PKEY_keygen(root_ctx,&root_pkey) <= 0) {
        EVP_PKEY_CTX_free(root_ctx);
        return false;
    }

    EVP_PKEY_CTX_free(root_ctx);

    X509 * root_x509 = X509_new();
    X509_set_version(root_x509, 2);
    ASN1_INTEGER_set(X509_get_serialNumber(root_x509), (long) _webui_generate_random_uint32());
    X509_gmtime_adj(X509_get_notBefore(root_x509), 0);
    X509_gmtime_adj(X509_get_notAfter(root_x509), (long)(WEBUI_SSL_EXPIRE));

    X509_NAME * root_name = X509_get_subject_name(root_x509);
    X509_NAME_add_entry_by_txt(root_name, "C", MBSTRING_ASC, (const unsigned char *)"CA", -1, -1,
        0); // Country
    X509_NAME_add_entry_by_txt(root_name, "O", MBSTRING_ASC, (const unsigned char *)"WebUI Root Authority", -1, -1, 0); // Organization
    X509_NAME_add_entry_by_txt(root_name, "OU", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // Organizational Unit
    X509_NAME_add_entry_by_txt(root_name, "CN", MBSTRING_ASC, (const unsigned char *)"localhost", -1, -1,
        0); // Common Name
    X509_NAME_add_entry_by_txt(
        root_name, "subjectAltName", MBSTRING_ASC, (const unsigned char *)"127.0.0.1", -1, -1,
        0
    ); // Subject Alternative Name
    X509_NAME_add_entry_by_txt(root_name, "ST", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // State
    X509_NAME_add_entry_by_txt(root_name, "L", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // Locality

    X509_set_issuer_name(root_x509, root_name);
    X509_set_pubkey(root_x509, root_pkey);
    ret = X509_sign(root_x509, root_pkey, EVP_sha256());
    if (ret <= 0) {
        X509_free(root_x509);
        EVP_PKEY_free(root_pkey);
        return false;
    }

    // Write Root Certificate and Key
    BIO * bio_root_cert = BIO_new(BIO_s_mem());
    PEM_write_bio_X509(bio_root_cert, root_x509);
    memset(root_cert, 0, WEBUI_SSL_SIZE);
    BIO_read(bio_root_cert, root_cert, (WEBUI_SSL_SIZE - 1));

    BIO * bio_root_key = BIO_new(BIO_s_mem());
    PEM_write_bio_PrivateKey(bio_root_key, root_pkey, NULL, NULL, 0, NULL, NULL);
    memset(root_key, 0, WEBUI_SSL_SIZE);
    BIO_read(bio_root_key, root_key, (WEBUI_SSL_SIZE - 1));

    // ----- Create Server Certificate and sign with Root -----
    EVP_PKEY * pkey = NULL;
    EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx) {
        X509_free(root_x509);
        EVP_PKEY_free(root_pkey);
        return false;
    }

    if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0 ||
        EVP_PKEY_keygen(ctx,&pkey) <= 0) {
        X509_free(root_x509);
        EVP_PKEY_free(root_pkey);
        EVP_PKEY_CTX_free(ctx);
        return false;
    }

    EVP_PKEY_CTX_free(ctx);

    X509 * x509 = X509_new();
    X509_set_version(x509, 2);
    ASN1_INTEGER_set(X509_get_serialNumber(x509), (long) _webui_generate_random_uint32());
    X509_gmtime_adj(X509_get_notBefore(x509), 0);
    X509_gmtime_adj(X509_get_notAfter(x509), (long)(WEBUI_SSL_EXPIRE));

    X509_NAME * name = X509_get_subject_name(x509);
    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (const unsigned char *)"CA", -1, -1,
        0); // Country
    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // Organization
    X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // Organizational Unit
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)"localhost", -1, -1,
        0); // Common Name
    X509_NAME_add_entry_by_txt(
        name, "subjectAltName", MBSTRING_ASC, (const unsigned char *)"127.0.0.1", -1, -1, 0
    ); // Subject Alternative Name
    X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // State
    X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
        0); // Locality

    X509_set_issuer_name(x509, root_name);
    X509_set_pubkey(x509, pkey);
    ret = X509_sign(x509, root_pkey, EVP_sha256());
    if (ret <= 0) {
        X509_free(root_x509);
        EVP_PKEY_free(root_pkey);
        X509_free(x509);
        EVP_PKEY_free(pkey);
        return false;
    }

    // Write the Server Certificate and Key
    BIO * bio_cert = BIO_new(BIO_s_mem());
    PEM_write_bio_X509(bio_cert, x509);
    memset(ssl_cert, 0, WEBUI_SSL_SIZE);
    BIO_read(bio_cert, ssl_cert, (WEBUI_SSL_SIZE - 1));

    BIO * bio_key = BIO_new(BIO_s_mem());
    PEM_write_bio_PrivateKey(bio_key, pkey, NULL, NULL, 0, NULL, NULL);
    memset(ssl_key, 0, WEBUI_SSL_SIZE);
    BIO_read(bio_key, ssl_key, (WEBUI_SSL_SIZE - 1));

    // Cleanup
    X509_free(root_x509);
    EVP_PKEY_free(root_pkey);
    BIO_free_all(bio_root_cert);
    BIO_free_all(bio_root_key);
    X509_free(x509);
    EVP_PKEY_free(pkey);
    BIO_free_all(bio_cert);
    BIO_free_all(bio_key);

    return true;
}
#endif

static bool _webui_show_window(_webui_window_t* win, struct mg_connection* client, const char* content, int type, size_t browser) {

    #ifdef WEBUI_LOG
    if (type == WEBUI_SHOW_HTML)
        printf("[Core]\t\t_webui_show_window(HTML, [%zu])\n", browser);
    else if (type == WEBUI_SHOW_URL)
        printf("[Core]\t\t_webui_show_window(URL, [%zu])\n", browser);
    else if (type == WEBUI_SHOW_FOLDER)
        printf("[Core]\t\t_webui_show_window(FOLDER, [%zu])\n", browser);
    else
        printf("[Core]\t\t_webui_show_window(FILE, [%zu])\n", browser);
    #endif

    #ifdef WEBUI_TLS
    // TLS
    if (_webui_is_empty(_webui.ssl_cert) || _webui_is_empty(_webui.ssl_key)) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show_window() -> Generating self-signed TLS "
            "certificate\n");
        #endif

        // Generate SSL self-signed certificate once
        char* root_cert = (char*)_webui_malloc(WEBUI_SSL_SIZE);
        char* root_key = (char*)_webui_malloc(WEBUI_SSL_SIZE);
        char* ssl_cert = (char*)_webui_malloc(WEBUI_SSL_SIZE);
        char* ssl_key = (char*)_webui_malloc(WEBUI_SSL_SIZE);
        if (!_webui_tls_generate_self_signed_cert(root_cert, root_key, ssl_cert, ssl_key)) {
            #ifdef WEBUI_LOG
            unsigned long err = ERR_get_error();
            char err_buf[1024];
            ERR_error_string_n(err, err_buf, sizeof(err_buf));
            printf(
                "[Core]\t\t_webui_show_window() -> Generating self-signed TLS "
                "certificate failed:\n%s\n",
                err_buf
            );
            #endif
            _webui_free_mem((void*)root_cert);
            _webui_free_mem((void*)root_key);
            _webui_free_mem((void*)ssl_cert);
            _webui_free_mem((void*)ssl_key);
            WEBUI_ASSERT("Generating self-signed TLS certificate failed");
            return false;
        }

        _webui.root_cert = root_cert;
        _webui.root_key = root_key;
        _webui.ssl_cert = ssl_cert;
        _webui.ssl_key = ssl_key;

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show_window() -> Self-signed SSL/TLS "
            "Certificate:\nRoot:\n");
        printf("%s\n", (const char*)_webui.root_cert);
        printf("%s\nServer:\n", (const char*)_webui.root_key);
        printf("%s\n", (const char*)_webui.ssl_cert);
        printf("%s\n", (const char*)_webui.ssl_key);
        #endif
    }
    #endif

    // Initialization
    if (win->html != NULL)
        _webui_free_mem((void*)win->html);
    if (win->url != NULL)
        _webui_free_mem((void*)win->url);

    // Get network ports
    if (win->custom_server_port > 0) win->server_port = win->custom_server_port;
    else if (win->server_port == 0) win->server_port = _webui_get_free_port();

    // Generate the server URL
    win->url = (char*)_webui_malloc(32); // [http][domain][port]
    WEBUI_SN_PRINTF_DYN(win->url, 32, WEBUI_HTTP_PROTOCOL "localhost:%zu", win->server_port);

    // Generate the window URL
    char* window_url = NULL;
    if (type == WEBUI_SHOW_HTML) {

        const char* user_html = content;

        // Show a window using the embedded HTML
        win->is_embedded_html = true;
        win->html = (user_html == NULL ? "" : user_html);

        // Set window URL
        size_t len = _webui_strlen(win->url);
        window_url = (char*)_webui_malloc(len);
        WEBUI_STR_COPY_DYN(window_url, len, win->url);
    }
    else if (type == WEBUI_SHOW_URL) {

        const char* user_url = content;

        // Show a window using a specific URL
        win->is_embedded_html = true;
        size_t bf_len = (64 + _webui_strlen(user_url));
        char* refresh = (char*)_webui_malloc(bf_len);
        WEBUI_SN_PRINTF_DYN(refresh, bf_len, "<meta http-equiv=\"refresh\" content=\"0;url=%s\">", user_url);
        win->html = refresh;

        // Set window URL
        window_url = (char*)user_url;
    }
    else if (type == WEBUI_SHOW_FOLDER) {

        const char* folder_path = content;

        // Show a window using a local folder
        win->is_embedded_html = false;
        win->html = NULL;

        // Set window URL
        window_url = win->url;
    }
    else {
        const char* user_file = content;

        // Show a window using a local file
        win->is_embedded_html = false;
        win->html = NULL;

        // Generate the URL
        const char* file_url_encoded = _webui_url_encode(user_file);
        size_t bf_len = (64 + _webui_strlen(file_url_encoded));
        char* url_encoded = (char*)_webui_malloc(bf_len); // [http][domain][port] [file_encoded]
        WEBUI_SN_PRINTF_DYN(url_encoded, bf_len, WEBUI_HTTP_PROTOCOL "localhost:%zu/%s", 
            win->server_port, file_url_encoded);
        _webui_free_mem((void*)file_url_encoded);
        _webui_free_mem((void*)user_file);

        // Set window URL
        window_url = url_encoded;
    }

    // Run the window
    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {

        // Start a new window

        // Prioritize the server thread if we
        // knows that there is UIs running
        if (_webui.ui) {
            
            // New server thread
            #ifdef _WIN32
            HANDLE thread = CreateThread(NULL, 0, _webui_server_thread, (void*)win, 0, NULL);
            win->server_thread = thread;
            if (thread != NULL)
                CloseHandle(thread);
            #else
            pthread_t thread;
            pthread_create(&thread, NULL,&_webui_server_thread, (void*)win);
            pthread_detach(thread);
            win->server_thread = thread;
            #endif
        }

        // New WebView
        bool runWebView = false;
        if (win->allow_webview) {
            // Trying to use WebView
            if (_webui_wv_show(win, window_url)) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_show_window() -> WebView Found\n");
                #endif
                win->current_browser = Webview;
                runWebView = true;
            }
            else {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_show_window() -> WebView Not Found\n");
                #endif
            }
        }

        // Run browser
        bool runBrowser = false;
        if (!runWebView) {
            if (browser != NoBrowser) {
                if (!_webui_browser_start(win, window_url, browser)) {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_show_window() -> App-mode browser failed\n");
                    #endif
                    // Opening App-mode browser failed
                    // let's try opening UI in native default browser
                    if (browser == AnyBrowser && _webui_open_url_native(window_url)) {
                        #ifdef WEBUI_LOG
                        printf("[Core]\t\t_webui_show_window() -> Native browser succeeded\n");
                        #endif
                        runBrowser = true;
                    }
                    else {
                        #ifdef WEBUI_LOG
                        printf("[Core]\t\t_webui_show_window() -> Native browser failed\n");
                        #endif
                    }
                }
                else {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_show_window() -> App-mode browser succeeded\n");
                    #endif
                    runBrowser = true;
                }
            } else {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_show_window() -> Starting server only mode (NoBrowser)\n");
                #endif
                runBrowser = true;
            }
        }

        _webui_free_mem((void*)window_url);
        if (browser != NoBrowser) {
            if (!runWebView && !runBrowser) {
                // Browser and WebView are not available
                _webui_free_mem((void*)win->html);
                _webui_free_mem((void*)win->url);
                _webui_free_port(win->server_port);
                win->server_port = 0;
                return false;
            }            
        }

        if (!_webui.ui) {

            // Let the wait() knows that this app
            // has atleast one window to wait for
            _webui.ui = true;

            // New server thread
            #ifdef _WIN32
            HANDLE thread = CreateThread(NULL, 0, _webui_server_thread, (void*)win, 0, NULL);
            win->server_thread = thread;
            if (thread != NULL)
                CloseHandle(thread);
            #else
            pthread_t thread;
            pthread_create(&thread, NULL,&_webui_server_thread, (void*)win);
            pthread_detach(thread);
            win->server_thread = thread;
            #endif
        }

    } else {

        // Refresh an existing running window (All connected clients)

        // Packet Protocol Format:
        // [...]
        // [CMD]
        // [URL]

        // Send the packet
        if (client) {
            // Update single client
            _webui_send_client(
                win, client, 0, WEBUI_CMD_NAVIGATION, 
                (const char*)window_url, _webui_strlen(window_url), false
            );
        }
        else {
            // Update all clients
            _webui_send_all(
                win, 0, WEBUI_CMD_NAVIGATION, 
                (const char*)window_url, _webui_strlen(window_url)
            );
        }

        // Free
        _webui_free_mem((void*)window_url);
    }

    // Wait for window connection
    if ((browser != NoBrowser) && _webui.config.show_wait_connection) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_show_window() -> Waiting for window connection\n");
        #endif

        size_t timeout = (_webui.startup_timeout > 0 ? 
            _webui.startup_timeout : WEBUI_DEF_TIMEOUT
        );

        if (_webui.is_webview) {

            _webui_timer_t timer;
            _webui_timer_start(&timer);
            for (;;) {

                _webui_sleep(10);

                // Process WebView rendering if any
                if (_webui.is_webview) {
                    #ifdef _WIN32
                    // ...
                    #elif __linux__
                    if (_webui.is_webview) {
                        while (gtk_events_pending()) {
                            gtk_main_iteration_do(0);
                        }
                    }
                    #else
                    if (!_webui.is_wkwebview_main_run) {
                        if (_webui.is_webview) {
                            _webui_macos_wv_process();
                        }
                    }
                    #endif
                }

                // Stop if window is connected
                if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                    break;
                
                // Stop if timer is finished
                if (_webui_timer_is_end(&timer, (timeout * 1000)))
                    break;
            }
        } else {

            // Wait for server thread to start
            _webui_timer_t timer;
            _webui_timer_start(&timer);
            for (;;) {

                _webui_sleep(10);

                // Stop if window is connected
                if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                    break;
                
                // Stop if timer is finished
                if (_webui_timer_is_end(&timer, (timeout * 1000)))
                    break;
            }
        }

        // The window is successfully launched and connected.
        return _webui_mutex_is_connected(win, WEBUI_MUTEX_NONE);
    }

    // The window is successfully launched.
    return true;
}

static void _webui_window_event(
    _webui_window_t* win, size_t connection_id, int event_type, char* element,
    size_t event_number, size_t client_id, const char* cookies) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_window_event([%zu], [%zu], [%s])\n", 
        win->num, connection_id, element);
    #endif

    // New Event (General)
    webui_event_t e;
    e.window = win->num;
    e.event_type = event_type;
    e.element = element;
    e.event_number = event_number;
    e.connection_id = connection_id;
    e.client_id = client_id;
    e.cookies = (char*)cookies;

    // Check for all events-bind functions
    if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) && win->has_all_events) {
        size_t events_cb_index = 0;
        bool exist = _webui_get_cb_index(win, "", &events_cb_index);
        if (exist && win->cb[events_cb_index] != NULL) {
            // Call user all-events cb
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_window_event() -> Calling all-events user callback\n");
            printf("[Call]\n");
            #endif
            e.bind_id = events_cb_index;
            win->cb[events_cb_index](&e);
        }
    }

    if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
        // Check for the regular bind functions
        if (!_webui_is_empty(element)) {
            size_t cb_index = 0;
            bool exist = _webui_get_cb_index(win, element, &cb_index);
            if (exist && win->cb[cb_index] != NULL) {
                // Call user cb
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_window_event() -> Calling user callback\n");
                printf("[Call]\n");
                #endif
                e.bind_id = cb_index;
                win->cb[cb_index](&e);
            }
        }
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_window_event() -> Finished\n");
    #endif
}

static void _webui_send_client_ws(_webui_window_t* win, struct mg_connection* client,
    size_t connection_id, char* packet, size_t packets_size) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_send_client_ws()\n");
    printf("[Core]\t\t_webui_send_client_ws() -> Client #%zu\n", connection_id);
    printf("[Core]\t\t_webui_send_client_ws() -> Packet size: %zu bytes \n", packets_size);
    printf("[Core]\t\t_webui_send_client_ws() -> Packet hex : [ ");
        _webui_print_hex(packet, packets_size);
    printf("]\n");
    #endif

    if (win == NULL || client == NULL) {
        WEBUI_ASSERT("_webui_send_client_ws() null ptr");
    }

    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE) || packet == NULL ||
        packets_size < WEBUI_PROTOCOL_SIZE)
        return;

    int ret = 0;
    if (win->num > 0 && win->num < WEBUI_MAX_IDS) {
        if (client != NULL) {
            // Mutex
            _webui_mutex_lock(&_webui.mutex_send);
            ret = mg_websocket_write(client, MG_WEBSOCKET_OPCODE_BINARY, packet, packets_size);
            _webui_mutex_unlock(&_webui.mutex_send);
        }
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_send_client_ws() -> %d bytes sent.\n", ret);
    #endif
}

static char* _webui_get_current_path(void) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_current_path()\n");
    #endif

    char* path = (char*)_webui_malloc(WEBUI_MAX_PATH);
    if (WEBUI_GET_CURRENT_DIR(path, WEBUI_MAX_PATH) == NULL)
        path[0] = 0x00;

    return path;
}

static void _webui_free_port(size_t port) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_free_port([%zu])\n", port);
    #endif

    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.used_ports[i] == port) {
            _webui.used_ports[i] = 0;
            break;
        }
    }
}

static size_t _webui_get_free_port(void) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_free_port()\n");
    #endif

    size_t port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;

    for (size_t i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) {

        // Search [port] in [_webui.used_ports]
        bool found = false;
        for (size_t j = 0; j < WEBUI_MAX_IDS; j++) {
            if (_webui.used_ports[j] == port) {
                found = true;
                break;
            }
        }

        if (found)
            // Port used by local window
            port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
        else {
            if (_webui_port_is_used(port))
                // Port used by an external app
                port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
            else
                // Port is free
                break;
        }
    }

    // Add
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.used_ports[i] == 0) {
            _webui.used_ports[i] = port;
            break;
        }
    }

    return port;
}

static void _webui_init(void) {

    if (_webui.initialized)
        return;
    memset(&_webui, 0, sizeof(_webui_core_t));
    _webui.initialized = true;

    #ifdef WEBUI_LOG
    printf("[Core]\t\tWebUI v"
        WEBUI_VERSION " ("
        WEBUI_OS ", "
        WEBUI_COMPILER ", "
        WEBUI_LIB_TYPE ", "
        WEBUI_SECURE ")\n");
    printf("[Core]\t\t_webui_init()\n");
    #endif

    // Random
    #ifdef _WIN32
    srand((unsigned int) time(NULL));
    #else
    srand(time(NULL));
    #endif

    // Initializing core
    _webui.startup_timeout = WEBUI_DEF_TIMEOUT;
    _webui.executable_path = _webui_get_current_path();
    _webui.default_server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);

    // Initializing configs
    _webui.config.show_wait_connection = true;
    _webui.config.use_cookies = true;

    // Initializing server services
    #ifdef WEBUI_TLS
    if ((unsigned)mg_init_library(MG_FEATURES_TLS) != (unsigned)MG_FEATURES_TLS) {
        WEBUI_ASSERT("mg_init_library() failed");
    }
    #else
    mg_init_library(0);
    #endif

    // Initializing mutex
    _webui_mutex_init(&_webui.mutex_server_start);
    _webui_mutex_init(&_webui.mutex_send);
    _webui_mutex_init(&_webui.mutex_receive);
    _webui_mutex_init(&_webui.mutex_wait);
    _webui_mutex_init(&_webui.mutex_bridge);
    _webui_mutex_init(&_webui.mutex_js_run);
    _webui_mutex_init(&_webui.mutex_win_connect);
    _webui_mutex_init(&_webui.mutex_exit_now);
    _webui_mutex_init(&_webui.mutex_webview_stop);
    _webui_mutex_init(&_webui.mutex_http_handler);
    _webui_mutex_init(&_webui.mutex_client);
    _webui_condition_init(&_webui.condition_wait);
}

static const char* _webui_url_encode(const char* str) {

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_url_encode()\n");
    #endif

    const char* hex = "0123456789ABCDEF";
    size_t len = _webui_strlen(str);
    char* encoded = (char*)_webui_malloc(4 * len + 1);
    if (!encoded)
        return NULL;

    char* pOutput = encoded;
    while(*str) {
        unsigned char byte = (unsigned char)(*str);
        if (isalnum(byte) || byte == '-' || byte == '_' || byte == '.' || byte == '~') {
            * pOutput++ = byte;
        } else {
            * pOutput++ = '%';
            * pOutput++ = hex[byte >> 4];
            * pOutput++ = hex[byte&15];
        }
        str++;
    }

    return (const char*)encoded;
}

static bool _webui_get_cb_index(_webui_window_t* win, const char* element, size_t* id) {

    _webui_mutex_lock(&_webui.mutex_bridge);

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_get_cb_index([%zu])\n", win->num);
    printf("[Core]\t\t_webui_get_cb_index() -> Element: [%s]\n", element);
    #endif

    // Search
    if (element != NULL) {
        for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
            if (win->html_elements[i] != NULL) {
                if (strcmp(win->html_elements[i], element) == 0) {
                    #ifdef WEBUI_LOG_VERBOSE
                    printf("[Core]\t\t_webui_get_cb_index() -> Found at %zu\n", i);
                    #endif
                    _webui_mutex_unlock(&_webui.mutex_bridge);
                    *id = i;
                    return true;
                }
            }
        }
    }

    #ifdef WEBUI_LOG_VERBOSE
    printf("[Core]\t\t_webui_get_cb_index() -> Not found\n");
    #endif

    _webui_mutex_unlock(&_webui.mutex_bridge);
    return false;
}

#ifdef WEBUI_LOG
static void _webui_print_hex(const char* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        printf("0x%02X ", (unsigned char)* data);
        data++;
    }
}
static void _webui_print_ascii(const char* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        if ((unsigned char)* data == 0x00)
            putchar(0xCF); // ¤
        else
            printf("%c", (unsigned char)* data);
        data++;
    }
}
#endif

// HTTP Server

static void _webui_http_send_header(
    _webui_window_t* win, struct mg_connection* client,
    const char* mime_type, size_t body_len, bool cache) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_send_header([%zu])\n", win->num);
    printf("[Core]\t\t_webui_http_send_header() -> mime_type: [%s]\n", mime_type);
    printf("[Core]\t\t_webui_http_send_header() -> body_len: [%zu]\n", body_len);
    printf("[Core]\t\t_webui_http_send_header() -> cache: [%d]\n", cache);
    #endif

    const char* no_cache = "no-cache, no-store, must-revalidate, private, max-age=0";
    const char* with_cache = "public, max-age=31536000";
    const char* cache_header = (cache ? with_cache : no_cache);

    // Cookies
    bool set_cookies = false;
    size_t new_client_id = 0;
    if (_webui.config.use_cookies) {
        // Cookies config is enabled
        char cookies[WEBUI_COOKIES_BUF] = {0};
        _webui_get_cookies(client, cookies);
        bool client_found = false;
        if (!_webui_is_empty(cookies)) {
            size_t client_id = 0;
            if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
                client_found = true;
            }
        }
        if (!client_found) {
            // Browser does not have cookies yet, let's set a new cookies
            char new_auth_cookies[WEBUI_COOKIES_BUF];
            _webui_generate_cookies(new_auth_cookies, WEBUI_COOKIES_LEN);
            if (_webui_client_cookies_save(win, new_auth_cookies, &new_client_id)) {
                set_cookies = true;
                _webui.cookies_single_set[win->num] = true;
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_http_send() -> New auth cookies [%s]\n",
                    _webui.cookies[new_client_id]
                );
                #endif
            }
            else {
                // Cookies list is full
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_http_send() -> Cookies list is full\n");
                #endif
            }
        }
    }

    // [header only]
    char buffer[1024] = {0};
    int to_send = 0;
    if (set_cookies) {
        // Header with auth cookies
        to_send = WEBUI_SN_PRINTF_STATIC(buffer, sizeof(buffer),
            "HTTP/1.1 200 OK\r\n"
            "Set-Cookie: webui_auth=%s; Path=/; HttpOnly; SameSite=Strict\r\n"
            "Access-Control-Allow-Origin: *\r\n"
            "Cache-Control: %s\r\n"
            "Content-Type: %s\r\n"
            "Content-Length: %zu\r\n"
            "Connection: close\r\n\r\n",
            _webui.cookies[new_client_id],
            cache_header, mime_type, body_len
        );
    }
    else {
        // Header without auth cookies
        to_send = WEBUI_SN_PRINTF_STATIC(buffer, sizeof(buffer),
            "HTTP/1.1 200 OK\r\n"
            "Access-Control-Allow-Origin: *\r\n"
            "Cache-Control: %s\r\n"
            "Content-Type: %s\r\n"
            "Content-Length: %zu\r\n"
            "Connection: close\r\n\r\n",
            cache_header, mime_type, body_len
        );
    }

    #ifdef WEBUI_LOG
    printf("---[ HTTP Header ]-----------------\n");
    printf("%s\n", buffer);
    printf("-----------------------------------\n");
    #endif

    // Send
    mg_write(client, buffer, to_send);
}

static void _webui_http_send_file(
    _webui_window_t* win, struct mg_connection* client,
    const char* mime_type, const char* path, bool cache) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_send_file([%zu])\n", win->num);
    #endif

    // Open the file
    FILE* file = NULL;
    WEBUI_FILE_OPEN(file, path, "rb");
    if (!file) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_http_send_file() -> Can't open file [%s]\n", path);
        #endif
        _webui_http_send_error(client, webui_html_res_not_available, 404);
        return;
    }

    // Get file size
    fseek(file, 0, SEEK_END);
    size_t file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // Send header
    _webui_http_send_header(win, client, mime_type, file_size, cache);

    // Send body
    mg_send_file_body(client, path);

    fclose(file);
}

static void _webui_http_send(
    _webui_window_t* win, struct mg_connection* client,
    const char* mime_type, const char* body, size_t body_len, bool cache) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_send([%zu])\n", win->num);
    #endif

    // Send header
    _webui_http_send_header(win, client, mime_type, body_len, cache);

    // Send body
    mg_write(client, body, body_len);
}

static void _webui_http_send_error(struct mg_connection* client, const char* body, int status) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_send_error()\n");
    #endif

    // [header][body]
    int to_send = 0;
    size_t body_len = _webui_strlen(body);
    size_t buffer_len = (512 + body_len);
    char* buffer = (char*)_webui_malloc(buffer_len);
    to_send = WEBUI_SN_PRINTF_DYN(buffer, buffer_len,
        "HTTP/1.1 %d OK\r\n"
        "Content-Type: text/html; charset=utf-8\r\n"
        "Cache-Control: no-cache, no-store, must-revalidate, private, max-age=0\r\n"
        "Pragma: no-cache\r\n"
        "Expires: 0\r\n"
        "Content-Length: %zu\r\n"
        "Connection: close\r\n\r\n%s",
        status, body_len, body
    );

    // Send
    mg_write(client, buffer, to_send);
    _webui_free_mem((void*)buffer);
}

#ifdef WEBUI_LOG
static int _webui_http_log(const struct mg_connection* client, const char* message) {
    (void)client;
    printf("[Core]\t\t_webui_http_log()\n");
    printf("[Core]\t\t_webui_http_log() -> Log: %s.\n", message);
    return 1;
}
#endif

// static size_t _webui_hash_djb2(const char* s) {
//     // DJB2 Algorithm
//     size_t hash = 5381;
//     int c;
//     while ((c = *s++)) {
//         // hash * 33 + c
//         hash = ((hash << 5) + hash) + c;
//     }
//     return hash;
// }

static void _webui_generate_cookies(char* cookies, size_t length) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_generate_cookies()\n");
    #endif
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    size_t charset_size = sizeof(charset) - 1;
    for (size_t i = 0; i < length - 1; i++) {
        uint32_t random_value = _webui_generate_random_uint32();
        int key = random_value % charset_size;
        cookies[i] = charset[key];
    }
    cookies[length - 1] = '\0';
}

static bool _webui_client_cookies_save(_webui_window_t* win, const char* cookies, size_t* client_id) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_client_cookies_save()\n");
    #endif
    // [win number][_][cookies]
    char win_cookies[WEBUI_COOKIES_BUF];
    WEBUI_SN_PRINTF_STATIC(win_cookies, sizeof(win_cookies), "%zu_%s", win->num, cookies);
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.cookies[i] == NULL) {
            _webui.cookies[i] = _webui_str_dup(win_cookies);
            *client_id = i;
            return true;
        }
    }
    // List is full
    return false;
}

static bool _webui_client_cookies_get_id(_webui_window_t* win, const char* cookies, size_t* client_id) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_client_cookies_get_id()\n");
    #endif
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.cookies[i] != NULL) {
            if (strcmp(_webui.cookies[i], cookies) == 0) {
                *client_id = i;
                return true;
            }
        }
    }
    // Not found
    return false;
}

static size_t _webui_client_get_id(_webui_window_t* win, struct mg_connection* client) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_client_get_id()\n");
    #endif
    size_t client_id = 0;
    if (_webui.config.use_cookies) {
        char cookies[WEBUI_COOKIES_BUF] = {0};
        _webui_get_cookies(client, cookies);
        _webui_client_cookies_get_id(win, cookies, &client_id);
    }
    return client_id;
}

// static void _webui_client_cookies_free_all(_webui_window_t* win) {
//     #ifdef WEBUI_LOG
//     printf("[Core]\t\t_webui_client_cookies_free_all()\n");
//     #endif
//     // [win number]
//     char win_num[24];
//     WEBUI_SN_PRINTF_STATIC(win_num, sizeof(win_num), "%zu_", win->num);
//     size_t len = strlen(win_num);
//     for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
//         if (_webui.cookies[i] != NULL) {
//             // [{win_number}_{cookies}]
//             if (strncmp(_webui.cookies[i], win_num, len) == 0) {
//                 _webui_free_mem((void*)_webui.cookies[i]);
//                 _webui.cookies[i] = NULL;
//             }
//         }
//     }
//     // Single
//     _webui.cookies_single_set[win->num] = false;
// }

// static void _webui_client_cookies_free(_webui_window_t* win, struct mg_connection* client) {
//     #ifdef WEBUI_LOG
//     printf("[Core]\t\t_webui_client_cookies_free()\n");
//     #endif
//     char cookies[WEBUI_COOKIES_BUF] = {0};
//     _webui_get_cookies(client, cookies);
//     if (cookies != NULL) {
//         size_t client_id = 0;
//         if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
//             _webui_free_mem((void*)_webui.cookies[client_id]);
//             _webui.cookies[client_id] = NULL;
//         }
//     }
// }

static const char* _webui_get_cookies_full(const struct mg_connection* client) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_cookies_full()\n");
    #endif
    const char* header = mg_get_header(client, "Cookie");
    if (header != NULL)
        return header;
    return "";
}

static void _webui_get_cookies(const struct mg_connection* client, char* buffer) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_cookies()\n");
    #endif
    const char* header = mg_get_header(client, "Cookie");
    if (!_webui_is_empty(header)) {
        mg_get_cookie(header, "webui_auth", buffer, WEBUI_COOKIES_BUF);
    }
}

static int _webui_http_handler(struct mg_connection* client, void * _win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_handler()\n");
    #endif

    // Mutex
    _webui_mutex_lock(&_webui.mutex_http_handler);

    // Get the window object
    _webui_window_t* win = _webui_dereference_win_ptr(_win);
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL) {
        _webui_mutex_unlock(&_webui.mutex_http_handler);
        return 500; // Internal Server Error
    }

    // Initializing
    int http_status_code = 200;
    const struct mg_request_info * ri = mg_get_request_info(client);
    const char* url = ri->local_uri;

    if (strcmp(ri->request_method, "GET") == 0) {

        // GET

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_http_handler() -> GET [%s]\n", url);
        #endif

        // Cookies
        size_t client_id = 0;
        bool client_found = false;
        if (_webui.config.use_cookies) {
            // Cookies config is enabled
            char cookies[WEBUI_COOKIES_BUF] = {0};
            _webui_get_cookies(client, cookies);
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_http_handler() -> Client cookies [%s]\n", cookies);
            #endif
            // Get client ID based on `webui_auth` cookies
            if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_http_handler() -> Client ID found [%zu]\n", client_id);
                #endif
                client_found = true;
            }
            #ifdef WEBUI_LOG
            else {
                printf("[Core]\t\t_webui_http_handler() -> Client ID not found\n");
            }
            #endif
        }
        
        // Single client authorisation
        if (!_webui.config.multi_client) {
            if (!client_found) {
                if ((_webui.cookies_single_set[win->num])) {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_http_handler() -> 403 Forbidden\n");
                    #endif
                    _webui_http_send_error(client, webui_html_served, 403);
                    _webui_mutex_unlock(&_webui.mutex_http_handler);
                    return 403;                    
                }
            } else _webui.cookies_single_set[win->num] = true;
        }

        // Let the server thread waits more time for `webui.js`
        win->wait = true;

        if (strcmp(url, "/webui.js") == 0) {

            // WebUI Bridge

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_http_handler() -> WebUI-Bridge\n");
            #endif

            // Generate JavaScript bridge
            const char* js = _webui_generate_js_bridge(win, client);

            if (js != NULL) {
                // Send 200
                _webui_http_send(win, client, "application/javascript", js, _webui_strlen(js), false);
                _webui_free_mem((void*)js);
            } else {
                // Non-authorized request to `webui.js`, like requesting twice
                // Send 200 (Empty)
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_http_handler() -> Non-authorized request to webui.js\n");
                #endif
                _webui_http_send(win, client, "application/javascript", "", 0, false);
            }
        }
        else if ((win->files_handler != NULL || (win->files_handler_window != NULL)) && (_webui_external_file_handler(win, client, client_id) != 0)) {

            // File already handled by the custom external file handler
            // nothing to do now.
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_http_handler() -> Handled by custom external file handler\n");
            #endif
        }
        else if (strcmp(url, "/") == 0) {

            // [/]

            if (win->is_embedded_html) {

                // Main HTML

                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_http_handler() -> Embedded Index HTML\n");
                #endif

                // Send 200
                _webui_http_send(win, client, "text/html", win->html, _webui_strlen(win->html), false);
            }
            else {

                // Looking for index file and redirect

                const char* index_files[] = {"index.ts", "index.js", "index.html", "index.htm"};

                // [Path][Sep][File Name]
                size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + 24);
                char* index_path = (char*)_webui_malloc(bf_len);
                for (size_t i = 0; i < (sizeof(index_files) / sizeof(index_files[0])); i++) {
                    WEBUI_SN_PRINTF_DYN(index_path, bf_len, "%s%s%s",
                        win->server_root_path, os_sep, index_files[i]
                    );
                    if (_webui_file_exist(index_path)) {
                        #ifdef WEBUI_LOG
                        printf("[Core]\t\t_webui_http_handler() -> 302 Redirecting to [%s]\n", index_files[i]);
                        #endif
                        mg_send_http_redirect(client, index_files[i], 302);
                        _webui_free_mem((void*)index_path);
                        _webui_mutex_unlock(&_webui.mutex_http_handler);
                        return 302;
                    }
                }

                // No index file is found in this folder
                _webui_free_mem((void*)index_path);
                _webui_http_send_error(client, webui_html_res_not_available, 404);
                http_status_code = 404;
            }
        }
        else if (strcmp(url, "/favicon.ico") == 0 || strcmp(url, "/favicon.svg") == 0) {

            // Favicon

            if (win->icon != NULL && win->icon_type != NULL) {

                // Custom user icon

                // User icon 200
                _webui_http_send(win, client, win->icon_type, win->icon, _webui_strlen(win->icon), false);
            }
            else if (_webui_file_exist_mg(win, client)) {

                // Local icon file
                http_status_code = _webui_serve_file(win, client, client_id);
            }
            else {

                // Default embedded icon

                if (strcmp(url, "/favicon.ico") == 0) {

                    mg_send_http_redirect(client, "favicon.svg", 302);
                    http_status_code = 302;
                }
                else {

                    // Default icon 200
                    _webui_http_send(win, client, webui_def_icon_type, webui_def_icon, _webui_strlen(webui_def_icon), false);
                }
            }
        } else {

            // [/file] or [/folder]

            // [Path][Sep][folder]
            char* folder_path = _webui_get_full_path(win, url);

            if (_webui_file_exist(folder_path)) {

                // [/file]

                bool script = false;
                if (win->runtime != None) {
                    const char* extension = _webui_get_extension(url);
                    const char* index_extensions[] = {
                        "js", "ts"
                    };
                    for (size_t i = 0; i < (sizeof(index_extensions) / sizeof(index_extensions[0])); i++) {
                        if (strcmp(extension, index_extensions[i]) == 0) {
                            script = true;
                            break;
                        }
                    }
                }

                if (script) {

                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_http_handler() -> Interpret local script file\n");
                    #endif

                    // Serve as a script file to be interpreted by
                    // an external interpreter (Deno, Bun, Nodejs)
                    http_status_code = _webui_interpret_file(win, client, NULL, client_id);
                }
                else {

                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_http_handler() -> Local file\n");
                    #endif

                    // Serve as a normal text-based file
                    http_status_code = _webui_serve_file(win, client, client_id);
                }
            }
            else if (_webui_folder_exist(folder_path)) {

                // [/folder]
                
                // Looking for index file and redirect

                const char* index_files[] = {"index.ts", "index.js", "index.html", "index.htm"};

                // [Path][Sep][File Name]
                size_t bf_len = (_webui_strlen(folder_path) + 1 + 24);
                char* index_path = (char*)_webui_malloc(bf_len);
                for (size_t i = 0; i < (sizeof(index_files) / sizeof(index_files[0])); i++) {
                    WEBUI_SN_PRINTF_DYN(index_path, bf_len, "%s%s%s", folder_path, os_sep, index_files[i]);
                    if (_webui_file_exist(index_path)) {
                        // [URL][/][Index Name]
                        size_t redirect_len = (_webui_strlen(url) + 1 + 24);
                        char* redirect_url = (char*)_webui_malloc(bf_len);
                        WEBUI_SN_PRINTF_DYN(redirect_url, redirect_len, "%s%s%s", url, "/", index_files[i]);
                        #ifdef WEBUI_LOG
                        printf("[Core]\t\t_webui_http_handler() -> 302 Redirecting to [%s]\n", redirect_url);
                        #endif
                        mg_send_http_redirect(client, redirect_url, 302);
                        _webui_free_mem((void*)redirect_url);
                        _webui_free_mem((void*)folder_path);
                        _webui_free_mem((void*)index_path);
                        _webui_mutex_unlock(&_webui.mutex_http_handler);
                        return 302;
                    }
                }

                // No index file is found in this folder
                _webui_free_mem((void*)folder_path);
                _webui_free_mem((void*)index_path);
                _webui_http_send_error(client, webui_html_res_not_available, 404);
                http_status_code = 404;
            }
            else {

                // [invalid]
				
				#ifdef WEBUI_LOG
				printf("[Core]\t\t_webui_http_handler() -> Not found\n");
				#endif

                // No file or folder is found at this path
                _webui_http_send_error(client, webui_html_res_not_available, 404);
                _webui_free_mem((void*)folder_path);
                http_status_code = 404;
            }

            // Clear
            _webui_free_mem((void*)folder_path);
        }
    } else {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_http_handler() -> Unknown request method [%s]\n", ri->request_method);
        #endif
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_http_handler() -> HTTP Status Code: %d\n", http_status_code);
    #endif

    _webui_mutex_unlock(&_webui.mutex_http_handler);
    return http_status_code;
}

static int _webui_ws_connect_handler(const struct mg_connection* client, void * _win) {
    (void)client;
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_connect_handler()\n");
    #endif

    // Dereference
    _webui_window_t* win = _webui_dereference_win_ptr(_win);
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL)
        return 1;
    
    // Check connection status
    if (!_webui.config.multi_client) {
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            // Multi-client is disabled, and the single client already connected.
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_ws_connect_handler() -> Single client already connected\n");
            printf("[Core]\t\t_webui_ws_connect_handler() -> Non-authorized connection\n");
            #endif
            // Block handshake
            return 1;
        }
    }
    
    // Cookies
    size_t client_id = 0;
    bool client_found = false;
    if (_webui.config.use_cookies) {
        // Cookies config is enabled
        char cookies[WEBUI_COOKIES_BUF] = {0};
        _webui_get_cookies(client, cookies);
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_ws_connect_handler() -> Client cookies [%s]\n", cookies);
        #endif
        // Get client ID based on `webui_auth` cookies
        if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_ws_connect_handler() -> Client ID found [%zu]\n", client_id);
            #endif
            client_found = true;
        }
        #ifdef WEBUI_LOG
        else {
            printf("[Core]\t\t_webui_ws_connect_handler() -> Client ID not found\n");
        }
        #endif
    }
    
    // Single client authorisation
    if (!_webui.config.multi_client) {
        if (!client_found) {
            if ((_webui.cookies_single_set[win->num])) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_ws_connect_handler() -> 403 Forbidden\n");
                #endif
                // Block handshake
                return 1;
            }
        }
    }
    
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_connect_handler() -> Connection authentication OK\n");
    #endif

    // OK. Process handshake
    return 0;
}

static void _webui_ws_ready_handler(struct mg_connection* client, void * _win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_ready_handler()\n");
    #endif

    // Dereference
    _webui_window_t* win = _webui_dereference_win_ptr(_win);
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL)
        return;

    _webui_receive(win, client, WEBUI_WS_OPEN, NULL, 0);
}

static int _webui_ws_data_handler(struct mg_connection* client, int opcode, char* data, size_t datasize, void * _win) {
    (void)client;
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_data_handler()\n");
    #endif

    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || datasize < WEBUI_PROTOCOL_SIZE)
        return 1; // OK

    switch(opcode&0xf) {

        case MG_WEBSOCKET_OPCODE_BINARY: {
            _webui_window_t* win = _webui_dereference_win_ptr(_win);
            if (win != NULL)
                _webui_receive(win, client, WEBUI_WS_DATA, data, datasize);
            break;
        }
        case MG_WEBSOCKET_OPCODE_TEXT: {
            break;
        }
        case MG_WEBSOCKET_OPCODE_PING: {
            break;
        }
        case MG_WEBSOCKET_OPCODE_PONG: {
            break;
        }
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_data_handler() -> Finished\n");
    #endif

    // OK
    return 1;
}

static void _webui_ws_close_handler(const struct mg_connection* client, void * _win) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_close_handler()\n");
    #endif

    // Dereference
    _webui_window_t* win = _webui_dereference_win_ptr(_win);
    if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL || !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
        return;

    _webui_receive(win, (struct mg_connection*)client, WEBUI_WS_CLOSE, NULL, 0);
}

static WEBUI_THREAD_SERVER_START {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_server_thread()\n");
    #endif

    // Mutex
    _webui_mutex_lock(&_webui.mutex_server_start);

    _webui_window_t* win = _webui_dereference_win_ptr(arg);
    if (win == NULL || win->server_running) {
        _webui_mutex_unlock(&_webui.mutex_server_start);
        WEBUI_THREAD_RETURN
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_server_thread([%zu]) -> URL: [%s]\n", win->num, win->url);
    #endif

    // Folder monitor thread
    bool monitor_created = false;
    #ifdef _WIN32
    HANDLE monitor_thread = NULL;
    #else
    pthread_t monitor_thread;
    #endif

    // Initialization
    _webui.servers++;
    win->server_running = true;
    if (_webui.startup_timeout < 1)
        _webui.startup_timeout = 0;
    if (_webui.startup_timeout > WEBUI_MAX_TIMEOUT)
        _webui.startup_timeout = WEBUI_MAX_TIMEOUT;

    // Public host access
    char host[16] = {0};
    if (!win->is_public)
        // Private localhost access
        WEBUI_STR_COPY_STATIC(host, sizeof(host), "127.0.0.1:");

    #ifdef WEBUI_TLS
    // HTTP Secure Port
    char* server_port = (char*)_webui_malloc(64);
    WEBUI_SN_PRINTF_DYN(server_port, 64, "%s%zus", host, win->server_port);
    #else
    // HTTP Port
    char* server_port = (char*)_webui_malloc(64);
    WEBUI_SN_PRINTF_DYN(server_port, 64, "%s%zu", host, win->server_port);
    #endif

    // Server Options
    const char* http_options[] = {
        // HTTP
        "listening_ports", server_port,
        "document_root", win->server_root_path,
        "access_control_allow_headers", "*",
        "access_control_allow_methods", "*",
        "access_control_allow_origin", "*",
        #ifdef WEBUI_TLS
        "authentication_domain", "localhost",
        "enable_auth_domain_check", "no",
        "ssl_protocol_version", "4",
        "ssl_cipher_list", "ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS",
        "strict_transport_security_max_age", WEBUI_SSL_EXPIRE_STR,
        #endif
        // WS
        "websocket_timeout_ms", "3600000",
        "enable_websocket_ping_pong", "yes",
        NULL, NULL
    };

    // Server Settings
    struct mg_callbacks http_callbacks;
    struct mg_context * http_ctx = NULL;
    memset(&http_callbacks, 0, sizeof(http_callbacks));
    #ifdef WEBUI_TLS
    http_callbacks.init_ssl = _webui_tls_initialization;
    #endif
    #ifdef WEBUI_LOG
    http_callbacks.log_message = _webui_http_log;
    #endif

    // Start Server
    http_ctx = mg_start(&http_callbacks, 0, http_options);
    mg_set_request_handler(http_ctx, "/", _webui_http_handler, (void*)win);

    if (http_ctx) {

        mg_set_websocket_handler(
            http_ctx, "/_webui_ws_connect", _webui_ws_connect_handler, _webui_ws_ready_handler,
            _webui_ws_data_handler, _webui_ws_close_handler, (void*)win
        );

        // Mutex
        _webui_mutex_unlock(&_webui.mutex_server_start);

        if (_webui.startup_timeout > 0) {

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening Success\n", 
                win->num);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> HTTP/WS Port: %s\n", 
                win->num, server_port);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Root path: %s\n", 
                win->num, win->server_root_path);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Timeout is %zu seconds\n", 
                win->num, _webui.startup_timeout);
            #endif

            bool stop = false;
            win->wait = false;

            while(!stop) {

                if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {

                    // UI is not connected

                    #ifdef WEBUI_LOG
                    printf(
                        "[Core]\t\t_webui_server_thread([%zu]) -> Waiting for connection\n",
                        win->num
                    );
                    #endif

                    // Wait for first connection
                    _webui_timer_t timer_1;
                    _webui_timer_start(&timer_1);
                    for (;;) {

                        _webui_sleep(1);

                        // Stop if window is connected
                        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                            break;

                        // Stop if timer is finished (Default WEBUI_DEF_TIMEOUT)
                        if (_webui_timer_is_end(&timer_1, (_webui.startup_timeout * 1000)))
                            break;
                    }

                    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE) && win->wait) {

                        // At this moment the browser is already started and HTML
                        // files are already handled, let's wait more time to give
                        // the WebSocket an extra seconds to connect. This is helpful
                        // when a web app have too many files to handel before `webui.js`
                        // get requested.

                        do {
                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_server_thread([%zu]) -> Waiting more for connection\n",
                                win->num
                            );
                            #endif

                            win->wait = false;

                            _webui_timer_t timer_2;
                            _webui_timer_start(&timer_2);
                            for (;;) {

                                // Stop if window is connected
                                _webui_sleep(1);
                                if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                                    break;

                                // Stop if timer is finished
                                if (_webui_timer_is_end(&timer_2, 5000))
                                    break;
                            }
                        } while(win->wait && !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE));
                    }

                    if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                        stop = true; // First run failed
                }
                else {

                    // UI is connected
                    win->is_closed = false;

                    #ifdef WEBUI_LOG
                    printf(
                        "[Core]\t\t_webui_server_thread([%zu]) -> Window Connected.\n",
                        win->num
                    );
                    #endif

                    // Folder monitor thread
                    if (_webui.config.folder_monitor && !monitor_created) {
                        monitor_created = true;
                        #ifdef _WIN32
                        monitor_thread = CreateThread(NULL, 0, _webui_folder_monitor_thread, (void*)win, 0, NULL);
                        if (monitor_thread != NULL)
                            CloseHandle(monitor_thread);
                        #else
                        pthread_create(&monitor_thread, NULL, &_webui_folder_monitor_thread, (void*)win);
                        pthread_detach(monitor_thread);
                        #endif
                    }

                    while(!stop) {

                        // Wait forever for disconnection

                        _webui_sleep(1);

                        // Exit signal
                        if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
                            stop = true;
                            break;
                        }

                        if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {

                            // The UI is just get disconnected

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_server_thread([%zu]) -> Window disconnected\n",
                                win->num
                            );
                            #endif

                            if (!win->is_closed) {

                                // probably the user did a refresh
                                // let's wait for re-connection...

                                do {
                                    #ifdef WEBUI_LOG
                                    printf(
                                        "[Core]\t\t_webui_server_thread([%zu]) -> Waiting for reconnection\n",
                                        win->num
                                    );
                                    #endif

                                    win->wait = false;

                                    _webui_timer_t timer_3;
                                    _webui_timer_start(&timer_3);
                                    for (;;) {

                                        // Stop if window is re-connected
                                        _webui_sleep(1);
                                        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
                                            break;

                                        // Stop if timer is finished
                                        if (_webui_timer_is_end(&timer_3, WEBUI_RELOAD_TIMEOUT))
                                            break;
                                    }
                                } while(win->wait && !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE));

                                if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
                                    stop = true;
                                    break;
                                }
                            }
                            else {

                                // Window get closed
                                stop = true;
                                break;
                            }
                        }
                    }
                }
            }
        }

        // Let's check the flag again, there is a change that
        // the flag has ben changed during the first loop for
        // example when set_timeout() get called after show()
        if (_webui.startup_timeout == 0) {

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening Success\n", 
                win->num);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> HTTP/WS Port: %s\n", 
                win->num, server_port);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Root path: %s\n", 
                win->num, win->server_root_path);
            printf("[Core]\t\t_webui_server_thread([%zu]) -> Infinite loop\n", 
                win->num);
            #endif

            // Folder monitor thread
            if (_webui.config.folder_monitor && !monitor_created) {
                monitor_created = true;
                #ifdef _WIN32
                monitor_thread = CreateThread(NULL, 0, _webui_folder_monitor_thread, (void*)win, 0, NULL);
                if (monitor_thread != NULL)
                    CloseHandle(monitor_thread);
                #else
                pthread_create(&monitor_thread, NULL, &_webui_folder_monitor_thread, (void*)win);
                pthread_detach(monitor_thread);
                #endif
            }

            // Wait forever
            for (;;) {
                _webui_sleep(1);
                if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
                    break;
            }
        }
    } else {

        #ifdef WEBUI_LOG
        if (!http_ctx) printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening failed.\n", win->num);
        #endif

        // Mutex
        _webui_mutex_unlock(&_webui.mutex_server_start);
    }

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_server_thread([%zu]) -> Cleaning\n", win->num);
    #endif

    _webui_mutex_is_connected(win, WEBUI_MUTEX_FALSE);

    // Clean WebView
    if (win->webView) {
        win->webView->stop = true;
        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
    }

    // Clean
    win->server_running = false;
    _webui_free_port(win->server_port);
    win->server_port = 0;
    _webui_free_mem((void*)server_port);
    // _webui_client_cookies_free_all(win);

    // Kill Process
    // _webui_kill_pid(win->process_id);
    // win->process_id = 0;

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_server_thread([%zu]) -> Server stopped.\n", win->num);
    #endif

    // Let the main wait() know that
    // this server thread is finished
    if (_webui.servers > 0)
        _webui.servers--;

    // Stop server services
    // This should be at the
    // end as it may take time
    mg_stop(http_ctx);

    // Fire the mutex condition for wait()
    if (_webui.startup_timeout > 0 && _webui.servers < 1) {

        // Stop all threads
        _webui.ui = false;
        _webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);
        // Break main loop
        _webui_condition_signal(&_webui.condition_wait);
        #ifdef __APPLE__
        _webui_macos_wv_stop();
        #endif
    }

    // Clean monitor thread
    if (_webui.config.folder_monitor && monitor_created) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_server_thread([%zu]) -> Killing folder monitor thread\n", win->num);
        #endif
        #ifdef _WIN32
        TerminateThread(monitor_thread, 0);
        CloseHandle(monitor_thread);
        #else
        if (monitor_thread) {
            pthread_cancel(monitor_thread);
        }
        #endif
    }

    WEBUI_THREAD_RETURN
}

static void _webui_receive(_webui_window_t* win, struct mg_connection* client,
    int event_type, void * data, size_t len) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_receive([%zu], [%d], [%zu])\n", win->num, event_type, len);
    #endif

    static size_t recvNum = 0;
    static bool multi_packet = false;
    static size_t multi_expect = 0;
    static size_t multi_receive = 0;
    static void * multi_buf = NULL;

    // Get connection id
    size_t connection_id = 0;
    if (event_type != WEBUI_WS_OPEN) {
        if (!_webui_connection_get_id(win, client, &connection_id)) {
            // Failed to find connection ID
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_receive() -> Failed to find connection ID\n");
            #endif
            _webui_connection_remove(win, client);
            return;
        }
    }

    // Websocket Open/Close Events
    if (event_type == WEBUI_WS_OPEN) {
        // New connection
        // Autorisation to register
        bool authorization = false;
        if (!_webui.config.multi_client) {
            if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
                authorization = true;
            }
        } else authorization = true;
        if (!authorization) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_receive(%zu) -> Connection not authorized to register\n",
                recvNum);
            #endif
            _webui_connection_remove(win, client);
            return;
        }
        else {
            // Register
            if (_webui_connection_save(win, client, &connection_id)) {
                // Update window connection status
                _webui_mutex_is_connected(win, WEBUI_MUTEX_TRUE);
                #ifdef WEBUI_LOG
                printf(
                    "[Core]\t\t_webui_receive(%zu) -> Connection #%zu registered\n",
                    recvNum, connection_id
                );
                #endif
            }
            else {
                // Connection failed to register
                _webui_connection_remove(win, client);
                return;
            }
        }
    } else if (event_type == WEBUI_WS_CLOSE) {
        // Connection close
        _webui_connection_remove(win, client);
        #ifdef WEBUI_LOG
        printf(
            "[Core]\t\t_webui_receive(%zu) -> Connection #%zu Closed\n",
            recvNum, connection_id
        );
        #endif
    }

    // Multi Packet (big data)
    if (multi_packet) {
        if ((multi_receive + len) > multi_expect) {
            // Received more data than expected
            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_receive() -> Multi packet received more data than expected (%zu + %zu > %zu).\n",
                multi_receive, len, multi_expect
            );
            #endif
            multi_packet = false;
            multi_expect = 0;
            multi_receive = 0;
            _webui_free_mem(multi_buf);
            multi_buf = NULL;
            return;
        }
        // Accumulate packet
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_receive() -> Multi packet accumulate %zu bytes\n", len);
        #endif
        memcpy(((unsigned char*)multi_buf + multi_receive), data, len);
        multi_receive += len;
        // Check if theire is more packets comming
        if (multi_receive < multi_expect)
            return;
    }
    else if (event_type == WEBUI_WS_DATA) {
        if (((unsigned char*)data)[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_MULTI) {
            if (len >= WEBUI_PROTOCOL_SIZE && ((unsigned char*)data)[WEBUI_PROTOCOL_SIGN] == WEBUI_SIGNATURE) {
                size_t expect_len = (size_t) strtoul(&((const char*)data)[WEBUI_PROTOCOL_DATA], NULL, 10);
                if (expect_len > 0 && expect_len <= WEBUI_MAX_BUF) {
                    #ifdef WEBUI_LOG
                    printf(
                        "[Core]\t\t_webui_receive() -> Multi packet started, Expecting %zu bytes\n",
                        expect_len
                    );
                    #endif
                    multi_buf = _webui_malloc(expect_len);
                    memcpy(multi_buf, data, len);
                    multi_receive = 0;
                    multi_expect = expect_len;
                    multi_packet = true;
                }
            }
            return;
        }
    }

    // Generate args
    void * arg_ptr = NULL;
    size_t arg_len = 0;
    if (multi_packet) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_receive() -> Processing multi packet\n");
        #endif
        // Get data from accumulated multipackets
        arg_len = multi_receive;
        arg_ptr = multi_buf;
        // Reset
        multi_packet = false;
        multi_expect = 0;
        multi_receive = 0;
        multi_buf = NULL;
    } else {
        arg_len = len;
        if (len > 0) {
            if (win->ws_block) {
                // This event has data. And it will be processed
                // in this current thread, no need to copy data
                arg_ptr = data;
            }
            else {
                // This event has data. And it will be processed
                // in a new thread, we should copy data once
                void * data_cpy = (void*)_webui_malloc(len);
                memcpy((char*)data_cpy, data, len);
                arg_ptr = data_cpy;
            }
        } else {
            // This is a WS connect/disconnect event
            arg_ptr = NULL;
        }
    }

    // Process
    if (win->ws_block) {
        // Process the packet in this current thread
        _webui_ws_process(win, client, connection_id, arg_ptr, arg_len, ++recvNum, event_type);
        if (arg_ptr != data)
            _webui_free_mem((void*)arg_ptr);
    }
    else {
        // Process the packet in a new thread
        _webui_recv_arg_t* arg = (_webui_recv_arg_t* ) _webui_malloc(sizeof(_webui_recv_arg_t));
        arg->win = win;
        arg->ptr = arg_ptr;
        arg->len = arg_len;
        arg->recvNum = ++recvNum;
        arg->event_type = event_type;
        arg->client = client;
        arg->connection_id = connection_id;
        #ifdef _WIN32
        HANDLE thread = CreateThread(NULL, 0, _webui_ws_process_thread, (void*)arg, 0, NULL);
        if (thread != NULL)
            CloseHandle(thread);
        #else
        pthread_t thread;
        pthread_create(&thread, NULL,&_webui_ws_process_thread, (void*)arg);
        pthread_detach(thread);
        #endif
    }
}

static bool _webui_connection_save(_webui_window_t* win, struct mg_connection* client, size_t* connection_id) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_save([%zu])\n", win->num);
    #endif

    // Save new ws client
    _webui_mutex_lock(&_webui.mutex_client);
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.clients[i] == NULL) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_connection_save() -> Registering client #%zu \n", i);
            #endif
            // Save
            if (win->single_client == NULL) {
                win->single_client = client;
                win->single_client_token_check = false;
            }
            _webui.clients[i] = client;
            _webui.clients_win_num[i] = win->num;
            _webui.clients_token_check[i] = false;
            win->clients_count++;
            _webui_mutex_unlock(&_webui.mutex_client);
            *connection_id = i;
            return true;
        }
    }
    
    // List is full
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_save() -> Clients list is full\n");
    #endif
    _webui_mutex_unlock(&_webui.mutex_client);
    return false;
}

static void _webui_connection_remove(_webui_window_t* win, struct mg_connection* client) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_remove([%zu])\n", win->num);
    #endif

    _webui_mutex_lock(&_webui.mutex_client);

    // Remove a ws client
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.clients[i] == client) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_connection_remove() -> Removing client #%zu \n", i);
            #endif
            // Reset Token
            if (!_webui.config.multi_client) {
                if (_webui.clients_token_check[i]) {
                    win->token = 0;
                }
            }
            // Clear
            if (win->single_client == client) {
                win->single_client = NULL;
                win->single_client_token_check = false;
            }
            _webui.clients[i] = NULL;
            _webui.clients_win_num[i] = 0;
            _webui.clients_token_check[i] = false;
            if (win->clients_count > 0)
                win->clients_count--;
            // Close
            _webui_mutex_unlock(&_webui.mutex_client);
            mg_close_connection(client);
            return;
        }
    }

    // Client not found
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_remove() -> Client not found\n");
    #endif
    _webui_mutex_unlock(&_webui.mutex_client);
    mg_close_connection(client);
}

static bool _webui_connection_get_id(_webui_window_t* win, struct mg_connection* client, size_t* connection_id) {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_get_id([%zu], [%p])\n", win->num, client);
    #endif

    // Find a ws client
    _webui_mutex_lock(&_webui.mutex_client);
    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
        if (_webui.clients[i] == client) {
            *connection_id = i;
            _webui_mutex_unlock(&_webui.mutex_client);
            return true;
        }
    }

    // Client not found
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_connection_get_id() -> Client not found\n");
    #endif
    _webui_mutex_unlock(&_webui.mutex_client);
    return false;
}

static void _webui_ws_process(
    _webui_window_t* win, struct mg_connection* client, size_t connection_id, 
    void* ptr, size_t len, size_t recvNum, int event_type) {
    
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_ws_process(%zu)\n", recvNum);
    #endif

    if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_ws_process(%zu) -> Start\n", recvNum);
        #endif

        if (event_type == WEBUI_WS_DATA) {

            const char* packet = (const char*)ptr;
            uint32_t packet_token = _webui_get_token(packet);
            uint16_t packet_id = _webui_get_id(packet);

            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> Data received\n",
                recvNum
            );
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> Packet Size : %zu bytes\n",
                recvNum, len
            );
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> Packet Header : [ ",
                recvNum
            );
            _webui_print_hex(packet, WEBUI_PROTOCOL_SIZE);
            printf("]\n");
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> Packet Token: 0x%08X (%" PRIu32 ")\n",
                recvNum, packet_token, packet_token
            );
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> Packet ID: 0x%04X (%u)\n",
                recvNum, packet_id, packet_id
            );
            printf("[Core]\t\t_webui_ws_process(%zu) -> Packet Data: [", recvNum);
                // _webui_print_ascii(&packet[WEBUI_PROTOCOL_DATA], (len -
                // WEBUI_PROTOCOL_SIZE));
            printf("]\n");
            #endif

            if ((len >= WEBUI_PROTOCOL_SIZE) &&
                ((unsigned char)packet[WEBUI_PROTOCOL_SIGN] == WEBUI_SIGNATURE) &&
                (packet_token == win->token)) {

                // Mutex
                if (_webui.config.ws_block) {
                    // wait for previous event to finish
                    if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] != WEBUI_CMD_JS) {
                        _webui_mutex_lock(&_webui.mutex_receive);
                    }
                }

                if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) { // Check if previous event called exit()

                    if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CLICK) {

                        // Click Event

                        // Protocol
                        // [Header]
                        // [Element]

                        // Get html element id
                        char* element = (char*)&packet[WEBUI_PROTOCOL_DATA];
                        size_t element_len = _webui_strlen(element);

                        #ifdef WEBUI_LOG
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CLICK \n",
                            recvNum
                        );
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> Element size: %zu bytes \n",
                            recvNum, element_len
                        );
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> Element : [%s] \n",
                            recvNum, element
                        );
                        #endif

                        // New event inf (Click)
                        // webui_event_inf_t* event_inf = NULL;
                        // size_t event_num = _webui_new_event_inf(win, &event_inf);

                        // Set arguments
                        // ...

                        _webui_window_event(
                            win, // Event -> Window
                            connection_id, // Event -> Client Unique ID
                            WEBUI_EVENT_MOUSE_CLICK, // Event -> Type of this event
                            element, // Event -> HTML Element
                            0, // Event -> Event Number
                            _webui_client_get_id(win, client), // Event -> Client ID
                            _webui_get_cookies_full(client) // Event -> Full cookies
                        );

                        // Free event
                        // _webui_free_event_inf(win, event_num);
                    } else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_JS) {

                        // JS Result

                        // Protocol
                        // [Header]
                        // [Error, ScriptResponse]

                        // Get pipe id
                        if (packet_id < WEBUI_MAX_IDS) {

                            if (_webui.run_userBuffer[packet_id] != NULL) {

                                _webui_mutex_lock(&_webui.mutex_js_run);
                                _webui.run_done[packet_id] = false;
                                _webui_mutex_unlock(&_webui.mutex_js_run);

                                // Get js-error
                                bool error = true;
                                if ((unsigned char)packet[WEBUI_PROTOCOL_DATA] == 0x00)
                                    error = false;

                                // Get data part
                                char* data = (char*)&packet[WEBUI_PROTOCOL_DATA + 1];
                                size_t data_len = _webui_strlen(data);

                                #ifdef WEBUI_LOG
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_JS \n",
                                    recvNum
                                );
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> run_id = 0x%02x (%u) \n",
                                    recvNum, packet_id, packet_id
                                );
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> error = %s \n",
                                    recvNum, error ? "true" : "false"
                                );
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> %zu bytes of data\n",
                                    recvNum, data_len
                                );
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> data = [%s] @ 0x%p\n",
                                    recvNum, data, data
                                );
                                #endif

                                // Set pipe
                                _webui.run_error[packet_id] = error;
                                if (data_len > 0) {

                                    // Copy response to the user's response buffer
                                    // directly
                                    size_t response_len = data_len + 1;
                                    size_t bytes_to_cpy =
                                        (response_len <=
                                            _webui
                                            .run_userBufferLen[packet_id] ?
                                            response_len :
                                            _webui
                                            .run_userBufferLen[packet_id]);
                                    memcpy(
                                        _webui.run_userBuffer[packet_id], data,
                                        bytes_to_cpy
                                    );
                                } else {

                                    // Empty Result
                                    _webui.run_userBuffer[packet_id] = 0x00;
                                }

                                // Send ready signal to webui_script()
                                _webui_mutex_lock(&_webui.mutex_js_run);
                                _webui.run_done[packet_id] = true;
                                _webui_mutex_unlock(&_webui.mutex_js_run);
                            }
                        }
                    } else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_NAVIGATION) {

                        // Navigation Event

                        // Protocol
                        // [Header]
                        // [URL]

                        // Events
                        if (win->has_all_events) {

                            // Get URL
                            char* url = (char*)&packet[WEBUI_PROTOCOL_DATA];
                            size_t url_len = _webui_strlen(url);

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_NAVIGATION \n",
                                recvNum
                            );
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> URL size: %zu bytes \n",
                                recvNum, url_len
                            );
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> URL: [%s] \n",
                                recvNum, url
                            );
                            #endif

                            // New event inf (Navigation)
                            webui_event_inf_t* event_inf = NULL;
                            size_t event_num = _webui_new_event_inf(win, &event_inf);

                            // Set arguments
                            event_inf->event_data[0] = url;
                            event_inf->event_size[0] = url_len;

                            _webui_window_event(
                                win, // Event -> Window
                                connection_id, // Event -> Client Unique ID
                                WEBUI_EVENT_NAVIGATION, // Event -> Type of this event
                                "", // Event -> HTML Element
                                event_num, // Event -> Event Number
                                _webui_client_get_id(win, client), // Event -> Client ID
                                _webui_get_cookies_full(client) // Event -> Full cookies
                            );

                            // Free event
                            _webui_free_event_inf(win, event_num);
                        }
                    } else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CALL_FUNC) {

                        // Function Call

                        // Protocol
                        // [Header]
                        // [Fn, Null, {Len;Len;...}, Null, {Data,Null,Data,Null...}]

                        // Get html element id
                        char* element = (char*)&packet[WEBUI_PROTOCOL_DATA];
                        size_t element_len = _webui_strlen(element);

                        #ifdef WEBUI_LOG
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CALL_FUNC \n",
                            recvNum
                        );
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> Call ID: [%u] \n",
                            recvNum, packet_id
                        );
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> Element: [%s] \n",
                            recvNum, element
                        );
                        #endif

                        // New event inf (Function Call)
                        webui_event_inf_t* event_inf = NULL;
                        size_t event_num = _webui_new_event_inf(win, &event_inf);

                        // Loop trough args
                        size_t data_size_expected = 0;
                        char* args_lens = (char*)&packet[WEBUI_PROTOCOL_DATA + element_len + 1];
                        size_t args_len = _webui_strlen(args_lens);
                        char* args_ptr = (char*)&packet[WEBUI_PROTOCOL_DATA + element_len + 1 + args_len + 1];
                        char* context;
                        char* tk = WEBUI_STR_TOK(args_lens, ";", &context);
                        size_t tk_num = 0;
                        while(tk != NULL && tk_num < WEBUI_MAX_ARG + 1) {

                            size_t arg_len = (size_t) strtoul(tk, NULL, 10);
                            data_size_expected = data_size_expected + arg_len + 1;

                            if (arg_len > 0) {

                                // Set argument
                                event_inf->event_size[tk_num] = arg_len;
                                event_inf->event_data[tk_num] = args_ptr;
                            }

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> Argument %zu: %zu bytes\n",
                                recvNum, tk_num, arg_len
                            );
                            #endif

                            args_ptr = args_ptr + (arg_len + 1);
                            tk = WEBUI_STR_TOK(NULL, ";", &context);

                            // Save total found arguments
                            event_inf->count = ++tk_num;
                        }

                        // Check data validity
                        size_t data_size_recv = len - (WEBUI_PROTOCOL_SIZE + // [Header]
                            element_len + // [Fn]
                            1 + // [Null]
                            args_len + // [{Len;Len;...}]
                            1); // [Null]

                        if (data_size_expected == data_size_recv) {

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> Expected and received %zu bytes of data.\n",
                                recvNum, data_size_expected
                            );
                            #endif

                            // Create new event (Function Call)
                            webui_event_t e;
                            e.window = win->num;
                            e.event_type = WEBUI_EVENT_CALLBACK;
                            e.element = element;
                            e.event_number = event_num;
                            e.connection_id = connection_id;
                            e.client_id = _webui_client_get_id(win, client);
                            e.cookies = (char*)_webui_get_cookies_full(client); // Full cookies

                            // Call user function
                            size_t cb_index = 0;
                            bool exist = _webui_get_cb_index(win, element, &cb_index);
                            if (exist && win->cb[cb_index] != NULL) {

                                // Call user cb
                                #ifdef WEBUI_LOG
                                printf(
                                    "[Core]\t\t_webui_ws_process(%zu) -> Calling user callback\n[Call]\n",
                                    recvNum
                                );
                                #endif
                                e.bind_id = cb_index;
                                win->cb[cb_index](&e);
                            }

                            // Check the response
                            if (_webui_is_empty(event_inf->response))
                                event_inf->response = NULL;

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> user-callback response [%s]\n",
                                recvNum, event_inf->response
                            );
                            #endif

                            // Packet Protocol Format:
                            // [...]
                            // [CMD]
                            // [CallResponse]

                            // Send the packet
                            _webui_send_client(
                                win, client, packet_id, WEBUI_CMD_CALL_FUNC,
                                event_inf->response, _webui_strlen(event_inf->response), false
                            );

                            // Free event
                            _webui_free_event_inf(win, event_num);
                        } else {

                            // WebSocket/Civetweb did not send all the data as expected.
                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> No enough data received. "
                                "Expected %zu bytes, received %zu bytes.\n",
                                recvNum, data_size_expected, data_size_recv
                            );
                            #endif

                            // Send a void response to solve `.call()` promise

                            // Packet Protocol Format:
                            // [...]
                            // [CMD]
                            // [CallResponse]

                            // Send the packet
                            _webui_send_client(
                                win, client, packet_id, WEBUI_CMD_CALL_FUNC, NULL, 0, false
                            );
                        }

                        // Free event
                        _webui_free_mem((void*)event_inf);
                    } else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CHECK_TK) {

                        // Check Token Event

                        // Protocol
                        // [Header]

                        #ifdef WEBUI_LOG
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CHECK_TK \n",
                            recvNum
                        );
                        #endif

                        size_t connection_id = 0;
                        if (_webui_connection_get_id(win, client, &connection_id)) {

                            if (win->single_client == client) {
                                win->single_client_token_check = true;
                            }
                            _webui.clients_token_check[connection_id] = true;

                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> Token accepted. Sending bind list\n",
                                recvNum
                            );
                            #endif

                            // Calculate the bind list size
                            // [0x01][element1,element2,element3,]
                            size_t csv_size = 1;
                            for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
                                if (!_webui_is_empty(win->html_elements[i])) {
                                    csv_size += _webui_strlen(win->html_elements[i]) + 1;
                                }
                            }

                            // Allocate
                            char* csv = (char*)_webui_malloc(csv_size);
                            csv[0] = 0x01;

                            // Generate the bind list array (CSV)
                            for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
                                if (!_webui_is_empty(win->html_elements[i])) {
                                    // [element1,element2,element3,]
                                    WEBUI_STR_CAT_DYN(&csv[1], csv_size, win->html_elements[i]);
                                    WEBUI_STR_CAT_DYN(&csv[1], csv_size, ",");
                                }
                            }

                            // Add all events bind element (empty)
                            if (win->has_all_events) {
                                // [element1,...,,]
                                WEBUI_STR_CAT_DYN(&csv[1], csv_size, ",");
                            }

                            // Packet Protocol Format:
                            // [...]
                            // [CMD]
                            // [Token Status]
                            // [0x01 element1,element2,element3,]

                            // Send the packet
                            _webui_send_client(
                                win, client, packet_id, WEBUI_CMD_CHECK_TK, 
                                (const char*)csv, _webui_strlen(csv), true
                            );

                            // Free
                            _webui_free_mem((void*)csv);                            

                            // New Event
                            if (win->has_all_events) {

                                // New event inf (Connected)
                                // webui_event_inf_t* event_inf = NULL;
                                // size_t event_num = _webui_new_event_inf(win, &event_inf);

                                // Set arguments
                                // ...

                                _webui_window_event(
                                    win, // Event -> Window
                                    connection_id, // Event -> Client Unique ID
                                    WEBUI_EVENT_CONNECTED, // Event -> Type of this event
                                    "", // Event -> HTML Element
                                    0, // Event -> Event Number
                                    _webui_client_get_id(win, client), // Event -> Client ID
                                    _webui_get_cookies_full(client) // Event -> Full cookies
                                );

                                // Free event
                                // _webui_free_event_inf(win, event_num);
                            }
                        }
                        else {
                            #ifdef WEBUI_LOG
                            printf(
                                "[Core]\t\t_webui_ws_process(%zu) -> Token not accepted.\n",
                                recvNum
                            );
                            #endif

                            unsigned char status = 0x00;

                            // Packet Protocol Format:
                            // [...]
                            // [CMD]
                            // [Token Status]
                            // [0x00]

                            // Send the packet
                            _webui_send_client(
                                win, client, packet_id, WEBUI_CMD_CHECK_TK, 
                                (const char*)&status, 1, true
                            );
                        }
                    }
                    #ifdef WEBUI_LOG
                    else {
                        printf(
                            "[Core]\t\t_webui_ws_process(%zu) -> Unknown command "
                            "[0x%02x]\n",
                            recvNum, (unsigned char)packet[WEBUI_PROTOCOL_CMD]
                        );
                    }
                    #endif
                }
                #ifdef WEBUI_LOG
                else {
                    printf(
                        "[Core]\t\t_webui_ws_process(%zu) -> Window is not connected.\n",
                        recvNum
                    );
                }
                #endif

                // Unlock Mutex
                if (_webui.config.ws_block) {
                    if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] != WEBUI_CMD_JS) {
                        _webui_mutex_unlock(&_webui.mutex_receive);
                    }
                }
            } else {

                #ifdef WEBUI_LOG
                printf(
                    "[Core]\t\t_webui_ws_process(%zu) -> Invalid Packet.\n",
                    recvNum
                );
                #endif

                // Send a void response to solve `.call()` promise

                // Packet Protocol Format:
                // [...]
                // [CMD]

                // Send the packet
                _webui_send_client(
                    win, client, packet_id, (unsigned char)packet[WEBUI_PROTOCOL_CMD], NULL, 0, true
                );

                // Forced close
                _webui_connection_remove(win, client);
            }
        } else if (event_type == WEBUI_WS_OPEN) {

            // New connection

            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_WS_OPEN \n",
                recvNum
            );
            #endif

            /*
            // New Event
            if (win->has_all_events) {

                // New event inf (Connected)
                // webui_event_inf_t* event_inf = NULL;
                // size_t event_num = _webui_new_event_inf(win, &event_inf);

                // Set arguments
                // ...

                _webui_window_event(
                    win, // Event -> Window
                    connection_id, // Event -> Client Unique ID
                    WEBUI_EVENT_CONNECTED, // Event -> Type of this event
                    "", // Event -> HTML Element
                    0, // Event -> Event Number
                    _webui_client_get_id(win, client), // Event -> Client ID
                    _webui_get_cookies_full(client) // Event -> Full cookies
                );

                // Free event
                // _webui_free_event_inf(win, event_num);
            }
            */
        } else if (event_type == WEBUI_WS_CLOSE) {

            // Connection close

            #ifdef WEBUI_LOG
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> WEBUI_WS_CLOSE \n",
                recvNum
            );
            #endif

            // Events
            if (win->has_all_events) {

                // New event inf (Disconnected)
                // webui_event_inf_t* event_inf = NULL;
                // size_t event_num = _webui_new_event_inf(win, &event_inf);

                // Set arguments
                // ...

                _webui_window_event(
                    win, // Event -> Window
                    connection_id, // Event -> Client Unique ID
                    WEBUI_EVENT_DISCONNECTED, // Event -> Type of this event
                    "", // Event -> HTML Element
                    0, // Event -> Event Number
                    _webui_client_get_id(win, client), // Event -> Client ID
                    _webui_get_cookies_full(client) // Event -> Full cookies
                );

                // Free event
                // _webui_free_event_inf(win, event_num);
            }
        }
        #ifdef WEBUI_LOG
        else {
            printf(
                "[Core]\t\t_webui_ws_process(%zu) -> UNKNOWN EVENT "
                "TYPE (%d)\n",
                recvNum, event_type
            );
        }
        #endif

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_ws_process(%zu) -> Finished.\n", recvNum);
        #endif
    }
}

static WEBUI_THREAD_RECEIVE {
    #ifdef WEBUI_LOG
    printf("[Core]\t\t[Thread .] _webui_ws_process_thread()\n");
    #endif

    // Get arguments
    _webui_recv_arg_t* arg = (_webui_recv_arg_t* ) _arg;

    // Process
    _webui_ws_process(arg->win, arg->client, arg->connection_id, arg->ptr, arg->len, arg->recvNum, arg->event_type);

    // Free
    _webui_free_mem((void*)arg->ptr);
    _webui_free_mem((void*)arg);

    WEBUI_THREAD_RETURN
}

#ifdef _WIN32
static void _webui_kill_pid(size_t pid) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_kill_pid(%zu)\n", pid);
    #endif

    if (pid < 1)
        return;
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid);
    if (hProcess != NULL) {
        TerminateProcess(hProcess, 1);
        CloseHandle(hProcess);
    }
}
#else
static void _webui_kill_pid(size_t pid) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_kill_pid(%zu)\n", pid);
    #endif

    if (pid < 1)
        return;
    kill((pid_t) pid, SIGTERM);
}
#endif

#ifdef _WIN32

static bool _webui_str_to_wide(const char *s, wchar_t **w) {
    int wlen = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
    if (wlen < 1)
        return false;
    wchar_t *wide = (wchar_t *)_webui_malloc(wlen * sizeof(wchar_t));
    if (!wide)
        return false;
    MultiByteToWideChar(CP_UTF8, 0, s, -1, wide, wlen);
    *w = wide;
    return true;
}

static bool _webui_socket_test_listen_win32(size_t port_num) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_socket_test_listen_win32([%zu])\n", port_num);
    #endif

    WSADATA wsaData;
    size_t iResult;
    SOCKET ListenSocket = INVALID_SOCKET;
    struct addrinfo * result = NULL;
    struct addrinfo hints;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2),&wsaData);
    if (iResult != 0) {
        // WSAStartup failed
        return false;
    }
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    char the_port[16] = {0};
    WEBUI_SN_PRINTF_STATIC(&the_port[0], sizeof(the_port), "%zu", port_num);
    iResult = getaddrinfo("127.0.0.1",&the_port[0],&hints,&result);
    if (iResult != 0) {
        // WSACleanup();
        return false;
    }

    // Create a SOCKET for the server to listen for client connections.
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        freeaddrinfo(result);
        // WSACleanup();
        return false;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int) result->ai_addrlen);
    if (iResult == (size_t)SOCKET_ERROR) {
        freeaddrinfo(result);
        closesocket(ListenSocket);
        shutdown(ListenSocket, SD_BOTH);
        // WSACleanup();
        return false;
    }

    // Clean
    freeaddrinfo(result);
    closesocket(ListenSocket);
    shutdown(ListenSocket, SD_BOTH);
    // WSACleanup();

    // Listening Success
    return true;
}

static int _webui_system_win32_out(const char* cmd, char ** output, bool show) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_system_win32_out()\n");
    #endif

        // Ini
        *
        output = NULL;
    if (cmd == NULL)
        return -1;

    // Return
    DWORD Return = 0;

    // Flags
    DWORD CreationFlags = CREATE_NO_WINDOW;
    if (show)
        CreationFlags = SW_SHOW;

    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;
    HANDLE stdout_read, stdout_write;
    if (!CreatePipe(&stdout_read,&stdout_write,&sa, 0)) {
        return -1;
    }
    if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
        CloseHandle(stdout_read);
        CloseHandle(stdout_write);
        return -1;
    }

    STARTUPINFOA si;
    ZeroMemory(&si, sizeof(STARTUPINFOA));
    si.cb = sizeof(STARTUPINFOA);
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdOutput = stdout_write;
    si.hStdError = stdout_write;

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

    if (!CreateProcessA(
            NULL, // No module name (use cmd line)
            (LPSTR) cmd, // Command line
            NULL, // Process handle not inheritable
            NULL, // Thread handle not inheritable
            TRUE, // Set handle inheritance to FALSE
            CreationFlags, // Creation flags
            NULL, // Use parent's environment block
            NULL, // Use parent's starting directory
            &
            si, // Pointer to STARTUP INFO structure
            &
            pi
        )) // Pointer to PROCESS_INFORMATION structure
    {
        CloseHandle(stdout_read);
        CloseHandle(stdout_write);
        return -1;
    }
    CloseHandle(stdout_write);

    SetFocus(pi.hProcess);
    WaitForSingleObject(pi.hProcess, INFINITE);
    GetExitCodeProcess(pi.hProcess,&Return);

    DWORD bytes_read;
    char buffer[WEBUI_STDOUT_BUF];
    size_t output_size = 0;

    while(ReadFile(stdout_read, buffer, WEBUI_STDOUT_BUF,&bytes_read, NULL) && bytes_read > 0) {

        char* new_output = realloc(*output, output_size + bytes_read + 1);
        if (new_output == NULL) {
            free(*output);
            CloseHandle(stdout_read);
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
            return -1;
        }

        * output = new_output;
        memcpy(*output + output_size, buffer, bytes_read);
        output_size += bytes_read;
    }

    if (*output != NULL)
        (*output)[output_size] = '\0';

    CloseHandle(stdout_read);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    if (Return == 0)
        return 0;
    else
        return -1;
}

/*
    static BOOL CALLBACK _webui_enum_windows_proc_win32(HWND hwnd, LPARAM
    targetProcessId) {

        #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_enum_windows_proc_win32()\n");
        #endif

        DWORD windowProcessId;
        GetWindowThreadProcessId(hwnd, &windowProcessId);

        if (windowProcessId == targetProcessId) {

            #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_enum_windows_proc_win32() -> Bring the"
                    "process (%lu) to the front\n", windowProcessId);
            #endif

            SetFocus(hwnd);
            SetForegroundWindow(hwnd);
            return FALSE;
        }

        return TRUE;
    }
*/

static int _webui_system_win32(_webui_window_t* win, char* cmd, bool show) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_system_win32()\n");
    #endif

    // Convert UTF-8 to wide string
    wchar_t* wcmd;
    if (!_webui_str_to_wide(cmd, &wcmd))
        return -1;

    /*
    We should not kill this process, because may had many child
    process of other WebUI app instances. Unfortunately, this is
    how modern browsers save memory by combine all windows into one
    single parent process, and we can't control this behavior.

    // Automatically close the browser process when the
    // parent process (this app) get closed. If this fail
    // webui.js will try to close the window.
    HANDLE JobObject = CreateJobObject(NULL, NULL);
    JOB_OBJECT_EXTENDED_LIMIT_INFORMATION ExtendedInfo = { 0 };
    ExtendedInfo.BasicLimitInformation.LimitFlags =
    JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
    JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;SetInformationJobObject(JobObject,
    JobObjectExtendedLimitInformation, &ExtendedInfo, sizeof(ExtendedInfo));
    */

    DWORD Return = 0;
    DWORD CreationFlags = CREATE_NO_WINDOW;

    if (show)
        CreationFlags = SW_SHOW;

    STARTUPINFOW si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    if (!CreateProcessW(
            NULL, // No module name (use command line)
            wcmd, // Command line
            NULL, // Process handle not inheritable
            NULL, // Thread handle not inheritable
            FALSE, // Set handle inheritance to FALSE
            CreationFlags, // Creation flags
            NULL, // Use parent's environment block
            NULL, // Use parent's starting directory
            &
            si, // Pointer to STARTUP INFO structure
            &
            pi
        )) // Pointer to PROCESS_INFORMATION structure
    {
        // CreateProcess failed
        _webui_free_mem((void*)wcmd);
        return -1;
    }

    if (win) {
        win->process_id = (size_t)pi.dwProcessId;
    }
    
    SetFocus(pi.hProcess);
    // EnumWindows(_webui_enum_windows_proc_win32, (LPARAM)(pi.dwProcessId));
    // AssignProcessToJobObject(JobObject, pi.hProcess);
    WaitForSingleObject(pi.hProcess, INFINITE);
    GetExitCodeProcess(pi.hProcess,&Return);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    _webui_free_mem((void*)wcmd);

    if (Return == 0)
        return 0;
    else
        return -1;
}

static bool _webui_get_windows_reg_value(HKEY key, LPCWSTR reg, LPCWSTR value_name, char value[WEBUI_MAX_PATH]) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_get_windows_reg_value([%ls])\n", reg);
    #endif

    HKEY hKey;

    if (RegOpenKeyExW(key, reg, 0, KEY_READ,&hKey) == ERROR_SUCCESS) {

        DWORD VALUE_TYPE;
        BYTE VALUE_DATA[WEBUI_MAX_PATH];
        DWORD VALUE_SIZE = sizeof(VALUE_DATA);

        // If `value_name` is empty then it will read the "(default)" reg-key
        if (RegQueryValueExW(hKey, value_name, NULL,&VALUE_TYPE, VALUE_DATA,&VALUE_SIZE) == ERROR_SUCCESS) {

            if (VALUE_TYPE == REG_SZ)
                WEBUI_SN_PRINTF_STATIC(value, WEBUI_MAX_PATH, "%S", (LPCWSTR) VALUE_DATA);
            else if (VALUE_TYPE == REG_DWORD)
                WEBUI_SN_PRINTF_STATIC(value, WEBUI_MAX_PATH, "%lu", *((DWORD * ) VALUE_DATA));

            RegCloseKey(hKey);
            return true;
        }
    }

    return false;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
    (void)hinstDLL;
    (void)fdwReason;
    (void)lpReserved;
    return true;
}

#endif

// -- WebView -------------------------

#ifdef _WIN32
    // Microsoft Windows

    typedef HRESULT (__stdcall *CreateCoreWebView2EnvironmentWithOptionsFunc)(
        PCWSTR browserExecutableFolder, PCWSTR userDataFolder, ICoreWebView2EnvironmentOptions* environmentOptions,
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environment_created_handler
    );

    typedef struct {
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl* lpVtbl;
        ULONG refCount;
        _webui_wv_win32_t* webView;
    } CreateWebViewEnvironmentHandler;

    typedef struct {
        ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl* lpVtbl;
        ULONG refCount;
        _webui_wv_win32_t* webView;
    } CreateWebViewControllerHandler;

    typedef struct {
        ICoreWebView2DocumentTitleChangedEventHandlerVtbl* lpVtbl;
        ULONG refCount;
        _webui_wv_win32_t* webView;
    } TitleChangedHandler;

    HRESULT STDMETHODCALLTYPE CreateWebViewEnvironmentHandler_Invoke(
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, 
        HRESULT result, 
        ICoreWebView2Environment* env
    );

    HRESULT STDMETHODCALLTYPE CreateWebViewControllerHandler_Invoke(
        ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, 
        HRESULT result, 
        ICoreWebView2Controller* controller
    );

    HRESULT STDMETHODCALLTYPE QueryInterfaceEnvironment(
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, 
        REFIID riid, 
        void** ppvObject
    );

    HRESULT STDMETHODCALLTYPE QueryInterfaceController(
        ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, 
        REFIID riid, 
        void** ppvObject
    );

    ULONG STDMETHODCALLTYPE AddRefEnvironment(
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This
    );

    ULONG STDMETHODCALLTYPE AddRefController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This);
    ULONG STDMETHODCALLTYPE ReleaseEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This);
    ULONG STDMETHODCALLTYPE ReleaseController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This);

    HRESULT STDMETHODCALLTYPE TitleChanged_Invoke(
        ICoreWebView2DocumentTitleChangedEventHandler* This, ICoreWebView2* sender, IUnknown* args) {
        (void)args;
        TitleChangedHandler* handler = (TitleChangedHandler*)This;
        _webui_wv_win32_t* webView = handler->webView;
        LPWSTR newTitle = NULL;
        sender->lpVtbl->get_DocumentTitle(sender, &newTitle);
        SetWindowTextW(webView->hwnd, newTitle);
        CoTaskMemFree(newTitle);
        return S_OK;
    };

    ULONG STDMETHODCALLTYPE TitleChanged_AddRef(ICoreWebView2DocumentTitleChangedEventHandler* This) {
        TitleChangedHandler* handler = (TitleChangedHandler*)This;
        return ++handler->refCount;
    };

    ULONG STDMETHODCALLTYPE TitleChanged_Release(ICoreWebView2DocumentTitleChangedEventHandler* This) {
        TitleChangedHandler* handler = (TitleChangedHandler*)This;
        if (--handler->refCount == 0) {
            _webui_free_mem((void*) handler->lpVtbl);
            _webui_free_mem((void*) handler);
            return 0;
        }
        return handler->refCount;
    };

    TitleChangedHandler* CreateTitleChangedHandler(_webui_wv_win32_t* webView) {
        TitleChangedHandler* handler = _webui_malloc(sizeof(TitleChangedHandler));
        handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2DocumentTitleChangedEventHandlerVtbl));
        handler->lpVtbl->Invoke = TitleChanged_Invoke;
        handler->lpVtbl->AddRef = TitleChanged_AddRef;
        handler->lpVtbl->Release = TitleChanged_Release;
        handler->refCount = 1;
        handler->webView = webView;

        // Save pointers to be freed by `_webui_wv_free()`
        webView->titleChangedHandler = handler;
        webView->titleChangedHandler_lpVtbl = handler->lpVtbl;
        return handler;
    };

    CreateWebViewEnvironmentHandler* CreateEnvironmentHandler(_webui_wv_win32_t* webView) {
        CreateWebViewEnvironmentHandler* handler = _webui_malloc(sizeof(CreateWebViewEnvironmentHandler));
        if (!handler) return NULL;
        handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
        if (!handler->lpVtbl) {
            _webui_free_mem((void*) handler);
            return NULL;
        }
        handler->lpVtbl->QueryInterface = QueryInterfaceEnvironment;
        handler->lpVtbl->AddRef = AddRefEnvironment;
        handler->lpVtbl->Release = ReleaseEnvironment;
        handler->lpVtbl->Invoke = CreateWebViewEnvironmentHandler_Invoke;
        handler->refCount = 1;
        handler->webView = webView;

        // Save pointers to be freed by `_webui_wv_free()`
        webView->createWebViewEnvironmentHandler = handler;
        webView->createWebViewEnvironmentHandler_lpVtbl = handler->lpVtbl;
        return handler;
    };

    CreateWebViewControllerHandler* CreateControllerHandler(_webui_wv_win32_t* webView) {
        CreateWebViewControllerHandler* handler = _webui_malloc(sizeof(CreateWebViewControllerHandler));
        if (!handler) return NULL;
        handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
        if (!handler->lpVtbl) {
            _webui_free_mem((void*) handler);
            return NULL;
        }
        handler->lpVtbl->QueryInterface = QueryInterfaceController;
        handler->lpVtbl->AddRef = AddRefController;
        handler->lpVtbl->Release = ReleaseController;
        handler->lpVtbl->Invoke = CreateWebViewControllerHandler_Invoke;
        handler->refCount = 1;
        handler->webView = webView;

        // Save pointers to be freed by `_webui_wv_free()`
        webView->createWebViewControllerHandler = handler;
        webView->createWebViewControllerHandler_lpVtbl = handler->lpVtbl;
        return handler;
    };

    HRESULT STDMETHODCALLTYPE CreateWebViewEnvironmentHandler_Invoke(
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, HRESULT result, 
        ICoreWebView2Environment* env
    ) {
        CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
        _webui_wv_win32_t* webView = handler->webView;
        if (SUCCEEDED(result)) {
            CreateWebViewControllerHandler* controllerHandler = CreateControllerHandler(webView);
            if (controllerHandler) {
                env->lpVtbl->CreateCoreWebView2Controller(env, webView->hwnd, 
                (ICoreWebView2CreateCoreWebView2ControllerCompletedHandler*)controllerHandler);
            }
        }
        return S_OK;
    };

    HRESULT STDMETHODCALLTYPE CreateWebViewControllerHandler_Invoke(
        ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, HRESULT result, 
        ICoreWebView2Controller* controller) {
        CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
        _webui_wv_win32_t* webView = handler->webView;
        if (SUCCEEDED(result) && controller != NULL) {
            webView->webviewController = controller;
            webView->webviewController->lpVtbl->get_CoreWebView2(webView->webviewController, 
            &webView->webviewWindow);
            webView->webviewController->lpVtbl->AddRef(webView->webviewController);
            ICoreWebView2Settings* settings;
            webView->webviewWindow->lpVtbl->get_Settings(webView->webviewWindow, &settings);
            settings->lpVtbl->put_IsScriptEnabled(settings, TRUE);
            settings->lpVtbl->put_AreDefaultScriptDialogsEnabled(settings, TRUE);
            settings->lpVtbl->put_IsWebMessageEnabled(settings, TRUE);
            RECT bounds = {0, 0, webView->width, webView->height};
            webView->webviewController->lpVtbl->put_Bounds(webView->webviewController, bounds);
            TitleChangedHandler* titleChangedHandler = CreateTitleChangedHandler(webView);
            EventRegistrationToken tk;
            webView->webviewWindow->lpVtbl->add_DocumentTitleChanged(webView->webviewWindow, 
            (ICoreWebView2DocumentTitleChangedEventHandler*)titleChangedHandler, &tk);
            webView->webviewWindow->lpVtbl->Navigate(webView->webviewWindow, webView->url);
            // Microsoft WebView2 Auto JS Inject
            if (_webui.config.show_auto_js_inject) {
                // HRESULT AutoInject = webView->webviewWindow->lpVtbl->AddScriptToExecuteOnDocumentCreated(
                //     webView->webviewWindow, L"var script = document.createElement('script');"
                //     "script.src = 'webui.js';document.head.appendChild(script);", 
                //     NULL
                // );
                // if (FAILED(AutoInject)) {
                //     #ifdef WEBUI_LOG
                //     printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Auto Inject creation failed\n");
                //     #endif
                // }
                // else {
                //     #ifdef WEBUI_LOG
                //     printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Auto Inject creation succeeds\n");
                //     #endif
                // }
            }
        } else return S_FALSE;
        return S_OK;
    };

    HRESULT STDMETHODCALLTYPE QueryInterfaceEnvironment(
        ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, REFIID riid, void** ppvObject) {
        (void)This;
        (void)riid;
        *ppvObject = NULL;
        return E_NOINTERFACE;
    };

    HRESULT STDMETHODCALLTYPE QueryInterfaceController(
        ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, REFIID riid, void** ppvObject) {
        (void)This;
        (void)riid;
        *ppvObject = NULL;
        return E_NOINTERFACE;
    };

    ULONG STDMETHODCALLTYPE AddRefEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This) {
        CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
        return ++handler->refCount;
    };

    ULONG STDMETHODCALLTYPE AddRefController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This) {
        CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
        return ++handler->refCount;
    };

    ULONG STDMETHODCALLTYPE ReleaseEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This) {
        CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
        if (--handler->refCount == 0) {
            _webui_free_mem((void*) handler->lpVtbl);
            _webui_free_mem((void*) handler);
            return 0;
        }
        return handler->refCount;
    };

    ULONG STDMETHODCALLTYPE ReleaseController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This) {
        CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
        if (--handler->refCount == 0) {
            _webui_free_mem((void*) handler->lpVtbl);
            _webui_free_mem((void*) handler);
            return 0;
        }
        return handler->refCount;
    };

    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        void* ptr = (void*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        _webui_window_t* win = _webui_dereference_win_ptr(ptr);

        switch (uMsg) {
            case WM_SIZE:
                if (win) {
                    if (win->webView && win->webView->webviewController) {
                        RECT bounds;
                        GetClientRect(hwnd, &bounds);
                        win->webView->webviewController->lpVtbl->put_Bounds(win->webView->webviewController, bounds);
                    }
                }
                break;
            case WM_GETMINMAXINFO:
            	if (win) {
					if (win->minimum_size_set) {
						LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
						lpMMI->ptMinTrackSize.x = win->minimum_width;
						lpMMI->ptMinTrackSize.y = win->minimum_height;
					}
				}
				break;
            case WM_CLOSE:
                if (win) {
                    // Stop the WebView thread, close the window
                    // and free resources.
                    if (win->webView) {
                        win->webView->stop = true;
                        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
                    }                    
                    _webui_wv_event_closed(win);
                }
                break;
            case WM_DESTROY:
                if (win) {
                    // Destroy message will be
                    // sent by `webui_wait()`
                    // Nothing to do here.
                }
                break;
            default:
                return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        return 0;
    };

    // Close Event
    static void _webui_wv_event_closed(_webui_window_t* win) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_event_closed([%zu])\n", win->num);
        #endif
        win->is_closed = true;
    }

    static bool _webui_wv_show(_webui_window_t* win, char* url) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show([%s])\n", url);
        #endif

        // Microsoft Windows WebView2

        // Free old WebView
        if (win->webView) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
        }

        // Get wide URL
        wchar_t* wURL = NULL;
        _webui_str_to_wide(url, &wURL);

        // Initializing the Win32 WebView struct
        _webui_wv_win32_t* webView = (_webui_wv_win32_t*) _webui_malloc(sizeof(_webui_wv_win32_t));
        webView->url = wURL;
        webView->width = (win->width > 0 ? win->width : 800);
        webView->height = (win->height > 0 ? win->height : 600);
        webView->x = (win->x > 0 ? win->x : (int)((GetSystemMetrics(SM_CXSCREEN) - 800) / 2));
        webView->y = (win->y > 0 ? win->y : (int)((GetSystemMetrics(SM_CYSCREEN) - 600) / 2));
        win->webView = webView;

        // Note: To garantee all Microsoft WebView's operations ownership we should
        // process all the WebView's operations in one single thread for each window.

        // Initializing
        // Expecting `_webui_webview_thread` to change `mutex_is_webview_update` 
        // to `false` when initialization is done, and `_webui.is_webview`
        // to `true` if loading the WebView is succeeded.
        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);

        // Win32 WebView thread
        #ifdef _WIN32
        HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
        if (thread != NULL)
            CloseHandle(thread);
        #else
        pthread_t thread;
        pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
        pthread_detach(thread);
        #endif
        
        // Wait for WebView thread to start
        _webui_timer_t timer;
        _webui_timer_start(&timer);
        for (;;) {
            _webui_sleep(10);
            if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
                // WebView thread just started
                // and loaded WebView successfully
                break;
            }
            if (_webui_timer_is_end(&timer, 2500)) {
                // Timeout. WebView thread failed.
                break;
            }
        }

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show() -> Return [%d]\n", (_webui.is_webview == true));
        #endif

        return (_webui.is_webview);
    };

    static bool _webui_wv_set_size(_webui_wv_win32_t* webView, int windowWidth, int windowHeight) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
        #endif
        // if (webView && webView->webviewController) {
        //     RECT bounds = {0, 0, windowWidth, windowHeight};
        //     HRESULT hr = webView->webviewController->lpVtbl->put_Bounds(webView->webviewController, bounds);
        //     return SUCCEEDED(hr);
        // }
        if (webView) {
            return (SetWindowPos(webView->hwnd, NULL, 0, 0, windowWidth, windowHeight, SWP_NOMOVE| SWP_NOREPOSITION));
        }
        return false;
    };

    static bool _webui_wv_set_position(_webui_wv_win32_t* webView, int x, int y) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
        #endif
        if (webView && webView->webviewController) {
            RECT bounds;
            webView->webviewController->lpVtbl->get_Bounds(webView->webviewController, &bounds);
            HRESULT hr = MoveWindow(webView->hwnd, x, y, bounds.right - bounds.left, bounds.bottom - bounds.top, TRUE);
            return hr != 0;
        }
        return false;
    };

    static bool _webui_wv_navigate(_webui_wv_win32_t* webView, wchar_t* url) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_navigate([%ls])\n", url);
        #endif
        if (webView && webView->webviewWindow) {
            HRESULT hr = webView->webviewWindow->lpVtbl->Navigate(webView->webviewWindow, url);
            return SUCCEEDED(hr);
        }
        return false;
    };

    static void _webui_wv_free(_webui_wv_win32_t* webView) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_free()\n");
        #endif
        if (webView != NULL) {
            if (webView->webviewWindow) {
                webView->webviewWindow->lpVtbl->Release(webView->webviewWindow);
            }
            if (webView->webviewController) {
                webView->webviewController->lpVtbl->Release(webView->webviewController);
            }
            if (webView->webviewEnvironment) {
                webView->webviewEnvironment->lpVtbl->Release(webView->webviewEnvironment);
            }
        }

        _webui_free_mem((void*) webView->titleChangedHandler_lpVtbl);
        _webui_free_mem((void*) webView->titleChangedHandler);
        _webui_free_mem((void*) webView->createWebViewEnvironmentHandler_lpVtbl);
        _webui_free_mem((void*) webView->createWebViewEnvironmentHandler);
        _webui_free_mem((void*) webView->createWebViewControllerHandler_lpVtbl);
        _webui_free_mem((void*) webView->createWebViewControllerHandler);

        _webui_free_mem((void*) webView->url);
        _webui_free_mem((void*) webView);
    };

    static void _webui_wv_close(_webui_wv_win32_t *webView) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_close()\n");
        #endif
        (void)webView;
        // ...
    }

    static WEBUI_THREAD_WEBVIEW {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
        #endif

        _webui_window_t* win = _webui_dereference_win_ptr(arg);
        if (win == NULL) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        // WebView Dynamic Library
        if (!_webui.webviewLib) {
            _webui.webviewLib = LoadLibraryA("WebView2Loader.dll");
            if (!_webui.webviewLib) {
                _webui_wv_free(win->webView);
                win->webView = NULL;
                _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
                WEBUI_THREAD_RETURN
            }
        }

        // Window class
        const char wvClass[] = "WebViewWindow";
        WNDCLASSA wc;
        wc.lpfnWndProc = WndProc;
        wc.hInstance = GetModuleHandle(NULL);
        wc.lpszClassName = wvClass;
        wc.style = 0;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hIcon = LoadIcon(GetModuleHandle(NULL) ,MAKEINTRESOURCE(101));	// default user icon resouce : 101
        if(!wc.hIcon) wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);			// if not existed, use system default icon

        if (!RegisterClassA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        win->webView->hwnd = CreateWindowExA(
            0, wvClass, "", WS_OVERLAPPEDWINDOW,
            win->webView->x, win->webView->y, 
            win->webView->width, win->webView->height,
            NULL, NULL, GetModuleHandle(NULL), NULL
        );

       	{	// window size correction
       		RECT rc;
       		GetClientRect(win->webView->hwnd, &rc);
       		win->webView->width	= rc.right - rc.left;
       		win->webView->height = rc.bottom - rc.top;
       	}

        if (!win->webView->hwnd) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        SetWindowLongPtr(win->webView->hwnd, GWLP_USERDATA, (LONG_PTR)win);
        ShowWindow(win->webView->hwnd, SW_SHOW);
        static CreateCoreWebView2EnvironmentWithOptionsFunc createEnv = NULL;
        createEnv = (CreateCoreWebView2EnvironmentWithOptionsFunc)(void*)GetProcAddress(
            _webui.webviewLib,
            "CreateCoreWebView2EnvironmentWithOptions"
        );

        if (!createEnv) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        CreateWebViewEnvironmentHandler* environmentHandler = CreateEnvironmentHandler(win->webView);
        if (!environmentHandler) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        // Get temp chache folder path
        if (!_webui.webview_cacheFolder) {
            const char* temp = _webui_get_temp_path();
            _webui.webview_cacheFolder = (char*)_webui_malloc(WEBUI_MAX_PATH);
            WEBUI_SN_PRINTF_DYN(_webui.webview_cacheFolder, WEBUI_MAX_PATH, 
                "%s%s.WebUI%sWebUIWebViewCache_%"PRIu32, temp, os_sep, os_sep,
                _webui_generate_random_uint32());
        }

        // Convert chache folder path to wide
        wchar_t* cacheFolderW = NULL;
        _webui_str_to_wide(_webui.webview_cacheFolder, &cacheFolderW);

        HRESULT hr = createEnv(NULL, cacheFolderW, NULL, 
            (ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*)environmentHandler
        );

        if (SUCCEEDED(hr)) {

            // Success
            // Let `wait()` use safe main-thread WebView2 loop
            _webui.is_webview = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

            MSG msg;
            while (true) {

                // Win32 Messages
                if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) > 0) {

                    // Process
                    TranslateMessage(&msg);
                    DispatchMessageW(&msg);

                    // Check if window manually closed
                    if (msg.message == WM_QUIT) {
                        if (win->webView) {
                            DestroyWindow(win->webView->hwnd);
                        }
                        break;
                    }
                }

                if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {

                    _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

                    if (win->webView) {

                        // Stop this thread
                        if (win->webView->stop) {
                            DestroyWindow(win->webView->hwnd);
                            break;
                        }

                        // Window Size
                        if (win->webView->size) {
                            win->webView->size = false;
                            _webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
                        }

                        // Window Position
                        if (win->webView->position) {
                            win->webView->position = false;
                            _webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
                        }

                        // Navigation
                        if (win->webView->navigate) {
                            win->webView->navigate = false;
                            _webui_wv_navigate(win->webView, win->webView->url);
                        }
                    }
                }
            }
        }

        // Clean
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Cleaning\n");
        #endif
        _webui_wv_free(win->webView);
        _webui_free_mem((void*) cacheFolderW);
        win->webView = NULL;

        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
        #endif

        WEBUI_THREAD_RETURN
    };

#elif __linux__
    // Linux

    #define G_CALLBACK(f) ((void (*)(void)) (f))
    #define GTK_RUNTIME_ARR { "libgtk-3.so.0" } // TODO: Add GTK v4 APIs "libgtk-4.so.1"
    #define WEBKIT_RUNTIME_ARR { "libwebkit2gtk-4.1.so.0", "libwebkit2gtk-4.0.so.37" }

    // Title Event
    static void _webui_wv_event_title(void *web_view, void *pspec, void *arg) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_event_title()\n");
        #endif
        _webui_window_t* win = (_webui_window_t *)arg;
        webkit_web_view_get_title_func webkit_web_view_get_title = (
            webkit_web_view_get_title_func)dlsym(libwebkit, "webkit_web_view_get_title");
        const char *title = webkit_web_view_get_title(web_view);
        if (title) {
            gtk_window_set_title_func gtk_window_set_title = (
                gtk_window_set_title_func)dlsym(libgtk, "gtk_window_set_title");
            gtk_window_set_title(win->webView->gtk_win, title);
        }
    }

    // Close Event
    static void _webui_wv_event_closed(void *widget, void *arg) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_event_closed()\n");
        #endif
        _webui_window_t* win = _webui_dereference_win_ptr(arg);
        if (win) {
            win->is_closed = true;
            if (win->webView) {
                win->webView->open = false;
            }
        }
    }

    static bool _webui_wv_set_size(_webui_wv_linux_t* webView, int windowWidth, int windowHeight) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
        #endif
        if (webView) {
            if (webView->gtk_win) {
                gtk_window_resize(webView->gtk_win, webView->width, webView->height);
                return true;
            }
        }
        return false;
    };

    static bool _webui_wv_set_position(_webui_wv_linux_t* webView, int x, int y) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
        #endif
        if (webView) {
            if (webView->gtk_win) {
                // Note:
                // This API does not work under Wayland, and it has been removed
                // in GTK v4, alongside all the APIs that relies on a global 
                // coordinates system. So, `gtk_window_move` may have no effect.
                gtk_window_move(webView->gtk_win, webView->x, webView->y);
                return true;
            }
        }
        return false;
    };

    static bool _webui_wv_navigate(_webui_wv_linux_t* webView, char* url) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_navigate([%s])\n", url);
        #endif
        if (webView) {
            if (webView->gtk_win) {
                webkit_web_view_load_uri(webView->gtk_wv, webView->url);
                return true;
            }
        }
        return false;
    };

    static void _webui_wv_close(_webui_wv_linux_t* webView) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_close()\n");
        #endif

        if (webView) {

            if (webView->open) {

                webView->open = false;

                #ifdef WEBUI_LOG
                printf("[Core]\t\t_webui_wv_close() -> Closing WebView window\n");
                #endif
                gtk_window_close(webView->gtk_win);
            }
        }

        _webui_free_mem((void*) webView->url);
        _webui_free_mem((void*) webView);
    };

    static void _webui_wv_free() {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_free()\n");
        #endif

        if (libwebkit) {

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_wv_free() -> Unload WebKit\n");
            #endif
            dlclose(libwebkit);
        }

        if (libgtk) {

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_wv_free() -> Unload GTK\n");
            #endif
            dlclose(libgtk);
        }

        _webui.is_webview = false;
        libgtk = NULL;
        libwebkit = NULL;
    };

    static void _webui_wv_create(_webui_window_t* win) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_create()\n");
        #endif

        // Initialize GTK Window
        win->webView->gtk_win = gtk_window_new(0);

        // Initialize WebView
        win->webView->gtk_wv = webkit_web_view_new();

        // Window Settings
        gtk_window_set_default_size(win->webView->gtk_win, win->webView->width, win->webView->height);
        gtk_container_add(win->webView->gtk_win, win->webView->gtk_wv);

        // Window position
        // Note: The new positioning system it's not GTK's toolkit job anymore.
        // if ((win->x > 0) && (win->y > 0)) {
        //     gtk_window_move(win->webView->gtk_win, win->webView->x, win->webView->y);
        // }
        // else {
        //     gtk_window_set_position(win->webView->gtk_wv, 1);
        // }

        // Events
        g_signal_connect_data(win->webView->gtk_wv, "notify::title", G_CALLBACK(
            _webui_wv_event_title), (void *)win, NULL, 0);
        g_signal_connect_data(win->webView->gtk_win, "destroy", G_CALLBACK(
            _webui_wv_event_closed), (void *)win, NULL, 0);
        
        // Linux GTK WebView Auto JS Inject
        if (_webui.config.show_auto_js_inject) {
            // ...
        }

        // Show
        webkit_web_view_load_uri(win->webView->gtk_wv, win->webView->url);
        gtk_widget_show_all(win->webView->gtk_win);
        win->webView->open = true;

        // Note: All the GTK WebView's operations should be
        // processed in one single thread for each window.

        // Linux WebView thread
        #ifdef _WIN32
        HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
        if (thread != NULL)
            CloseHandle(thread);
        #else
        pthread_t thread;
        pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
        pthread_detach(thread);
        #endif
    }

    static int _webui_wv_create_schedule(void* arg) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_create_schedule()\n");
        #endif

        // This callback is fired by GTK. so it's safe
        // to create the new WebView window right now.

        _webui_window_t* win = _webui_dereference_win_ptr(arg);
        if (win) {
            _webui_wv_create(win);
        }

        return 0;
    }

    static bool _webui_wv_show(_webui_window_t* win, char* url) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show([%s])\n", url);
        #endif

        // Linux GTK WebView

        #ifdef WEBUI_DYNAMIC
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show() -> WebUI dynamic version does not support Linux WebView\n");
        #endif
        return false;
        #endif

        if (_webui.is_browser_main_run)
            return false;

        // Dynamic Load
        if (!libgtk || !libwebkit) {

            _webui.is_webview = false;

            // GTK Dynamic Load
            const char *gtk_libs[] = GTK_RUNTIME_ARR;
            for (size_t i = 0; i < (sizeof(gtk_libs) / sizeof(gtk_libs[0]));++i) {
                libgtk = dlopen(gtk_libs[i], RTLD_LAZY);
                if (libgtk) {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_wv_show() -> GTK loaded [%s]\n", 
                    gtk_libs[i]);
                    #endif
                    break;
                }
            }

            if (!libgtk) {
                _webui_wv_free();
                return false;
            }
    
            // WebView Dynamic Load
            const char *webkit_libs[] = WEBKIT_RUNTIME_ARR;
            for (size_t i = 0; i < (sizeof(webkit_libs) / sizeof(webkit_libs[0]));++i) {
                libwebkit = dlopen(webkit_libs[i], RTLD_LAZY);
                if (libwebkit) {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t_webui_wv_show() -> WebKit loaded [%s]\n", 
                    webkit_libs[i]);
                    #endif
                    break;
                }
            }

            if (!libwebkit) {
                _webui_wv_free();
                return false;
            }

            // GTK Symbol Addresses
            gtk_init = (gtk_init_func)dlsym(
                libgtk, "gtk_init");
            gtk_widget_show_all = (gtk_widget_show_all_func)dlsym(
                libgtk, "gtk_widget_show_all");
            gtk_main_iteration_do = (gtk_main_iteration_do_func)dlsym(
                libgtk, "gtk_main_iteration_do");
            gtk_events_pending = (gtk_events_pending_func)dlsym(
                libgtk, "gtk_events_pending");
            gtk_container_add = (gtk_container_add_func)dlsym(
                libgtk, "gtk_container_add");
            gtk_window_new = (gtk_window_new_func)dlsym(
                libgtk, "gtk_window_new");
            gtk_window_set_default_size = (gtk_window_set_default_size_func)dlsym(
                libgtk, "gtk_window_set_default_size");
            gtk_window_set_title = (gtk_window_set_title_func)dlsym(
                libgtk, "gtk_window_set_title");
            gtk_window_move = (gtk_window_move_func)dlsym(
                libgtk, "gtk_window_move");
            gtk_window_close = (gtk_window_close_func)dlsym(
                libgtk, "gtk_window_close");
            gtk_window_resize = (gtk_window_resize_func)dlsym(
                libgtk, "gtk_window_resize");
            gtk_window_set_position = (gtk_window_set_position_func)dlsym(
                libgtk, "gtk_window_set_position");
            g_signal_connect_data = (g_signal_connect_data_func)dlsym(
                libgtk, "g_signal_connect_data");
            g_idle_add = (g_idle_add_func)dlsym(
                libgtk, "g_idle_add");
            
            // WebView Symbol Addresses
            webkit_web_view_new = (webkit_web_view_new_func)dlsym(
                libwebkit, "webkit_web_view_new");
            webkit_web_view_load_uri = (webkit_web_view_load_uri_func)dlsym(
                libwebkit, "webkit_web_view_load_uri");
            webkit_web_view_get_title = (webkit_web_view_get_title_func)dlsym(
                libwebkit, "webkit_web_view_get_title");

            // Check GTK
            if (
                // GTK Commun
                !gtk_init || !gtk_window_new || !gtk_window_set_default_size
                || !gtk_window_set_title || !g_signal_connect_data
                // GTK v3
                || !gtk_widget_show_all || !gtk_main_iteration_do
                || !gtk_events_pending || !gtk_container_add
                || !gtk_window_move
                // GTK v4
                // ...
                )
            {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_webview_thread() -> GTK symbol addresses failed\n");
                #endif
                _webui_wv_free();
                return false;
            }

            // Check WebView
            if (!webkit_web_view_new || !webkit_web_view_load_uri || !webkit_web_view_get_title) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_webview_thread() -> WebKit symbol addresses failed\n");
                #endif
                _webui_wv_free();
                return false;
            }

            // Initialize GTK
            gtk_init(NULL, NULL);
        }

        // Free old WebView
        if (win->webView) {
            win->webView->stop = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
        }

        // Copy URL
        char* url_copy = _webui_str_dup(url);

        // Initializing the Linux WebView struct
        _webui_wv_linux_t* webView = (_webui_wv_linux_t*) _webui_malloc(sizeof(_webui_wv_linux_t));
        webView->url = url_copy;
        webView->width = (win->width > 0 ? win->width : 800);
        webView->height = (win->height > 0 ? win->height : 600);
        webView->x = (win->x > 0 ? win->x : 0);
        webView->y = (win->y > 0 ? win->y : 0);
        win->webView = webView;

        // New WebView window
        if (_webui.is_gtk_main_run) {

            // Schedule the creation of the new WebView
            // window in the main thread `webui_wait()`

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_wv_show() -> Schedule the creation of the new WebView window\n");
            #endif

            g_idle_add(_webui_wv_create_schedule, (void*)win);
        }
        else {

            // The main thread `webui_wait()` is not running
            // so it's safe to create the new WebView window
            // from this thread, which is should be fired from
            // the back-end main thread.

            #ifdef WEBUI_LOG
            printf("[Core]\t\t_webui_wv_show() -> New WebView window\n");
            #endif

            _webui_wv_create(win);
        }

        // Initializing
        // Expecting `_webui_webview_thread` to change `mutex_is_webview_update` 
        // to `false` when initialization is done, and `_webui.is_webview`
        // to `true` if loading the WebView is succeeded.
        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);

        // Wait for WebView thread to get
        // started by `_webui_wv_create()`
        
        _webui_timer_t timer;
        _webui_timer_start(&timer);
        for (;;) {
            _webui_sleep(100);
            if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
                // WebView thread just started
                // and loaded window successfully
                break;
            }
            if (_webui_timer_is_end(&timer, 2500)) {
                // Timeout. WebView thread failed.
                break;
            }
        }

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show() -> Return [%d]\n", (_webui.is_webview == true));
        #endif

        return (_webui.is_webview);
    };

    static WEBUI_THREAD_WEBVIEW {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
        #endif

        _webui_window_t* win = _webui_dereference_win_ptr(arg);
        if (win == NULL) {
            _webui_wv_close(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        // Check GTK is loaded
        if (!libgtk || !libwebkit) {
            _webui_wv_close(win->webView);
            win->webView = NULL;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
            WEBUI_THREAD_RETURN
        }

        if (win->webView->gtk_win && win->webView->gtk_wv) {

            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Started\n");
            #endif

            // Success
            // Let `wait()` use safe main-thread GTK WebView loop
            _webui.is_webview = true;
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

            while (true) {

                if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {

                    _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

                    if (win->webView) {

                        // Stop this thread
                        if (win->webView->stop) {
                            break;
                        }

                        // Window Size
                        if (win->webView->size) {
                            win->webView->size = false;
                            _webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
                        }

                        // Window Position
                        if (win->webView->position) {
                            win->webView->position = false;
                            _webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
                        }

                        // Navigation
                        if (win->webView->navigate) {
                            win->webView->navigate = false;
                            _webui_wv_navigate(win->webView, win->webView->url);
                        }
                    }
                }
            }
        }

        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Cleaning\n");
        #endif

        // Clean
        _webui_wv_close(win->webView);
        win->webView = NULL;

        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
        #endif

        WEBUI_THREAD_RETURN
    }
#else
    // macOS

    static bool _webui_wv_set_size(_webui_wv_macos_t* webView, int windowWidth, int windowHeight) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
        #endif
        _webui_macos_wv_set_size(webView->index, windowWidth, windowHeight);
        return false;
    };

    static bool _webui_wv_set_position(_webui_wv_macos_t* webView, int x, int y) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
        #endif
        _webui_macos_wv_set_position(webView->index, x, y);
        return false;
    };

    static bool _webui_wv_navigate(_webui_wv_macos_t* webView, char* url) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_navigate([%s])\n", url);
        #endif
        _webui_macos_wv_navigate(webView->index, (const char*)url);
        return false;
    };

    static void _webui_wv_free(_webui_wv_macos_t* webView) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_free()\n");
        #endif
        // ...
        _webui_free_mem((void*) webView->url);
        _webui_free_mem((void*) webView);
    };

    static void _webui_wv_close(_webui_wv_macos_t *webView) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_close()\n");
        #endif
        // ...
    }

    // Close Event
    static void _webui_wv_event_closed(int index) {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_event_closed([%d])\n", index);
        #endif
        if (_webui.wins[index] != NULL) {
            _webui.wins[index]->is_closed = true;
            if (_webui.wins[index]->webView) {
                // Close window
                if (_webui_mutex_is_connected(_webui.wins[index], WEBUI_MUTEX_NONE)) {
                    _webui_send_all(
                        _webui.wins[index], 0, WEBUI_CMD_CLOSE, NULL, 0
                    );
                }
                // Stop WebView thread if any
                if (_webui.wins[index]->webView) {
                    _webui.wins[index]->webView->stop = true;
                    _webui_mutex_is_webview_update(_webui.wins[index], WEBUI_MUTEX_TRUE);
                }
            }
        }
    }

    static bool _webui_wv_show(_webui_window_t* win, char* url) {

        #ifdef WEBUI_LOG
        printf("[Core]\t\t_webui_wv_show([%s])\n", url);
        #endif

        // Apple macOS WKWebView

        if (_webui.is_browser_main_run)
            return false;

        if (!_webui.is_wkwebview_main_run) {
            if (_webui_macos_wv_new(win->num)) {
                if (!_webui.is_webview) {
                    // Let `wait()` use safe main-thread WKWebView loop
                    _webui.is_webview = true;
                    // Set close callback once
                    _webui_macos_wv_set_close_cb(_webui_wv_event_closed);
                }
            } else return false;
        }
        else {

            _webui_macos_wv_new_thread_safe(win->num);
            _webui_sleep(250);
        }

        // Free old WebView
        if (win->webView) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
        }

        // Copy URL
        char* url_copy = _webui_str_dup(url);

        // Initializing the macOS WebView struct
        _webui_wv_macos_t* webView = (_webui_wv_macos_t*) _webui_malloc(sizeof(_webui_wv_macos_t));
        webView->url = url_copy;
        webView->width = (win->width > 0 ? win->width : 800);
        webView->height = (win->height > 0 ? win->height : 600);
        webView->x = (win->x > 0 ? win->x : 100);
        webView->y = (win->y > 0 ? win->y : 100);
        webView->index = win->num;
        win->webView = webView;

        // Show window
        _webui_macos_wv_show(
            webView->index, webView->url,
            webView->x, webView->y,
            webView->width, webView->height);

        // Initializing
        // Expecting `_webui_webview_thread` to change
        // `mutex_is_webview_update` to false when success
        _webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);

        // macOS WebView thread
        #ifdef _WIN32
        HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
        if (thread != NULL)
            CloseHandle(thread);
        #else
        pthread_t thread;
        pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
        pthread_detach(thread);
        #endif
        
        // Wait for WebView thread to start
        _webui_timer_t timer;
        _webui_timer_start(&timer);
        for (;;) {
            _webui_sleep(10);
            if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
                // WebView thread just started
                // and loaded WebView successfully
                break;
            }
            if (_webui_timer_is_end(&timer, 2500)) {
                // Timeout. WebView thread failed.
                break;
            }
        }

        return (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE) == false);
    };

    static WEBUI_THREAD_WEBVIEW {
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
        #endif

        _webui_window_t* win = _webui_dereference_win_ptr(arg);
        if (win == NULL) {
            _webui_wv_free(win->webView);
            win->webView = NULL;
            WEBUI_THREAD_RETURN
        }

        // ...

        if (true) {

            // Success
            _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

            while (true) {

                // Messages ...

                if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {

                    _webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);

                    if (win->webView) {

                        // Stop this thread
                        if (win->webView->stop) {
                            _webui_macos_wv_close(win->webView->index);
                            break;
                        }

                        // Window Size
                        if (win->webView->size) {
                            win->webView->size = false;
                            _webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
                        }

                        // Window Position
                        if (win->webView->position) {
                            win->webView->position = false;
                            _webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
                        }

                        // Navigation
                        if (win->webView->navigate) {
                            win->webView->navigate = false;
                            _webui_wv_navigate(win->webView, win->webView->url);
                        }
                    }
                }
            }
        }

        // Close window in case WKWebView did
        // not fire the close event.
        if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
            _webui_send_all(
                win, 0, WEBUI_CMD_CLOSE, NULL, 0
            );
        }

        // Clean
        _webui_wv_free(win->webView);
        win->webView = NULL;

        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
        #endif

        WEBUI_THREAD_RETURN
    }
#endif

static WEBUI_THREAD_MONITOR {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread()\n");
    #endif

    _webui_window_t* win = _webui_dereference_win_ptr(arg);
    if (win == NULL) {
        WEBUI_THREAD_RETURN
    }

    // Stop if a lower window already monitoring the same folder
    // Loop trough all windows
    for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
        if ((_webui.wins[i] != NULL) && (_webui.wins[i] != win) && (i < win->num)) {
            WEBUI_THREAD_RETURN
        }
    }

    const char* js = "location.reload();";
    size_t js_len = _webui_strlen(js);

    #ifdef _WIN32
        // Windows
        HANDLE hDir = CreateFile(
            win->server_root_path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
        );
        if (hDir == INVALID_HANDLE_VALUE) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to open folder: %s\n", 
                win->server_root_path
            );
            #endif
            WEBUI_THREAD_RETURN
        }
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
        #endif
        char buffer[1024];
        DWORD bytesReturned;
        while ((!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) && (win->server_running)) {
            if (ReadDirectoryChangesW(
                    hDir, buffer, sizeof(buffer), TRUE,
                    FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES |
                    FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesReturned, NULL, NULL
                ))
            {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
                #endif
                // Loop trough all connected clients in this window
                for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
                    if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) && (_webui.clients_token_check[i])) {
                        _webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
                    }
                }
            } else {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to read folder changes\n");
                #endif
                break;
            }
        }
        CloseHandle(hDir);
    #elif __linux__
        // Linux
        int fd = inotify_init();
        if (fd < 0) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_init error\n");
            #endif
            WEBUI_THREAD_RETURN
        }
        int wd = inotify_add_watch(fd, win->server_root_path, IN_MODIFY | IN_CREATE | IN_DELETE);
        if (wd < 0) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_add_watch error\n");
            #endif
            close(fd);
            WEBUI_THREAD_RETURN
        }
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
        #endif
        char buffer[1024];
        while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
            int length = read(fd, buffer, sizeof(buffer));
            if (length < 0) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> read error\n");
                #endif
                break;
            }
            int i = 0;
            while (i < length) {
                struct inotify_event *event = (struct inotify_event *)&buffer[i];
                if (event->len) {
                    if (event->mask&(IN_CREATE | IN_DELETE | IN_MODIFY)) {
                        #ifdef WEBUI_LOG
                        printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
                        #endif
                        // Loop trough all connected clients in this window
                        for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
                            if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) && (_webui.clients_token_check[i])) {
                                _webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
                            }
                        }
                    }
                }
                i += sizeof(struct inotify_event) + event->len;
            }
        }
        inotify_rm_watch(fd, wd);
        close(fd);
    #else
        // macOS
        int kq = kqueue();
        if (kq == -1) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kqueue error\n");
            #endif
            WEBUI_THREAD_RETURN
        }
        int fd = open(win->server_root_path, O_RDONLY);
        if (fd == -1) {
            #ifdef WEBUI_LOG
            printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> open error\n");
            #endif
            close(kq);
            WEBUI_THREAD_RETURN
        }
        #ifdef WEBUI_LOG
        printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
        #endif
        struct kevent change;
        EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE, 0, NULL);
        while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
            struct kevent event;
            int nev = kevent(kq, &change, 1, &event, 1, NULL);
            if (nev == -1) {
                #ifdef WEBUI_LOG
                printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kevent error\n");
                #endif
                break;
            } else if (nev > 0) {
                if (event.fflags&NOTE_WRITE) {
                    #ifdef WEBUI_LOG
                    printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
                    #endif
                    // Loop trough all connected clients in this window
                    for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
                        if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) && (_webui.clients_token_check[i])) {
                            _webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
                        }
                    }
                }
            }
        }
        close(fd);
        close(kq);
    #endif
    #ifdef WEBUI_LOG
    printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Exit\n");
    #endif
    WEBUI_THREAD_RETURN
}

static void _webui_bridge_api_handler(webui_event_t* e) {

    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_bridge_api_handler()\n");
    #endif

	// This function gets called by `webui.js` to reach
    // an internal core API. This is not a public user API.

    const char* api_name = webui_get_string(e);
    #ifdef WEBUI_LOG
    printf("[Core]\t\t_webui_bridge_api_handler() -> api_name: [%s]\n", api_name);
    #endif

    if (strcmp(api_name, "high_contrast") == 0) {
        webui_return_bool(e, webui_is_high_contrast());
    }
}
