#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

/// Probe EGL: load libEGL, get a display, initialize it, and verify that at
/// least one usable RGBA config exists.  Returns true only if the full
/// sequence succeeds  - this catches missing drivers, Mesa/NVIDIA mismatches,
/// and broken configs that would give WebKitGTK a black window.
/// Uses dlopen so there's no link-time dependency on EGL.
#[cfg(target_os = "linux")]
fn egl_display_available() -> bool {
    extern "C" {
        fn dlopen(filename: *const std::ffi::c_char, flags: i32) -> *mut std::ffi::c_void;
        fn dlsym(handle: *mut std::ffi::c_void, symbol: *const std::ffi::c_char) -> *mut std::ffi::c_void;
        fn dlclose(handle: *mut std::ffi::c_void) -> i32;
    }
    const RTLD_LAZY: i32 = 1;

    macro_rules! sym {
        ($handle:expr, $name:literal) => {{
            let s = dlsym($handle, concat!($name, "\0").as_ptr() as *const _);
            if s.is_null() { dlclose($handle); return false; }
            s
        }};
    }

    unsafe {
        let handle = dlopen(b"libEGL.so.1\0".as_ptr() as *const _, RTLD_LAZY);
        if handle.is_null() {
            return false;
        }

        // Resolve the three functions we need
        let get_display: extern "C" fn(usize) -> usize =
            std::mem::transmute(sym!(handle, "eglGetDisplay"));
        let initialize: extern "C" fn(usize, *mut i32, *mut i32) -> u32 =
            std::mem::transmute(sym!(handle, "eglInitialize"));
        let choose_config: extern "C" fn(usize, *const i32, *mut usize, i32, *mut i32) -> u32 =
            std::mem::transmute(sym!(handle, "eglChooseConfig"));
        let terminate: extern "C" fn(usize) -> u32 =
            std::mem::transmute(sym!(handle, "eglTerminate"));

        // Step 1: Get display
        let display = get_display(0); // EGL_DEFAULT_DISPLAY
        if display == 0 {
            dlclose(handle);
            return false;
        }

        // Step 2: Initialize
        let mut major: i32 = 0;
        let mut minor: i32 = 0;
        if initialize(display, &mut major, &mut minor) == 0 {
            dlclose(handle);
            return false;
        }

        // Step 3: Check for a usable RGBA config
        // EGL_RED_SIZE=0x3024 EGL_GREEN_SIZE=0x3023 EGL_BLUE_SIZE=0x3022
        // EGL_ALPHA_SIZE=0x3021 EGL_SURFACE_TYPE=0x3033 EGL_WINDOW_BIT=0x0004
        // EGL_RENDERABLE_TYPE=0x3040 EGL_OPENGL_ES2_BIT=0x0004 EGL_NONE=0x3038
        let attribs: [i32; 15] = [
            0x3024, 8,      // RED 8
            0x3023, 8,      // GREEN 8
            0x3022, 8,      // BLUE 8
            0x3021, 8,      // ALPHA 8
            0x3033, 0x0004, // SURFACE_TYPE = WINDOW
            0x3040, 0x0004, // RENDERABLE_TYPE = ES2
            0x3038,         // NONE
            0, 0,           // padding
        ];
        let mut config: usize = 0;
        let mut num_configs: i32 = 0;
        let ok = choose_config(
            display,
            attribs.as_ptr(),
            &mut config,
            1,
            &mut num_configs,
        );

        terminate(display);
        dlclose(handle);

        ok != 0 && num_configs > 0
    }
}

fn main() {
    // The AppImage's linuxdeploy-plugin-gtk.sh forces GDK_BACKEND=x11 as a
    // safety default, but XWayland on NVIDIA breaks WebKitGTK input events
    // (no clicks, no scroll) and client-side decorations.
    // Override to native Wayland which works correctly on modern NVIDIA drivers.
    // See: https://github.com/tauri-apps/tauri/issues/11790
    #[cfg(target_os = "linux")]
    {
        if std::env::var("WAYLAND_DISPLAY").is_ok() || std::env::var("XDG_SESSION_TYPE").map(|v| v == "wayland").unwrap_or(false) {
            std::env::set_var("GDK_BACKEND", "wayland");
        }

        // When running as an AppImage, prevent GTK from loading the host's
        // GIO modules (gvfs, dconf, libproxy). These are compiled against the
        // host's newer glib and fail with "undefined symbol" errors when loaded
        // into the AppImage's bundled older glib.
        // These modules are optional  - gvfs (virtual filesystems), dconf
        // (desktop settings), libproxy (proxy config)  - none are needed here.
        if std::env::var("APPIMAGE").is_ok() {
            if let Ok(appdir) = std::env::var("APPDIR") {
                let gio_path = format!("{}/usr/lib/x86_64-linux-gnu/gio/modules", appdir);
                if std::path::Path::new(&gio_path).exists() {
                    std::env::set_var("GIO_MODULE_DIR", &gio_path);
                } else {
                    // No bundled GIO modules  - point to empty dir to skip system ones
                    std::env::set_var("GIO_MODULE_DIR", &appdir);
                }
            }
        }

        // Disable the DMA-BUF renderer in AppImage builds.
        // WebKitGTK 2.42+ defaults to DMA-BUF for framebuffers, but inside an
        // AppImage the bundled Mesa often can't construct them against the host's
        // GPU drivers  - producing a completely black window with zero errors.
        // This falls back to shared-memory buffers; GPU compositing still works,
        // visual/performance difference is negligible for a desktop app.
        // Override: WEBKIT_DISABLE_DMABUF_RENDERER=0
        // See: https://github.com/tauri-apps/tauri/issues/13183
        if std::env::var("APPIMAGE").is_ok()
            && std::env::var("WEBKIT_DISABLE_DMABUF_RENDERER").is_err()
        {
            std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
        }

        // Auto-detect EGL failures for AppImage builds.
        // If EGL can't even initialize or find a usable config, also disable
        // GPU compositing entirely (more aggressive than the DMABUF fix above).
        // Override: WEBKIT_DISABLE_COMPOSITING_MODE=0
        if std::env::var("APPIMAGE").is_ok()
            && std::env::var("WEBKIT_DISABLE_COMPOSITING_MODE").is_err()
            && !egl_display_available()
        {
            std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
        }

        // Ensure GStreamer can find system plugins for WebKitGTK Web Audio.
        // The AppImage doesn't bundle GStreamer (bundleMediaFramework: false),
        // so point it at the host's plugin directories.
        if std::env::var("GST_PLUGIN_SYSTEM_PATH").is_err()
            && std::env::var("GST_PLUGIN_SYSTEM_PATH_1_0").is_err()
        {
            let paths = [
                "/usr/lib/x86_64-linux-gnu/gstreamer-1.0",
                "/usr/lib64/gstreamer-1.0",
                "/usr/lib/gstreamer-1.0",
            ];
            let existing: Vec<&str> = paths
                .iter()
                .filter(|p| std::path::Path::new(p).exists())
                .copied()
                .collect();
            if !existing.is_empty() {
                std::env::set_var("GST_PLUGIN_SYSTEM_PATH_1_0", existing.join(":"));
            }
        }
    }

    zeroclock_lib::run();
}
