// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "hid_core/hid_types.h"

namespace Core::HID {
class EmulatedController;
}

namespace Service::HID {
static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
static constexpr std::size_t StyleIndexCount = 7;

// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
    Vertical = 0,
    Horizontal = 1,
};

// This is nn::hid::NpadJoyAssignmentMode
enum class NpadJoyAssignmentMode : u32 {
    Dual = 0,
    Single = 1,
};

// This is nn::hid::NpadJoyDeviceType
enum class NpadJoyDeviceType : s64 {
    Left = 0,
    Right = 1,
};

// This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
    Dual = 0,
    Single = 1,
    None = 2,
    MaxActivationMode = 3,
};

// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
    INSERT_PADDING_BYTES(0x4);
};

// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
    None = 0,
    HandheldNone = 1,
    HandheldJoyConLeftOnly = 2,
    HandheldJoyConRightOnly = 3,
    HandheldJoyConLeftJoyConRight = 4,
    JoyDual = 5,
    JoyDualLeftOnly = 6,
    JoyDualRightOnly = 7,
    JoyLeftHorizontal = 8,
    JoyLeftVertical = 9,
    JoyRightHorizontal = 10,
    JoyRightVertical = 11,
    SwitchProController = 12,
    CompatibleProController = 13,
    CompatibleJoyCon = 14,
    LarkHvc1 = 15,
    LarkHvc2 = 16,
    LarkNesLeft = 17,
    LarkNesRight = 18,
    Lucia = 19,
    Verification = 20,
    Lagon = 21,
};

using AppletFooterUiVariant = u8;

// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
    AppletFooterUiVariant ui_variant;
    INSERT_PADDING_BYTES(0x2);
    AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
    Mode_5ms = 0,
    Mode_10ms = 1,
    Mode_15ms = 2,
    Default = 3,
};

enum class NpadRevision : u32 {
    Revision0 = 0,
    Revision1 = 1,
    Revision2 = 2,
    Revision3 = 3,
};

// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
    Ok = 0,
    ReadError = 1,
    NoController = 2,
};
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");

// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
    ColorAttribute attribute{ColorAttribute::NoController};
    Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");

// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
    ColorAttribute attribute{ColorAttribute::NoController};
    Core::HID::NpadControllerColor left{};
    Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");

// This is nn::hid::NpadAttribute
struct NpadAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_connected;
        BitField<1, 1, u32> is_wired;
        BitField<2, 1, u32> is_left_connected;
        BitField<3, 1, u32> is_left_wired;
        BitField<4, 1, u32> is_right_connected;
        BitField<5, 1, u32> is_right_wired;
    };
};
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");

// This is nn::hid::NpadFullKeyState
// This is nn::hid::NpadHandheldState
// This is nn::hid::NpadJoyDualState
// This is nn::hid::NpadJoyLeftState
// This is nn::hid::NpadJoyRightState
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
    s64_le sampling_number{};
    Core::HID::NpadButtonState npad_buttons{};
    Core::HID::AnalogStickState l_stick{};
    Core::HID::AnalogStickState r_stick{};
    NpadAttribute connection_status{};
    INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");

// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
    s64 sampling_number{};
    s32 l_analog{};
    s32 r_analog{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");

// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
    union {
        s64 raw{};
        BitField<0, 1, s64> is_charging_joy_dual;
        BitField<1, 1, s64> is_charging_joy_left;
        BitField<2, 1, s64> is_charging_joy_right;
        BitField<3, 1, s64> is_powered_joy_dual;
        BitField<4, 1, s64> is_powered_joy_left;
        BitField<5, 1, s64> is_powered_joy_right;
        BitField<9, 1, s64> is_system_unsupported_button;
        BitField<10, 1, s64> is_system_ext_unsupported_button;
        BitField<11, 1, s64> is_vertical;
        BitField<12, 1, s64> is_horizontal;
        BitField<13, 1, s64> use_plus;
        BitField<14, 1, s64> use_minus;
        BitField<15, 1, s64> use_directional_buttons;
    };
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");

// This is nn::hid::NpadSystemButtonProperties
struct NpadSystemButtonProperties {
    union {
        s32 raw{};
        BitField<0, 1, s32> is_home_button_protection_enabled;
    };
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");

// This is nn::hid::system::DeviceType
struct DeviceType {
    union {
        u32 raw{};
        BitField<0, 1, s32> fullkey;
        BitField<1, 1, s32> debug_pad;
        BitField<2, 1, s32> handheld_left;
        BitField<3, 1, s32> handheld_right;
        BitField<4, 1, s32> joycon_left;
        BitField<5, 1, s32> joycon_right;
        BitField<6, 1, s32> palma;
        BitField<7, 1, s32> lark_hvc_left;
        BitField<8, 1, s32> lark_hvc_right;
        BitField<9, 1, s32> lark_nes_left;
        BitField<10, 1, s32> lark_nes_right;
        BitField<11, 1, s32> handheld_lark_hvc_left;
        BitField<12, 1, s32> handheld_lark_hvc_right;
        BitField<13, 1, s32> handheld_lark_nes_left;
        BitField<14, 1, s32> handheld_lark_nes_right;
        BitField<15, 1, s32> lucia;
        BitField<16, 1, s32> lagon;
        BitField<17, 1, s32> lager;
        BitField<31, 1, s32> system;
    };
};

// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
    u64 handle{};
    bool is_available{};
    bool is_activated{};
    INSERT_PADDING_BYTES(0x6); // Reserved
    u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
              "NfcXcdDeviceHandleStateImpl is an invalid size");

// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
    Invalid,
    H1,
    H2,
    NL,
    NR,
};

// This is nn::hid::NpadLuciaType
enum class NpadLuciaType : u32 {
    Invalid,
    J,
    E,
    U,
};

// This is nn::hid::NpadLagonType
enum class NpadLagonType : u32 {
    Invalid,
};

// This is nn::hid::NpadLagerType
enum class NpadLagerType : u32 {
    Invalid,
    J,
    E,
    U,
};

// nn::hidtypes::FeatureType
struct FeatureType {
    union {
        u64 raw{};
        BitField<0, 1, u64> has_left_analog_stick;
        BitField<1, 1, u64> has_right_analog_stick;
        BitField<2, 1, u64> has_left_joy_six_axis_sensor;
        BitField<3, 1, u64> has_right_joy_six_axis_sensor;
        BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor;
        BitField<5, 1, u64> has_left_lra_vibration_device;
        BitField<6, 1, u64> has_right_lra_vibration_device;
        BitField<7, 1, u64> has_gc_vibration_device;
        BitField<8, 1, u64> has_erm_vibration_device;
        BitField<9, 1, u64> has_left_joy_rail_bus;
        BitField<10, 1, u64> has_right_joy_rail_bus;
        BitField<11, 1, u64> has_internal_bus;
        BitField<12, 1, u64> is_palma;
        BitField<13, 1, u64> has_nfc;
        BitField<14, 1, u64> has_ir_sensor;
        BitField<15, 1, u64> is_analog_stick_calibration_supported;
        BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported;
        BitField<17, 1, u64> has_left_right_joy_battery;
        BitField<18, 1, u64> has_fullkey_battery;
        BitField<19, 1, u64> is_disconnect_controller_if_battery_none;
        BitField<20, 1, u64> has_controller_color;
        BitField<21, 1, u64> has_grip_color;
        BitField<22, 1, u64> has_identification_code;
        BitField<23, 1, u64> has_bluetooth_address;
        BitField<24, 1, u64> has_mcu;
        BitField<25, 1, u64> has_notification_led;
        BitField<26, 1, u64> has_directional_buttons;
        BitField<27, 1, u64> has_indicator_led;
        BitField<28, 1, u64> is_button_config_embedded_supported;
        BitField<29, 1, u64> is_button_config_full_supported;
        BitField<30, 1, u64> is_button_config_left_supported;
        BitField<31, 1, u64> is_button_config_right_supported;
        BitField<32, 1, u64> is_usb_hid_device;
        BitField<33, 1, u64> is_kuina_device;
        BitField<34, 1, u64> is_direct_usb_to_bt_switching_device;
        BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross;
    };
};
static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size");

// This is nn::hid::AssignmentStyle
struct AssignmentStyle {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_external_assigned;
        BitField<1, 1, u32> is_external_left_assigned;
        BitField<2, 1, u32> is_external_right_assigned;
        BitField<3, 1, u32> is_handheld_assigned;
        BitField<4, 1, u32> is_handheld_left_assigned;
        BitField<5, 1, u32> is_handheld_right_assigned;
    };
};
static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size");

// This is nn::hid::server::IAbstractedPad::InternalFlags
struct InternalFlags {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_bound;
        BitField<1, 1, u32> is_connected;
        BitField<2, 1, u32> is_battery_low_ovln_required;
        BitField<3, 1, u32> is_battery_low_ovln_delay_required;
        BitField<4, 1, u32> is_sample_received;
        BitField<5, 1, u32> is_virtual_input;
        BitField<6, 1, u32> is_wired;
        BitField<8, 1, u32> use_center_clamp;
        BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration;
        BitField<10, 1, u32> has_virtual_six_axis_sensor_angle;
        BitField<11, 1, u32> is_debug_pad;
    };
};
static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size");

/// This is nn::hid::server::IAbstractedPad
struct IAbstractedPad {
    InternalFlags internal_flags;
    u64 controller_id;
    u32 controller_number;
    u64 low_battery_display_delay_time;
    u64 low_battery_display_delay_interval;
    FeatureType feature_set;
    FeatureType disabled_feature_set;
    AssignmentStyle assignment_style;
    Core::HID::NpadStyleIndex device_type;
    Core::HID::NpadInterfaceType interface_type;
    Core::HID::NpadPowerInfo power_info;
    u32 pad_state;
    u32 button_mask;
    u32 system_button_mask;
    u8 indicator;
    std::vector<f32> virtual_six_axis_sensor_acceleration;
    std::vector<f32> virtual_six_axis_sensor_angle;
    Core::HID::EmulatedController* xcd_handle;
    u64 color;
};
} // namespace Service::HID
