/**
 * @file hidbus.h
 * @brief hidbus service IPC wrapper, for using external devices attached to HID controllers. See also: https://switchbrew.org/wiki/HID_services#hidbus
 * @note Only available on [5.0.0+].
 * @author yellows8
 */
#pragma once
#include "../types.h"
#include "../kernel/event.h"
#include "../services/hid.h"
#include "../sf/service.h"

/// BusType
typedef enum {
    HidbusBusType_JoyLeftRail   = 0,          ///< JoyLeftRail
    HidbusBusType_JoyRightRail  = 1,          ///< JoyRightRail
    HidbusBusType_LarkRightRail = 2,          ///< [6.0.0+] LarkRightRail (for microphone).
} HidbusBusType;

/// JoyPollingMode
typedef enum {
    HidbusJoyPollingMode_JoyDisableSixAxisPollingData = 0,    ///< JoyDisableSixAxisPollingData
    HidbusJoyPollingMode_JoyEnableSixAxisPollingData  = 1,    ///< JoyEnableSixAxisPollingData
    HidbusJoyPollingMode_JoyButtonOnlyPollingData     = 2,    ///< [6.0.0+] JoyButtonOnlyPollingData
} HidbusJoyPollingMode;

/// BusHandle
typedef struct {
    u32 abstracted_pad_id;         ///< AbstractedPadId
    u8 internal_index;             ///< InternalIndex
    u8 player_number;              ///< PlayerNumber
    u8 bus_type_id;                ///< BusTypeId
    u8 is_valid;                   ///< IsValid
} HidbusBusHandle;

/// JoyPollingReceivedData
typedef struct {
    u8 data[0x30];                 ///< Data.
    u64 size;                      ///< Size of data.
    u64 timestamp;                 ///< Timestamp.
} HidbusJoyPollingReceivedData;

/// HidbusDataAccessorHeader
typedef struct {
    Result res;                                ///< Result.
    u32 pad;                                   ///< Padding.
    u8 unused[0x18];                           ///< Initialized sysmodule-side, not used by sdknso.
    u64 latest_entry;                          ///< Latest entry.
    u64 total_entries;                         ///< Total entries.
} HidbusDataAccessorHeader;

/// HidbusJoyDisableSixAxisPollingDataAccessorEntryData
typedef struct {
    u8 data[0x26];                             ///< Data.
    u8 size;                                   ///< Size of data.
    u8 pad;                                    ///< Padding.
    u64 timestamp;                             ///< Timestamp.
} HidbusJoyDisableSixAxisPollingDataAccessorEntryData;

/// HidbusJoyDisableSixAxisPollingDataAccessorEntry
typedef struct {
    u64 timestamp;                                               ///< Timestamp.
    HidbusJoyDisableSixAxisPollingDataAccessorEntryData data;    ///< \ref HidbusJoyDisableSixAxisPollingDataAccessorEntryData
} HidbusJoyDisableSixAxisPollingDataAccessorEntry;

/// HidbusJoyEnableSixAxisPollingDataAccessorEntryData
typedef struct {
    u8 data[0x8];                              ///< Data.
    u8 size;                                   ///< Size of data.
    u8 pad[7];                                 ///< Padding.
    u64 timestamp;                             ///< Timestamp.
} HidbusJoyEnableSixAxisPollingDataAccessorEntryData;

/// HidbusJoyEnableSixAxisPollingDataAccessorEntry
typedef struct {
    u64 timestamp;                                               ///< Timestamp.
    HidbusJoyEnableSixAxisPollingDataAccessorEntryData data;     ///< \ref HidbusJoyEnableSixAxisPollingDataAccessorEntryData
} HidbusJoyEnableSixAxisPollingDataAccessorEntry;

/// HidbusJoyButtonOnlyPollingDataAccessorEntryData
typedef struct {
    u8 data[0x2c];                             ///< Data.
    u8 size;                                   ///< Size of data.
    u8 pad[3];                                 ///< Padding.
    u64 timestamp;                             ///< Timestamp.
} HidbusJoyButtonOnlyPollingDataAccessorEntryData;

/// HidbusJoyButtonOnlyPollingDataAccessorEntry
typedef struct {
    u64 timestamp;                                               ///< Timestamp.
    HidbusJoyButtonOnlyPollingDataAccessorEntryData data;        ///< \ref HidbusJoyEnableSixAxisPollingDataAccessorEntryData
} HidbusJoyButtonOnlyPollingDataAccessorEntry;

/// HidbusJoyDisableSixAxisPollingDataAccessor
typedef struct {
    HidbusDataAccessorHeader hdr;                                    ///< \ref HidbusDataAccessorHeader
    HidbusJoyDisableSixAxisPollingDataAccessorEntry entries[0xb];    ///< \ref HidbusJoyDisableSixAxisPollingDataAccessorEntry
} HidbusJoyDisableSixAxisPollingDataAccessor;

/// HidbusJoyEnableSixAxisPollingDataAccessor
typedef struct {
    HidbusDataAccessorHeader hdr;                                    ///< \ref HidbusDataAccessorHeader
    HidbusJoyEnableSixAxisPollingDataAccessorEntry entries[0xb];     ///< \ref HidbusJoyEnableSixAxisPollingDataAccessorEntry
} HidbusJoyEnableSixAxisPollingDataAccessor;

/// HidbusJoyButtonOnlyPollingDataAccessor
typedef struct {
    HidbusDataAccessorHeader hdr;                                    ///< \ref HidbusDataAccessorHeader
    HidbusJoyButtonOnlyPollingDataAccessorEntry entries[0xb];        ///< \ref HidbusJoyButtonOnlyPollingDataAccessorEntry
} HidbusJoyButtonOnlyPollingDataAccessor;

/// Common data for HidbusStatusManagerEntry*.
typedef struct {
    u8 flag_x0;                    ///< Flag.
    u8 pad[3];                     ///< Padding.
    Result res;                    ///< Result.
    u8 device_enabled;             ///< Flag indicating whether a device is enabled (\ref hidbusEnableExternalDevice).
    u8 is_valid;                   ///< Flag indicating whether this entry is valid.
    u8 polling_enabled;            ///< Flag indicating whether polling is enabled (\ref hidbusEnableJoyPollingReceiveMode).
    u8 unk_xb;                     ///< Unknown / padding?
    u32 polling_mode;              ///< \ref HidbusJoyPollingMode
} HidbusStatusManagerEntryCommon;

/// HidbusStatusManagerEntry on 5.x.
typedef struct {
    HidbusStatusManagerEntryCommon common;       ///< \ref HidbusStatusManagerEntryCommon
    u8 unk_x10[0xf0];                            ///< Ignored by official sw.
} HidbusStatusManagerEntryV5;

/// HidbusStatusManagerEntry
typedef struct {
    HidbusStatusManagerEntryCommon common;       ///< \ref HidbusStatusManagerEntryCommon
    u8 unk_x10[0x70];                            ///< Ignored by official sw.
} HidbusStatusManagerEntry;

/// StatusManager on 5.x.
typedef struct {
    HidbusStatusManagerEntryV5 entries[0x10];    ///< \ref HidbusStatusManagerEntryV5
} HidbusStatusManagerV5;

/// StatusManager
typedef struct {
    HidbusStatusManagerEntry entries[0x13];      ///< \ref HidbusStatusManagerEntry
    u8 unused[0x680];                            ///< Unused.
} HidbusStatusManager;

/// Gets the Service object for the actual hidbus service session. This object must be closed by the user once finished using cmds with this.
Result hidbusGetServiceSession(Service* srv_out);

/// Gets the SharedMemory addr (\ref HidbusStatusManagerV5 on 5.x, otherwise \ref HidbusStatusManager). Only valid when at least one BusHandle is currently initialized (\ref hidbusInitialize).
void* hidbusGetSharedmemAddr(void);

/**
 * @brief GetBusHandle
 * @param[out] handle \ref HidbusBusHandle
 * @param[out] flag Output flag indicating whether the handle is valid.
 * @param[in] id \ref HidControllerID
 * @param[in] bus_type \ref HidbusBusType
 */
Result hidbusGetBusHandle(HidbusBusHandle *handle, bool *flag, HidControllerID id, HidbusBusType bus_type);

/**
 * @brief Initialize
 * @param[in] handle \ref HidbusBusHandle
 */
Result hidbusInitialize(HidbusBusHandle handle);

/**
 * @brief Finalize
 * @param[in] handle \ref HidbusBusHandle
 */
Result hidbusFinalize(HidbusBusHandle handle);

/**
 * @brief EnableExternalDevice
 * @note This uses \ref hidLaShowControllerFirmwareUpdate if needed.
 * @param[in] handle \ref HidbusBusHandle
 * @param[in] flag Whether to enable the device (true = enable, false = disable). When false, this will internally use \ref hidbusDisableJoyPollingReceiveMode if needed.
 * @param[in] device_id ExternalDeviceId which must match the connected device. Only used when flag is set.
 */
Result hidbusEnableExternalDevice(HidbusBusHandle handle, bool flag, u32 device_id);

/**
 * @brief SendAndReceive
 * @param[in] handle \ref HidbusBusHandle
 * @param[in] inbuf Input buffer, containing the command data.
 * @param[in] inbuf_size Input buffer size, must be <0x26.
 * @param[out] outbuf Output buffer, containing the command reply data.
 * @param[in] outbuf_size Output buffer max size.
 * @param[out] out_size Actual output size.
 */
Result hidbusSendAndReceive(HidbusBusHandle handle, const void* inbuf, size_t inbuf_size, void* outbuf, size_t outbuf_size, u64 *out_size);

/**
 * @brief EnableJoyPollingReceiveMode
 * @param[in] handle \ref HidbusBusHandle
 * @param[in] inbuf Input buffer, containing the command data.
 * @param[in] inbuf_size Input buffer size, must be <0x26.
 * @param[out] workbuf TransferMemory buffer, must be 0x1000-byte aligned. This buffer must not be written to until after \ref hidbusDisableJoyPollingReceiveMode is used.
 * @param[in] workbuf_size TransferMemory buffer size, must be 0x1000-byte aligned.
 * @param[in] polling_mode \ref HidbusJoyPollingMode
 */
Result hidbusEnableJoyPollingReceiveMode(HidbusBusHandle handle, const void* inbuf, size_t inbuf_size, void* workbuf, size_t workbuf_size, HidbusJoyPollingMode polling_mode);

/**
 * @brief DisableJoyPollingReceiveMode
 * @note This can also be used via \ref hidbusEnableExternalDevice with flag=false.
 * @param[in] handle \ref HidbusBusHandle
 */
Result hidbusDisableJoyPollingReceiveMode(HidbusBusHandle handle);

/**
 * @brief GetJoyPollingReceivedData
 * @param[in] handle \ref HidbusBusHandle
 * @param[out] recv_data Output array of \ref HidbusJoyPollingReceivedData.
 * @param[in] count Total entries for the recv_data array. The maximum is 0xa. Official apps use range 0x1-0x9.
 */
Result hidbusGetJoyPollingReceivedData(HidbusBusHandle handle, HidbusJoyPollingReceivedData *recv_data, s32 count);