use std::ops::Neg;

use crate::ui;

const R_DPAD: usize = 0;
const L_DPAD: usize = 1;
const D_DPAD: usize = 2;
const U_DPAD: usize = 3;
const START_BUTTON: usize = 4;
const Z_TRIG: usize = 5;
const B_BUTTON: usize = 6;
const A_BUTTON: usize = 7;
const R_CBUTTON: usize = 8;
const L_CBUTTON: usize = 9;
const D_CBUTTON: usize = 10;
const U_CBUTTON: usize = 11;
const R_TRIG: usize = 12;
const L_TRIG: usize = 13;
const AXIS_LEFT: usize = 14;
const AXIS_RIGHT: usize = 15;
const AXIS_UP: usize = 16;
const AXIS_DOWN: usize = 17;
const HOTKEY: usize = 18;
pub const PROFILE_SIZE: usize = 19;

const X_AXIS_SHIFT: usize = 16;
const Y_AXIS_SHIFT: usize = 24;

const MAX_AXIS_VALUE: f64 = 85.0;

pub const DEADZONE_DEFAULT: i32 = 5;

pub const UNKNOWN_CONTROLLER_NAME: &str = "Unknown controller";

#[derive(Default)]
pub struct Controllers {
    pub rumble: bool,
    pub game_controller: *mut sdl3_sys::gamepad::SDL_Gamepad,
    pub joystick: *mut sdl3_sys::joystick::SDL_Joystick,
    pub guid: sdl3_sys::guid::SDL_GUID,
    pub last_key_state: u32,
}

#[derive(Default, PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub struct InputData {
    pub data: u32,
    pub pak_change_pressed: bool,
}

fn bound_axis(x: &mut f64, y: &mut f64) {
    let radius = f64::sqrt(70.0 * 70.0 + 70.0 * 70.0); // this is roughly the maximum diagonal distance of the controller

    // Calculate the distance from the origin (0, 0)
    let distance = f64::sqrt((*x) * (*x) + (*y) * (*y));

    // If the distance is greater than the radius, scale the coordinates
    if distance > radius {
        let scale_factor = radius / distance;
        *x *= scale_factor;
        *y *= scale_factor;
    }
}

fn apply_deadzone(x: &mut f64, y: &mut f64, deadzone: i32) {
    let axis_deadzone = MAX_AXIS_VALUE * (deadzone as f64 / 100.0);

    // Calculate the distance from the origin (0, 0)
    let distance = f64::sqrt((*x) * (*x) + (*y) * (*y));

    if distance <= axis_deadzone {
        *x = 0.0;
        *y = 0.0;
        return;
    }

    let new_distance =
        (distance - axis_deadzone) * MAX_AXIS_VALUE / (MAX_AXIS_VALUE - axis_deadzone);
    *x = *x / distance * new_distance;
    *y = *y / distance * new_distance;
}

fn normalize_axis_position(axis_position: i16) -> f64 {
    axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64
}

fn set_axis_from_joystick(
    profile: &ui::config::InputProfile,
    joystick: *mut sdl3_sys::joystick::SDL_Joystick,
) -> (f64, f64) {
    let mut x = 0.0;
    let mut y = 0.0;
    if profile.joystick_axis[AXIS_LEFT].enabled {
        let axis_position = unsafe {
            sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_LEFT].id)
        };
        if axis_position as isize * profile.joystick_axis[AXIS_LEFT].axis as isize > 0 {
            x = normalize_axis_position(axis_position);
        }
    }
    if profile.joystick_axis[AXIS_RIGHT].enabled {
        let axis_position = unsafe {
            sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_RIGHT].id)
        };
        if axis_position as isize * profile.joystick_axis[AXIS_RIGHT].axis as isize > 0 {
            x = normalize_axis_position(axis_position);
        }
    }
    if profile.joystick_axis[AXIS_DOWN].enabled {
        let axis_position = unsafe {
            sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_DOWN].id)
        };
        if axis_position as isize * profile.joystick_axis[AXIS_DOWN].axis as isize > 0 {
            y = normalize_axis_position(axis_position).neg();
        }
    }
    if profile.joystick_axis[AXIS_UP].enabled {
        let axis_position = unsafe {
            sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_UP].id)
        };
        if axis_position as isize * profile.joystick_axis[AXIS_UP].axis as isize > 0 {
            y = normalize_axis_position(axis_position).neg();
        }
    }
    (x, y)
}

fn set_axis_from_controller(
    profile: &ui::config::InputProfile,
    controller: *mut sdl3_sys::gamepad::SDL_Gamepad,
) -> (f64, f64) {
    let mut x = 0.0;
    let mut y = 0.0;
    if profile.controller_axis[AXIS_LEFT].enabled {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_LEFT].id),
            )
        };
        if axis_position as isize * profile.controller_axis[AXIS_LEFT].axis as isize > 0 {
            x = normalize_axis_position(axis_position);
        }
    }
    if profile.controller_axis[AXIS_RIGHT].enabled {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_RIGHT].id),
            )
        };
        if axis_position as isize * profile.controller_axis[AXIS_RIGHT].axis as isize > 0 {
            x = normalize_axis_position(axis_position);
        }
    }
    if profile.controller_axis[AXIS_DOWN].enabled {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_DOWN].id),
            )
        };
        if axis_position as isize * profile.controller_axis[AXIS_DOWN].axis as isize > 0 {
            y = normalize_axis_position(axis_position).neg();
        }
    }
    if profile.controller_axis[AXIS_UP].enabled {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_UP].id),
            )
        };
        if axis_position as isize * profile.controller_axis[AXIS_UP].axis as isize > 0 {
            y = normalize_axis_position(axis_position).neg();
        }
    }
    (x, y)
}

fn set_axis_from_keys(
    profile: &ui::config::InputProfile,
    keyboard_state: *const bool,
) -> (f64, f64) {
    let mut x = 0.0;
    let mut y = 0.0;
    if profile.keys[AXIS_LEFT].enabled
        && unsafe { *keyboard_state.offset(profile.keys[AXIS_LEFT].id as isize) }
    {
        x = -MAX_AXIS_VALUE
    }
    if profile.keys[AXIS_RIGHT].enabled
        && unsafe { *keyboard_state.offset(profile.keys[AXIS_RIGHT].id as isize) }
    {
        x = MAX_AXIS_VALUE
    }
    if profile.keys[AXIS_DOWN].enabled
        && unsafe { *keyboard_state.offset(profile.keys[AXIS_DOWN].id as isize) }
    {
        y = -MAX_AXIS_VALUE
    }
    if profile.keys[AXIS_UP].enabled
        && unsafe { *keyboard_state.offset(profile.keys[AXIS_UP].id as isize) }
    {
        y = MAX_AXIS_VALUE
    }
    (x, y)
}

fn set_buttons_from_joystick(
    profile: &ui::config::InputProfile,
    i: usize,
    joystick: *mut sdl3_sys::joystick::SDL_Joystick,
    keys: &mut u32,
) {
    let profile_joystick_button = profile.joystick_buttons[i];
    if profile_joystick_button.enabled {
        *keys |= (unsafe {
            sdl3_sys::joystick::SDL_GetJoystickButton(joystick, profile_joystick_button.id)
        } as u32)
            << i;
    }

    let profile_joystick_hat = profile.joystick_hat[i];
    if profile_joystick_hat.enabled
        && (unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, profile_joystick_hat.id) }
            & profile_joystick_hat.direction)
            != 0
    {
        *keys |= 1 << i;
    }

    let profile_joystick_axis = profile.joystick_axis[i];
    if profile_joystick_axis.enabled {
        let axis_position =
            unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile_joystick_axis.id) };
        if (axis_position as isize * profile_joystick_axis.axis as isize > 0
            || profile_joystick_axis.initial_state != 0)
            && axis_position.abs_diff(profile_joystick_axis.initial_state) > (u16::MAX / 4)
        {
            *keys |= 1 << i;
        }
    }
}

fn set_buttons_from_controller(
    profile: &ui::config::InputProfile,
    i: usize,
    controller: *mut sdl3_sys::gamepad::SDL_Gamepad,
    keys: &mut u32,
) {
    let profile_controller_button = profile.controller_buttons[i];
    if profile_controller_button.enabled {
        *keys |= (unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadButton(
                controller,
                sdl3_sys::gamepad::SDL_GamepadButton(profile_controller_button.id),
            )
        } as u32)
            << i;
    }

    let profile_controller_axis = profile.controller_axis[i];
    if profile_controller_axis.enabled {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(profile_controller_axis.id),
            )
        };
        if axis_position as isize * profile_controller_axis.axis as isize > 0
            && axis_position.saturating_abs() > i16::MAX / 2
        {
            *keys |= 1 << i;
        }
    }
}

pub fn set_rumble(ui: &ui::Ui, channel: usize, rumble: u8) {
    if !ui.input.controllers[channel].rumble {
        return;
    }
    let controller = ui.input.controllers[channel].game_controller;
    let joystick = ui.input.controllers[channel].joystick;
    if !controller.is_null() {
        unsafe {
            sdl3_sys::gamepad::SDL_RumbleGamepad(
                controller,
                (rumble & 1) as u16 * u16::MAX,
                (rumble & 1) as u16 * u16::MAX,
                (rumble & 1) as u32 * sdl3_sys::haptic::SDL_HAPTIC_INFINITY,
            )
        };
    } else if !joystick.is_null() {
        unsafe {
            sdl3_sys::joystick::SDL_RumbleJoystick(
                joystick,
                (rumble & 1) as u16 * u16::MAX,
                (rumble & 1) as u16 * u16::MAX,
                (rumble & 1) as u32 * sdl3_sys::haptic::SDL_HAPTIC_INFINITY,
            )
        };
    }
}

fn hotkey_pressed(
    profile: &ui::config::InputProfile,
    joystick: *mut sdl3_sys::joystick::SDL_Joystick,
    controller: *mut sdl3_sys::gamepad::SDL_Gamepad,
) -> bool {
    let controller_button = profile.controller_buttons[HOTKEY];
    let controller_axis = profile.controller_axis[HOTKEY];
    let joystick_button = profile.joystick_buttons[HOTKEY];
    let joystick_hat = profile.joystick_hat[HOTKEY];
    let joystick_axis = profile.joystick_axis[HOTKEY];

    let mut pressed = false;
    if controller_button.enabled && !controller.is_null() {
        pressed = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadButton(
                controller,
                sdl3_sys::gamepad::SDL_GamepadButton(controller_button.id),
            )
        };
    } else if controller_axis.enabled && !controller.is_null() {
        let axis_position = unsafe {
            sdl3_sys::gamepad::SDL_GetGamepadAxis(
                controller,
                sdl3_sys::gamepad::SDL_GamepadAxis(controller_axis.id),
            )
        };
        pressed = axis_position as isize * controller_axis.axis as isize > 0
            && axis_position.saturating_abs() > i16::MAX / 2
    } else if joystick_button.enabled && !joystick.is_null() {
        pressed =
            unsafe { sdl3_sys::joystick::SDL_GetJoystickButton(joystick, joystick_button.id) };
    } else if joystick_hat.enabled && !joystick.is_null() {
        pressed = (unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, joystick_hat.id) }
            & joystick_hat.direction)
            != 0;
    } else if joystick_axis.enabled && !joystick.is_null() {
        let axis_position =
            unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, joystick_axis.id) };
        pressed = (axis_position as isize * joystick_axis.axis as isize > 0
            || joystick_axis.initial_state != 0)
            && axis_position.abs_diff(joystick_axis.initial_state) > (u16::MAX / 4)
    }
    pressed
}

pub fn get_controller_names() -> Vec<String> {
    #[cfg(target_os = "android")]
    return ui::android::get_controller_names();

    #[cfg(not(target_os = "android"))]
    {
        let mut controllers: Vec<String> = vec![];

        for joystick in get_joysticks().iter() {
            let name = unsafe { sdl3_sys::joystick::SDL_GetJoystickNameForID(*joystick) };
            controllers.push(if name.is_null() {
                UNKNOWN_CONTROLLER_NAME.to_string()
            } else {
                unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }.to_string()
            });
        }
        controllers.insert(0, "None".into());

        controllers
    }
}

#[cfg(feature = "gui")]
pub fn get_controller_paths() -> Vec<String> {
    #[cfg(target_os = "android")]
    return ui::android::get_controller_paths();

    #[cfg(not(target_os = "android"))]
    {
        let mut controller_paths: Vec<String> = vec![];

        for joystick in get_joysticks().iter() {
            let path = unsafe { sdl3_sys::joystick::SDL_GetJoystickPathForID(*joystick) };
            controller_paths.push(if path.is_null() {
                String::new()
            } else {
                unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string()
            });
        }
        controller_paths.insert(0, String::new());

        controller_paths
    }
}

fn handle_joystick_events(ui: &mut ui::Ui) {
    let joystick_event = unsafe { ui::video::get_joystick_event() };
    if joystick_event.joystick_id != 0 {
        let joystick_id = sdl3_sys::joystick::SDL_JoystickID(joystick_event.joystick_id);
        for (i, controller) in ui.input.controllers.iter_mut().enumerate() {
            if joystick_event.connected {
                if let Some(profile) = ui
                    .config
                    .input
                    .input_profiles
                    .get(&ui.config.input.input_profile_binding[i])
                {
                    if profile.dinput {
                        if controller.joystick.is_null()
                            && controller.guid
                                == unsafe {
                                    sdl3_sys::joystick::SDL_GetJoystickGUIDForID(joystick_id)
                                }
                        {
                            controller.joystick =
                                unsafe { sdl3_sys::joystick::SDL_OpenJoystick(joystick_id) };
                        }
                    } else {
                        if controller.game_controller.is_null()
                            && controller.guid
                                == unsafe {
                                    sdl3_sys::gamepad::SDL_GetGamepadGUIDForID(joystick_id)
                                }
                        {
                            controller.game_controller =
                                unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(joystick_id) };
                        }
                    }
                }
            } else {
                if !controller.joystick.is_null()
                    && controller.joystick
                        == unsafe { sdl3_sys::joystick::SDL_GetJoystickFromID(joystick_id) }
                {
                    unsafe { sdl3_sys::joystick::SDL_CloseJoystick(controller.joystick) };
                    controller.joystick = std::ptr::null_mut();
                } else if !controller.game_controller.is_null()
                    && controller.game_controller
                        == unsafe { sdl3_sys::gamepad::SDL_GetGamepadFromID(joystick_id) }
                {
                    unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller.game_controller) };
                    controller.game_controller = std::ptr::null_mut();
                }
            }
        }
    }
}

fn handle_hotkeys(keys: u32, last_key_state: u32) {
    if keys & (1 << L_TRIG) != 0 && last_key_state & (1 << L_TRIG) == 0 {
        unsafe {
            sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event {
                user: sdl3_sys::events::SDL_UserEvent {
                    r#type: u32::from(sdl3_sys::events::SDL_EVENT_USER),
                    code: 1, //save state
                    ..Default::default()
                },
            })
        };
    }
    if keys & (1 << R_TRIG) != 0 && last_key_state & (1 << R_TRIG) == 0 {
        unsafe {
            sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event {
                user: sdl3_sys::events::SDL_UserEvent {
                    r#type: u32::from(sdl3_sys::events::SDL_EVENT_USER),
                    code: 2, //load state
                    ..Default::default()
                },
            })
        };
    }
    if keys & (1 << START_BUTTON) != 0 && last_key_state & (1 << START_BUTTON) == 0 {
        unsafe {
            sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event {
                user: sdl3_sys::events::SDL_UserEvent {
                    r#type: u32::from(sdl3_sys::events::SDL_EVENT_USER),
                    code: 3, //exit game
                    ..Default::default()
                },
            })
        };
    }
    if keys & (1 << Z_TRIG) != 0 && last_key_state & (1 << Z_TRIG) == 0 {
        unsafe {
            sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event {
                user: sdl3_sys::events::SDL_UserEvent {
                    r#type: u32::from(sdl3_sys::events::SDL_EVENT_USER),
                    code: 4, //fast forward
                    ..Default::default()
                },
            })
        };
    }
    if keys & (1 << L_CBUTTON) != 0 && last_key_state & (1 << L_CBUTTON) == 0 {
        unsafe {
            sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event {
                user: sdl3_sys::events::SDL_UserEvent {
                    r#type: u32::from(sdl3_sys::events::SDL_EVENT_USER),
                    code: 5, //load rewind
                    ..Default::default()
                },
            })
        };
    }
}

pub fn get(ui: &mut ui::Ui, channel: usize, vi_counter: u64) -> InputData {
    handle_joystick_events(ui);

    if ui.input.last_polled != vi_counter {
        ui.input.last_polled = vi_counter;
        unsafe { sdl3_sys::joystick::SDL_UpdateJoysticks() };
    }

    let profile_name = &ui.config.input.input_profile_binding[channel];
    let Some(profile) = ui.config.input.input_profiles.get(profile_name) else {
        eprintln!("Invalid profile name: {profile_name}");
        return InputData {
            data: 0,
            pak_change_pressed: false,
        };
    };
    let mut keys = 0;
    let controller = ui.input.controllers[channel].game_controller;
    let joystick = ui.input.controllers[channel].joystick;

    let alt_pressed = unsafe {
        // ignore key presses if ALT is pressed
        *ui.input
            .keyboard_state
            .offset(i32::from(sdl3_sys::scancode::SDL_SCANCODE_LALT) as isize)
            || *ui
                .input
                .keyboard_state
                .offset(i32::from(sdl3_sys::scancode::SDL_SCANCODE_RALT) as isize)
    };

    for i in 0..14 {
        if profile_name != "default" || channel == 0 {
            let profile_key = profile.keys[i];
            if profile_key.enabled && !alt_pressed {
                keys |= (unsafe { *ui.input.keyboard_state.offset(profile_key.id as isize) }
                    as u32)
                    << i;
            }
        }

        if !controller.is_null() {
            set_buttons_from_controller(profile, i, controller, &mut keys);
        } else if !joystick.is_null() {
            set_buttons_from_joystick(profile, i, joystick, &mut keys);
        }
    }

    let mut x: f64 = 0.0;
    let mut y: f64 = 0.0;

    if profile_name != "default" || channel == 0 {
        (x, y) = set_axis_from_keys(profile, ui.input.keyboard_state);
    }

    if !controller.is_null() {
        (x, y) = set_axis_from_controller(profile, controller);
        apply_deadzone(&mut x, &mut y, profile.deadzone);
    } else if !joystick.is_null() {
        (x, y) = set_axis_from_joystick(profile, joystick);
        apply_deadzone(&mut x, &mut y, profile.deadzone);
    }
    bound_axis(&mut x, &mut y);

    keys |= (x.round() as i8 as u8 as u32) << X_AXIS_SHIFT;
    keys |= (y.round() as i8 as u8 as u32) << Y_AXIS_SHIFT;

    let last_key_state = ui.input.controllers[channel].last_key_state;
    ui.input.controllers[channel].last_key_state = keys;

    if hotkey_pressed(profile, joystick, controller) {
        handle_hotkeys(keys, last_key_state);
        InputData {
            data: 0,
            pak_change_pressed: keys & (1 << B_BUTTON) != 0,
        }
    } else {
        let key = profile.keys[HOTKEY];
        InputData {
            data: keys,
            pak_change_pressed: key.enabled
                && unsafe { *ui.input.keyboard_state.offset(key.id as isize) },
        }
    }
}

pub fn assign_controller(config: &mut ui::config::Config, controller: i32, port: usize) {
    let joysticks = get_joysticks();
    if controller < 0 {
        config.input.controller_assignment[port - 1] = None;
    } else if controller < joysticks.len() as i32 {
        let path =
            unsafe { sdl3_sys::joystick::SDL_GetJoystickPathForID(joysticks[controller as usize]) };
        if !path.is_null() {
            config.input.controller_assignment[port - 1] =
                Some(unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap().to_string() });
        } else {
            eprintln!("Invalid controller path for controller {controller}");
        }
    } else {
        eprintln!("Invalid controller number")
    }
}

pub fn bind_input_profile(config: &mut ui::config::Config, profile: String, port: usize) {
    if config.input.input_profiles.contains_key(&profile) {
        config.input.input_profile_binding[port - 1] = profile;
    } else {
        eprintln!("Invalid profile name")
    }
}

pub fn clear_bindings(config: &mut ui::config::Config) {
    for i in 0..4 {
        config.input.controller_assignment[i] = None;
        config.input.input_profile_binding[i] = "default".to_string();
    }
}

fn close_input_profile_window(
    open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick>,
    open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad>,
    renderer: *mut sdl3_sys::render::SDL_Renderer,
    text_engine: *mut sdl3_ttf_sys::ttf::TTF_TextEngine,
    font: *mut sdl3_ttf_sys::ttf::TTF_Font,
    texture: *mut sdl3_sys::render::SDL_Texture,
    window: *mut sdl3_sys::video::SDL_Window,
) {
    for joystick in open_joysticks {
        unsafe { sdl3_sys::joystick::SDL_CloseJoystick(joystick) }
    }
    for controller in open_controllers {
        unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller) }
    }

    unsafe {
        sdl3_sys::render::SDL_DestroyTexture(texture);
        sdl3_ttf_sys::ttf::TTF_CloseFont(font);
        sdl3_ttf_sys::ttf::TTF_DestroyRendererTextEngine(text_engine);
        sdl3_sys::render::SDL_DestroyRenderer(renderer);
        sdl3_sys::video::SDL_DestroyWindow(window);
    }
}

pub fn configure_input_profile(
    config: &mut ui::config::Config,
    profile: String,
    dinput: bool,
    deadzone: i32,
) {
    ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO);
    ui::sdl_init(sdl3_sys::init::SDL_INIT_GAMEPAD);
    ui::ttf_init();

    if profile == "default" {
        eprintln!("Profile name cannot be default");
        return;
    }
    if profile.is_empty() {
        eprintln!("Profile name cannot be empty");
        return;
    }
    let mut open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick> = Vec::new();
    let mut open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad> = Vec::new();

    for joystick in get_joysticks().iter() {
        if !dinput {
            let controller = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*joystick) };
            if !controller.is_null() {
                open_controllers.push(controller);
            }
        } else {
            let joystick = unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*joystick) };
            if !joystick.is_null() {
                open_joysticks.push(joystick);
            }
        }
    }

    let title = std::ffi::CString::new("configure input profile").unwrap();
    let mut window: *mut sdl3_sys::video::SDL_Window = std::ptr::null_mut();
    let mut renderer: *mut sdl3_sys::render::SDL_Renderer = std::ptr::null_mut();
    #[cfg(target_os = "android")]
    let window_flags = sdl3_sys::video::SDL_WINDOW_FULLSCREEN;
    #[cfg(not(target_os = "android"))]
    let window_flags = sdl3_sys::video::SDL_WindowFlags(0);
    if !unsafe {
        sdl3_sys::render::SDL_CreateWindowAndRenderer(
            title.as_ptr(),
            852,
            480,
            window_flags,
            &mut window,
            &mut renderer,
        )
    } {
        panic!("Could not create window and renderer")
    }
    if !unsafe { sdl3_sys::video::SDL_ShowWindow(window) } {
        panic!("Could not show window")
    }

    let key_labels: [(&str, usize); PROFILE_SIZE] = [
        ("A", A_BUTTON),
        ("B", B_BUTTON),
        ("Start", START_BUTTON),
        ("D Up", U_DPAD),
        ("D Down", D_DPAD),
        ("D Left", L_DPAD),
        ("D Right", R_DPAD),
        ("C Up", U_CBUTTON),
        ("C Down", D_CBUTTON),
        ("C Left", L_CBUTTON),
        ("C Right", R_CBUTTON),
        ("L", L_TRIG),
        ("R", R_TRIG),
        ("Z", Z_TRIG),
        ("Control Stick Up", AXIS_UP),
        ("Control Stick Down", AXIS_DOWN),
        ("Control Stick Left", AXIS_LEFT),
        ("Control Stick Right", AXIS_RIGHT),
        ("Hotkey Activator", HOTKEY),
    ];

    let mut new_keys = [ui::config::InputKeyButton {
        enabled: false,
        id: 0,
    }; PROFILE_SIZE];
    let mut new_joystick_buttons = [ui::config::InputKeyButton {
        enabled: false,
        id: 0,
    }; PROFILE_SIZE];
    let mut new_joystick_hat = [ui::config::InputJoystickHat {
        enabled: false,
        id: 0,
        direction: 0,
    }; PROFILE_SIZE];
    let mut new_joystick_axis = [ui::config::InputControllerAxis {
        enabled: false,
        id: 0,
        axis: 0,
        initial_state: 0,
    }; PROFILE_SIZE];
    let mut new_controller_buttons = [ui::config::InputKeyButton {
        enabled: false,
        id: 0,
    }; PROFILE_SIZE];
    let mut new_controller_axis = [ui::config::InputControllerAxis {
        enabled: false,
        id: 0,
        axis: 0,
        initial_state: 0,
    }; PROFILE_SIZE];

    let mut last_joystick_axis_result = ui::config::InputControllerAxis {
        enabled: false,
        id: 0,
        axis: 0,
        initial_state: 0,
    };

    let image_bytes = include_bytes!("../../data/ui/controller.png");
    let image = unsafe {
        sdl3_image_sys::image::IMG_LoadPNG_IO(sdl3_sys::everything::SDL_IOFromConstMem(
            image_bytes.as_ptr() as *const std::ffi::c_void,
            image_bytes.len(),
        ))
    };
    let image_texture = unsafe { sdl3_sys::render::SDL_CreateTextureFromSurface(renderer, image) };
    unsafe { sdl3_sys::surface::SDL_DestroySurface(image) };
    let text_engine = unsafe { sdl3_ttf_sys::ttf::TTF_CreateRendererTextEngine(renderer) };
    let font = unsafe {
        let font_bytes = include_bytes!("../../data/ui/RobotoMono-Regular.ttf");
        sdl3_ttf_sys::ttf::TTF_OpenFontIO(
            sdl3_sys::everything::SDL_IOFromConstMem(
                font_bytes.as_ptr() as *const std::ffi::c_void,
                font_bytes.len(),
            ),
            true,
            35.0,
        )
    };

    for (key, value) in key_labels.iter() {
        unsafe {
            sdl3_sys::events::SDL_PumpEvents();
            sdl3_sys::events::SDL_FlushEvents(
                u32::from(sdl3_sys::events::SDL_EVENT_FIRST),
                u32::from(sdl3_sys::events::SDL_EVENT_LAST),
            );
        }

        let mut key_set = false;
        while !key_set {
            ui::video::draw_text(
                format!("Select binding for: {key}").as_str(),
                renderer,
                image_texture,
                text_engine,
                font,
            );
            let mut event: sdl3_sys::events::SDL_Event = Default::default();
            while !key_set && unsafe { sdl3_sys::events::SDL_WaitEventTimeout(&mut event, 100) } {
                if event.event_type() == sdl3_sys::events::SDL_EVENT_WINDOW_CLOSE_REQUESTED
                    || (event.event_type() == sdl3_sys::events::SDL_EVENT_KEY_DOWN
                        && unsafe { event.key.scancode }
                            == sdl3_sys::scancode::SDL_SCANCODE_AC_BACK)
                {
                    close_input_profile_window(
                        open_joysticks,
                        open_controllers,
                        renderer,
                        text_engine,
                        font,
                        image_texture,
                        window,
                    );
                    return;
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_KEY_DOWN {
                    if unsafe {
                        !event.key.repeat
                            && event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT
                            && event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_RALT
                    } {
                        new_keys[*value] = ui::config::InputKeyButton {
                            enabled: true,
                            id: i32::from(unsafe { event.key.scancode }),
                        };
                        key_set = true
                    }
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_GAMEPAD_BUTTON_DOWN {
                    if !open_controllers.is_empty() {
                        new_controller_buttons[*value] = ui::config::InputKeyButton {
                            enabled: true,
                            id: i32::from(unsafe { event.gbutton.button }),
                        };
                        key_set = true
                    }
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_GAMEPAD_AXIS_MOTION {
                    let axis_value = unsafe { event.gaxis.value };
                    let axis = unsafe { event.gaxis.axis };
                    if !open_controllers.is_empty()
                        && axis_value.saturating_abs() > (i16::MAX as i32 * 3 / 4) as i16
                    {
                        let result = ui::config::InputControllerAxis {
                            enabled: true,
                            id: axis as i32,
                            axis: axis_value / axis_value.saturating_abs(),
                            initial_state: 0,
                        };
                        if result != last_joystick_axis_result {
                            new_controller_axis[*value] = result;
                            last_joystick_axis_result = result;
                            key_set = true
                        }
                    }
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_BUTTON_DOWN {
                    if !open_joysticks.is_empty() {
                        new_joystick_buttons[*value] = ui::config::InputKeyButton {
                            enabled: true,
                            id: i32::from(unsafe { event.jbutton.button }),
                        };
                        key_set = true
                    }
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_HAT_MOTION {
                    let state = unsafe { event.jhat.value };
                    let hat = unsafe { event.jhat.hat };
                    if !open_joysticks.is_empty() && state != sdl3_sys::joystick::SDL_HAT_CENTERED {
                        new_joystick_hat[*value] = ui::config::InputJoystickHat {
                            enabled: true,
                            id: hat as i32,
                            direction: state,
                        };
                        key_set = true
                    }
                } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_AXIS_MOTION {
                    let axis_value = unsafe { event.jaxis.value };
                    let axis = unsafe { event.jaxis.axis };

                    let mut initial_state = 0;
                    let has_initial_state = unsafe {
                        sdl3_sys::joystick::SDL_GetJoystickAxisInitialState(
                            sdl3_sys::joystick::SDL_GetJoystickFromID(event.jaxis.which),
                            axis as i32,
                            &mut initial_state,
                        )
                    };
                    initial_state = if has_initial_state {
                        if initial_state < i16::MIN / 2 {
                            i16::MIN
                        } else if initial_state > i16::MAX / 2 {
                            i16::MAX
                        } else {
                            0
                        }
                    } else {
                        0
                    };

                    if !open_joysticks.is_empty()
                        && axis_value.abs_diff(initial_state) > (u16::MAX / 4)
                    {
                        let result = ui::config::InputControllerAxis {
                            enabled: true,
                            id: axis as i32,
                            axis: axis_value / axis_value.saturating_abs(),
                            initial_state,
                        };
                        let same_trigger = last_joystick_axis_result.id == result.id
                            && last_joystick_axis_result.initial_state != 0;
                        if result != last_joystick_axis_result && !same_trigger {
                            new_joystick_axis[*value] = result;
                            last_joystick_axis_result = result;
                            key_set = true
                        }
                    }
                }
            }
        }
        ui::video::draw_text(
            "Binding set, please wait...",
            renderer,
            image_texture,
            text_engine,
            font,
        );
        std::thread::sleep(std::time::Duration::from_millis(500));
    }

    close_input_profile_window(
        open_joysticks,
        open_controllers,
        renderer,
        text_engine,
        font,
        image_texture,
        window,
    );

    let new_profile = ui::config::InputProfile {
        keys: new_keys,
        controller_buttons: new_controller_buttons,
        controller_axis: new_controller_axis,
        joystick_buttons: new_joystick_buttons,
        joystick_hat: new_joystick_hat,
        joystick_axis: new_joystick_axis,
        dinput,
        deadzone,
    };
    config.input.input_profiles.insert(profile, new_profile);
}

pub fn get_default_profile() -> ui::config::InputProfile {
    let mut default_controller_buttons = [ui::config::InputKeyButton {
        enabled: false,
        id: 0,
    }; PROFILE_SIZE];
    let mut default_controller_axis = [ui::config::InputControllerAxis {
        enabled: false,
        id: 0,
        axis: 0,
        initial_state: 0,
    }; PROFILE_SIZE];
    let mut default_keys = [ui::config::InputKeyButton {
        enabled: false,
        id: 0,
    }; PROFILE_SIZE];
    default_keys[R_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_D),
    };
    default_keys[L_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_A),
    };
    default_keys[D_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_S),
    };
    default_keys[U_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_W),
    };
    default_keys[START_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RETURN),
    };
    default_keys[Z_TRIG] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_Z),
    };
    default_keys[B_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LCTRL),
    };
    default_keys[A_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LSHIFT),
    };
    default_keys[R_CBUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_L),
    };
    default_keys[L_CBUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_J),
    };
    default_keys[D_CBUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_K),
    };
    default_keys[U_CBUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_I),
    };
    default_keys[R_TRIG] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_C),
    };
    default_keys[L_TRIG] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_X),
    };
    default_keys[AXIS_LEFT] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LEFT),
    };
    default_keys[AXIS_RIGHT] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RIGHT),
    };
    default_keys[AXIS_UP] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_UP),
    };
    default_keys[AXIS_DOWN] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_DOWN),
    };
    default_keys[HOTKEY] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_COMMA),
    };

    default_controller_buttons[R_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT),
    };
    default_controller_buttons[L_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT),
    };
    default_controller_buttons[D_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN),
    };
    default_controller_buttons[U_DPAD] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP),
    };
    default_controller_buttons[START_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_START),
    };
    default_controller_axis[Z_TRIG] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFT_TRIGGER),
        axis: 1,
        initial_state: 0,
    };
    default_controller_buttons[B_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST),
    };
    default_controller_buttons[A_BUTTON] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH),
    };
    default_controller_axis[R_CBUTTON] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
        axis: 1,
        initial_state: 0,
    };
    default_controller_axis[L_CBUTTON] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
        axis: -1,
        initial_state: 0,
    };
    default_controller_axis[D_CBUTTON] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
        axis: 1,
        initial_state: 0,
    };
    default_controller_axis[U_CBUTTON] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
        axis: -1,
        initial_state: 0,
    };
    default_controller_buttons[R_TRIG] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER),
    };
    default_controller_buttons[L_TRIG] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER),
    };
    default_controller_axis[AXIS_LEFT] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
        axis: -1,
        initial_state: 0,
    };
    default_controller_axis[AXIS_RIGHT] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
        axis: 1,
        initial_state: 0,
    };
    default_controller_axis[AXIS_UP] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
        axis: -1,
        initial_state: 0,
    };
    default_controller_axis[AXIS_DOWN] = ui::config::InputControllerAxis {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
        axis: 1,
        initial_state: 0,
    };
    default_controller_buttons[HOTKEY] = ui::config::InputKeyButton {
        enabled: true,
        id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_BACK),
    };

    ui::config::InputProfile {
        keys: default_keys,
        controller_buttons: default_controller_buttons,
        controller_axis: default_controller_axis,
        joystick_buttons: [ui::config::InputKeyButton {
            enabled: false,
            id: 0,
        }; PROFILE_SIZE],
        joystick_hat: [ui::config::InputJoystickHat {
            enabled: false,
            id: 0,
            direction: 0,
        }; PROFILE_SIZE],
        joystick_axis: [ui::config::InputControllerAxis {
            enabled: false,
            id: 0,
            axis: 0,
            initial_state: 0,
        }; PROFILE_SIZE],
        dinput: false,
        deadzone: DEADZONE_DEFAULT,
    }
}

fn get_joysticks() -> Vec<sdl3_sys::joystick::SDL_JoystickID> {
    unsafe { sdl3_sys::events::SDL_PumpEvents() };
    let mut num_joysticks = 0;
    let sdl_joysticks = unsafe { sdl3_sys::joystick::SDL_GetJoysticks(&mut num_joysticks) };
    if !sdl_joysticks.is_null() {
        let parts =
            unsafe { std::slice::from_raw_parts(sdl_joysticks, num_joysticks as usize) }.to_vec();
        unsafe { sdl3_sys::stdinc::SDL_free(sdl_joysticks as *mut std::ffi::c_void) };
        parts
    } else {
        eprintln!("Could not get joysticks");
        vec![]
    }
}

pub fn init(ui: &mut ui::Ui) {
    ui::sdl_init(sdl3_sys::init::SDL_INIT_GAMEPAD);

    ui.input.keyboard_state =
        unsafe { sdl3_sys::keyboard::SDL_GetKeyboardState(std::ptr::null_mut()) };
    if ui.input.keyboard_state.is_null() {
        panic!("Could not get keyboard state");
    }

    for i in 0..4 {
        if let Some(controller_assignment) = &ui.config.input.controller_assignment[i]
            && ui.config.input.controller_enabled[i]
        {
            let mut joystick_id = sdl3_sys::everything::SDL_JoystickID(0);

            for joystick in get_joysticks().iter() {
                let path = if cfg!(target_os = "android") {
                    let name = if let name =
                        unsafe { sdl3_sys::joystick::SDL_GetJoystickNameForID(*joystick) }
                        && !name.is_null()
                    {
                        unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }.to_string()
                    } else {
                        UNKNOWN_CONTROLLER_NAME.to_string()
                    };

                    let vendor_id =
                        unsafe { sdl3_sys::joystick::SDL_GetJoystickVendorForID(*joystick) };
                    let product_id =
                        unsafe { sdl3_sys::joystick::SDL_GetJoystickProductForID(*joystick) };
                    Some(format!("{}:{}:{}", name, vendor_id, product_id))
                } else {
                    let path = unsafe { sdl3_sys::joystick::SDL_GetJoystickPathForID(*joystick) };
                    if !path.is_null() {
                        Some(
                            unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string(),
                        )
                    } else {
                        None
                    }
                };
                if let Some(path) = path
                    && path == *controller_assignment
                    && unsafe { sdl3_sys::joystick::SDL_GetJoystickFromID(*joystick) }.is_null()
                    && unsafe { sdl3_sys::gamepad::SDL_GetGamepadFromID(*joystick) }.is_null()
                {
                    joystick_id = *joystick;
                    break;
                }
            }

            if joystick_id != 0
                && let Some(profile) = ui
                    .config
                    .input
                    .input_profiles
                    .get(&ui.config.input.input_profile_binding[i])
            {
                if !profile.dinput {
                    let gamepad = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(joystick_id) };
                    if gamepad.is_null() {
                        eprintln!("could not connect gamepad: {}", u32::from(joystick_id))
                    } else {
                        ui.input.controllers[i].game_controller = gamepad;
                        ui.input.controllers[i].guid =
                            unsafe { sdl3_sys::gamepad::SDL_GetGamepadGUIDForID(joystick_id) };
                        let properties =
                            unsafe { sdl3_sys::gamepad::SDL_GetGamepadProperties(gamepad) };
                        if properties == 0 {
                            eprintln!("could not get gamepad properties");
                        }
                        ui.input.controllers[i].rumble = unsafe {
                            sdl3_sys::properties::SDL_GetBooleanProperty(
                                properties,
                                sdl3_sys::gamepad::SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN,
                                false,
                            )
                        };
                    }
                } else {
                    let joystick = unsafe { sdl3_sys::joystick::SDL_OpenJoystick(joystick_id) };
                    if joystick.is_null() {
                        eprintln!("could not connect joystick: {}", u32::from(joystick_id))
                    } else {
                        ui.input.controllers[i].joystick = joystick;
                        ui.input.controllers[i].guid =
                            unsafe { sdl3_sys::joystick::SDL_GetJoystickGUIDForID(joystick_id) };
                        let properties =
                            unsafe { sdl3_sys::joystick::SDL_GetJoystickProperties(joystick) };
                        if properties == 0 {
                            eprintln!("could not get joystick properties");
                        }
                        ui.input.controllers[i].rumble = unsafe {
                            sdl3_sys::properties::SDL_GetBooleanProperty(
                                properties,
                                sdl3_sys::joystick::SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN,
                                false,
                            )
                        };
                    }
                }
            } else {
                eprintln!("Could not bind assigned controller");
            }
        }
    }
}

pub fn close(ui: &mut ui::Ui) {
    for controller in ui.input.controllers.iter_mut() {
        if !controller.joystick.is_null() {
            unsafe { sdl3_sys::joystick::SDL_CloseJoystick(controller.joystick) }
            controller.joystick = std::ptr::null_mut();
        }
        if !controller.game_controller.is_null() {
            unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller.game_controller) }
            controller.game_controller = std::ptr::null_mut();
        }
    }
}
