const std = @import("std");
pub const c = @import("c.zig").c;

pub const PropertiesID = u32;

pub const SensorType = enum(c_int) {
    sensorInvalid, //Returned for an invalid sensor
    sensorUnknown, //Unknown sensor type
    sensorAccel, //Accelerometer
    sensorGyro, //Gyroscope
    sensorAccelL, //Accelerometer for left Joy-Con controller and Wii nunchuk
    sensorGyroL, //Gyroscope for left Joy-Con controller
    sensorAccelR, //Accelerometer for right Joy-Con controller
    sensorGyroR, //Gyroscope for right Joy-Con controller
    sensorCount,
};

pub const GUID = extern struct {
    data: [16]u8,
};

pub const PowerState = enum(c_int) {
    powerstateError, //error determining power status
    powerstateUnknown, //cannot determine power status
    powerstateOnBattery, //Not plugged in, running on the battery
    powerstateNoBattery, //Plugged in, no battery available
    powerstateCharging, //Plugged in, charging battery
    powerstateCharged,
};

pub const Joystick = opaque {
    pub inline fn setJoystickVirtualAxis(joystick: *Joystick, axis: c_int, value: i16) bool {
        return c.SDL_SetJoystickVirtualAxis(joystick, axis, value);
    }

    pub inline fn setJoystickVirtualBall(joystick: *Joystick, ball: c_int, xrel: i16, yrel: i16) bool {
        return c.SDL_SetJoystickVirtualBall(joystick, ball, xrel, yrel);
    }

    pub inline fn setJoystickVirtualButton(joystick: *Joystick, button: c_int, down: bool) bool {
        return c.SDL_SetJoystickVirtualButton(joystick, button, down);
    }

    pub inline fn setJoystickVirtualHat(joystick: *Joystick, hat: c_int, value: u8) bool {
        return c.SDL_SetJoystickVirtualHat(joystick, hat, value);
    }

    pub inline fn setJoystickVirtualTouchpad(joystick: *Joystick, touchpad: c_int, finger: c_int, down: bool, x: f32, y: f32, pressure: f32) bool {
        return c.SDL_SetJoystickVirtualTouchpad(joystick, touchpad, finger, down, x, y, pressure);
    }

    pub inline fn sendJoystickVirtualSensorData(joystick: *Joystick, _type: SensorType, sensor_timestamp: u64, data: *const f32, num_values: c_int) bool {
        return c.SDL_SendJoystickVirtualSensorData(joystick, @intFromEnum(_type), sensor_timestamp, @ptrCast(data), num_values);
    }

    pub inline fn getJoystickProperties(joystick: *Joystick) PropertiesID {
        return c.SDL_GetJoystickProperties(joystick);
    }

    pub inline fn getJoystickName(joystick: *Joystick) [*c]const u8 {
        return c.SDL_GetJoystickName(joystick);
    }

    pub inline fn getJoystickPath(joystick: *Joystick) [*c]const u8 {
        return c.SDL_GetJoystickPath(joystick);
    }

    pub inline fn getJoystickPlayerIndex(joystick: *Joystick) c_int {
        return c.SDL_GetJoystickPlayerIndex(joystick);
    }

    pub inline fn setJoystickPlayerIndex(joystick: *Joystick, player_index: c_int) bool {
        return c.SDL_SetJoystickPlayerIndex(joystick, player_index);
    }

    pub inline fn getJoystickGUID(joystick: *Joystick) GUID {
        return c.SDL_GetJoystickGUID(joystick);
    }

    pub inline fn getJoystickVendor(joystick: *Joystick) u16 {
        return c.SDL_GetJoystickVendor(joystick);
    }

    pub inline fn getJoystickProduct(joystick: *Joystick) u16 {
        return c.SDL_GetJoystickProduct(joystick);
    }

    pub inline fn getJoystickProductVersion(joystick: *Joystick) u16 {
        return c.SDL_GetJoystickProductVersion(joystick);
    }

    pub inline fn getJoystickFirmwareVersion(joystick: *Joystick) u16 {
        return c.SDL_GetJoystickFirmwareVersion(joystick);
    }

    pub inline fn getJoystickSerial(joystick: *Joystick) [*c]const u8 {
        return c.SDL_GetJoystickSerial(joystick);
    }

    pub inline fn getJoystickType(joystick: *Joystick) JoystickType {
        return @intFromEnum(c.SDL_GetJoystickType(joystick));
    }

    pub inline fn joystickConnected(joystick: *Joystick) bool {
        return c.SDL_JoystickConnected(joystick);
    }

    pub inline fn getJoystickID(joystick: *Joystick) JoystickID {
        return c.SDL_GetJoystickID(joystick);
    }

    pub inline fn getNumJoystickAxes(joystick: *Joystick) c_int {
        return c.SDL_GetNumJoystickAxes(joystick);
    }

    pub inline fn getNumJoystickBalls(joystick: *Joystick) c_int {
        return c.SDL_GetNumJoystickBalls(joystick);
    }

    pub inline fn getNumJoystickHats(joystick: *Joystick) c_int {
        return c.SDL_GetNumJoystickHats(joystick);
    }

    pub inline fn getNumJoystickButtons(joystick: *Joystick) c_int {
        return c.SDL_GetNumJoystickButtons(joystick);
    }

    pub inline fn getJoystickAxis(joystick: *Joystick, axis: c_int) i16 {
        return c.SDL_GetJoystickAxis(joystick, axis);
    }

    pub inline fn getJoystickAxisInitialState(joystick: *Joystick, axis: c_int, state: *i16) bool {
        return c.SDL_GetJoystickAxisInitialState(joystick, axis, @ptrCast(state));
    }

    pub inline fn getJoystickBall(joystick: *Joystick, ball: c_int, dx: *c_int, dy: *c_int) bool {
        return c.SDL_GetJoystickBall(joystick, ball, @ptrCast(dx), @ptrCast(dy));
    }

    pub inline fn getJoystickHat(joystick: *Joystick, hat: c_int) u8 {
        return c.SDL_GetJoystickHat(joystick, hat);
    }

    pub inline fn getJoystickButton(joystick: *Joystick, button: c_int) bool {
        return c.SDL_GetJoystickButton(joystick, button);
    }

    pub inline fn rumbleJoystick(joystick: *Joystick, low_frequency_rumble: u16, high_frequency_rumble: u16, duration_ms: u32) bool {
        return c.SDL_RumbleJoystick(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
    }

    pub inline fn rumbleJoystickTriggers(joystick: *Joystick, left_rumble: u16, right_rumble: u16, duration_ms: u32) bool {
        return c.SDL_RumbleJoystickTriggers(joystick, left_rumble, right_rumble, duration_ms);
    }

    pub inline fn setJoystickLED(joystick: *Joystick, red: u8, green: u8, blue: u8) bool {
        return c.SDL_SetJoystickLED(joystick, red, green, blue);
    }

    pub inline fn sendJoystickEffect(joystick: *Joystick, data: ?*const anyopaque, size: c_int) bool {
        return c.SDL_SendJoystickEffect(joystick, data, size);
    }

    pub inline fn closeJoystick(joystick: *Joystick) void {
        return c.SDL_CloseJoystick(joystick);
    }

    pub inline fn getJoystickConnectionState(joystick: *Joystick) JoystickConnectionState {
        return c.SDL_GetJoystickConnectionState(joystick);
    }

    pub inline fn getJoystickPowerInfo(joystick: *Joystick, percent: *c_int) PowerState {
        return c.SDL_GetJoystickPowerInfo(joystick, @ptrCast(percent));
    }
};

pub const JoystickID = u32;

pub const JoystickType = enum(c_int) {
    joystickTypeUnknown,
    joystickTypeGamepad,
    joystickTypeWheel,
    joystickTypeArcadeStick,
    joystickTypeFlightStick,
    joystickTypeDancePad,
    joystickTypeGuitar,
    joystickTypeDrumKit,
    joystickTypeArcadePad,
    joystickTypeThrottle,
    joystickTypeCount,
};

pub const JoystickConnectionState = enum(c_int) {
    joystickConnectionUnknown,
    joystickConnectionWired,
    joystickConnectionWireless,
};

pub inline fn lockJoysticks() void {
    return c.SDL_LockJoysticks();
}

pub inline fn unlockJoysticks() void {
    return c.SDL_UnlockJoysticks();
}

pub inline fn hasJoystick() bool {
    return c.SDL_HasJoystick();
}

pub inline fn getJoysticks(count: *c_int) ?*JoystickID {
    return c.SDL_GetJoysticks(@ptrCast(count));
}

pub inline fn getJoystickNameForID(instance_id: JoystickID) [*c]const u8 {
    return c.SDL_GetJoystickNameForID(instance_id);
}

pub inline fn getJoystickPathForID(instance_id: JoystickID) [*c]const u8 {
    return c.SDL_GetJoystickPathForID(instance_id);
}

pub inline fn getJoystickPlayerIndexForID(instance_id: JoystickID) c_int {
    return c.SDL_GetJoystickPlayerIndexForID(instance_id);
}

pub inline fn getJoystickGUIDForID(instance_id: JoystickID) GUID {
    return c.SDL_GetJoystickGUIDForID(instance_id);
}

pub inline fn getJoystickVendorForID(instance_id: JoystickID) u16 {
    return c.SDL_GetJoystickVendorForID(instance_id);
}

pub inline fn getJoystickProductForID(instance_id: JoystickID) u16 {
    return c.SDL_GetJoystickProductForID(instance_id);
}

pub inline fn getJoystickProductVersionForID(instance_id: JoystickID) u16 {
    return c.SDL_GetJoystickProductVersionForID(instance_id);
}

pub inline fn getJoystickTypeForID(instance_id: JoystickID) JoystickType {
    return @intFromEnum(c.SDL_GetJoystickTypeForID(instance_id));
}

pub inline fn openJoystick(instance_id: JoystickID) ?*Joystick {
    return c.SDL_OpenJoystick(instance_id);
}

pub inline fn getJoystickFromID(instance_id: JoystickID) ?*Joystick {
    return c.SDL_GetJoystickFromID(instance_id);
}

pub inline fn getJoystickFromPlayerIndex(player_index: c_int) ?*Joystick {
    return c.SDL_GetJoystickFromPlayerIndex(player_index);
}

pub const VirtualJoystickTouchpadDesc = extern struct {
    nfingers: u16, // the number of simultaneous fingers on this touchpad
    padding: [3]u16,
};

pub const VirtualJoystickSensorDesc = extern struct {
    _type: SensorType, // the type of this sensor
    rate: f32, // the update frequency of this sensor, may be 0.0f
};

pub const VirtualJoystickDesc = extern struct {
    version: u32, // the version of this interface
    _type: u16, // `SDL_JoystickType`
    padding: u16, // unused
    vendor_id: u16, // the USB vendor ID of this joystick
    product_id: u16, // the USB product ID of this joystick
    naxes: u16, // the number of axes on this joystick
    nbuttons: u16, // the number of buttons on this joystick
    nballs: u16, // the number of balls on this joystick
    nhats: u16, // the number of hats on this joystick
    ntouchpads: u16, // the number of touchpads on this joystick, requires `touchpads` to point at valid descriptions
    nsensors: u16, // the number of sensors on this joystick, requires `sensors` to point at valid descriptions
    padding2: [2]u16, // unused
    name: [*c]const u8, // the name of the joystick
    touchpads: *const VirtualJoystickTouchpadDesc, // A pointer to an array of touchpad descriptions, required if `ntouchpads` is > 0
    sensors: *const VirtualJoystickSensorDesc, // A pointer to an array of sensor descriptions, required if `nsensors` is > 0
    userdata: ?*anyopaque, // User data pointer passed to callbacks
    Update: ?*const anyopaque, // Called when the joystick state should be updated
    SetPlayerIndex: ?*const anyopaque, // Called when the player index is set
    Rumble: ?*const anyopaque, // Implements SDL_RumbleJoystick()
    RumbleTriggers: ?*const anyopaque, // Implements SDL_RumbleJoystickTriggers()
    SetLED: ?*const anyopaque, // Implements SDL_SetJoystickLED()
    SendEffect: ?*const anyopaque, // Implements SDL_SendJoystickEffect()
    SetSensorsEnabled: ?*const anyopaque, // Implements SDL_SetGamepadSensorEnabled()
    Cleanup: ?*const anyopaque, // Cleans up the userdata when the joystick is detached
};

pub inline fn attachVirtualJoystick(desc: *const VirtualJoystickDesc) JoystickID {
    return c.SDL_AttachVirtualJoystick(@ptrCast(desc));
}

pub inline fn detachVirtualJoystick(instance_id: JoystickID) bool {
    return c.SDL_DetachVirtualJoystick(instance_id);
}

pub inline fn isJoystickVirtual(instance_id: JoystickID) bool {
    return c.SDL_IsJoystickVirtual(instance_id);
}

pub inline fn getJoystickGUIDInfo(guid: GUID, vendor: *u16, product: *u16, version: *u16, crc16: *u16) void {
    return c.SDL_GetJoystickGUIDInfo(guid, @ptrCast(vendor), @ptrCast(product), @ptrCast(version), @ptrCast(crc16));
}

pub inline fn setJoystickEventsEnabled(enabled: bool) void {
    return c.SDL_SetJoystickEventsEnabled(enabled);
}

pub inline fn joystickEventsEnabled() bool {
    return c.SDL_JoystickEventsEnabled();
}

pub inline fn updateJoysticks() void {
    return c.SDL_UpdateJoysticks();
}
