/*
 * Private winebth.sys defs
 *
 * Copyright 2024 Vibhav Pant
 * Copyright 2025 Vibhav Pant
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef __WINE_WINEBTH_WINEBTH_H_
#define __WINE_WINEBTH_WINEBTH_H_

#include <stdint.h>

#include <bthsdpdef.h>
#include <bthledef.h>
#include <bluetoothapis.h>
#include <bthdef.h>
#include <ddk/wdm.h>

#include <wine/debug.h>

#ifdef __ASM_USE_FASTCALL_WRAPPER
extern void * WINAPI wrap_fastcall_func1(void *func, const void *a);
__ASM_STDCALL_FUNC(wrap_fastcall_func1, 8,
                   "popl %ecx\n\t"
                   "popl %eax\n\t"
                   "xchgl (%esp),%ecx\n\t"
                   "jmp *%eax");
#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
#else
#define call_fastcall_func1(func,a) func(a)
#endif
struct string_buffer
{
    WCHAR *string;
    size_t len;
};

#define XX(i) case (i): return #i

static inline const char *debugstr_BUS_QUERY_ID_TYPE( BUS_QUERY_ID_TYPE type )
{
    switch (type)
    {
        XX(BusQueryDeviceID);
        XX(BusQueryHardwareIDs);
        XX(BusQueryCompatibleIDs);
        XX(BusQueryInstanceID);
        XX(BusQueryDeviceSerialNumber);
        XX(BusQueryContainerID);
    default:
        return wine_dbg_sprintf( "(unknown %d)", type );
    }
}

static inline const char *debugstr_minor_function_code( UCHAR code )
{
    switch(code)
    {
        XX(IRP_MN_START_DEVICE);
        XX(IRP_MN_QUERY_REMOVE_DEVICE);
        XX(IRP_MN_REMOVE_DEVICE);
        XX(IRP_MN_CANCEL_REMOVE_DEVICE);
        XX(IRP_MN_STOP_DEVICE);
        XX(IRP_MN_QUERY_STOP_DEVICE);
        XX(IRP_MN_CANCEL_STOP_DEVICE);
        XX(IRP_MN_QUERY_DEVICE_RELATIONS);
        XX(IRP_MN_QUERY_INTERFACE);
        XX(IRP_MN_QUERY_CAPABILITIES);
        XX(IRP_MN_QUERY_RESOURCES);
        XX(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
        XX(IRP_MN_QUERY_DEVICE_TEXT);
        XX(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
        XX(IRP_MN_READ_CONFIG);
        XX(IRP_MN_WRITE_CONFIG);
        XX(IRP_MN_EJECT);
        XX(IRP_MN_SET_LOCK);
        XX(IRP_MN_QUERY_ID);
        XX(IRP_MN_QUERY_PNP_DEVICE_STATE);
        XX(IRP_MN_QUERY_BUS_INFORMATION);
        XX(IRP_MN_DEVICE_USAGE_NOTIFICATION);
        XX(IRP_MN_SURPRISE_REMOVAL);
        XX(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
        default:
            return wine_dbg_sprintf( "(unknown %#x)", code );
    }
}
#undef XX

static inline void uuid_to_le( const GUID *uuid, BTH_LE_UUID *le_uuid )
{
    const GUID le_base_uuid = { 0, 0, 0x1000, { 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } };

    if (uuid->Data1 <= UINT16_MAX && uuid->Data2 == le_base_uuid.Data2
        && uuid->Data3 == le_base_uuid.Data3
        && !memcmp( uuid->Data4, le_base_uuid.Data4, sizeof( uuid->Data4 ) ))
    {
        le_uuid->IsShortUuid = TRUE;
        le_uuid->Value.ShortUuid = uuid->Data1;
    }
    else
    {
        le_uuid->IsShortUuid = FALSE;
        le_uuid->Value.LongUuid = *uuid;
    }
}

/* Undocumented device properties for Bluetooth radios. */
#define DEFINE_BTH_RADIO_DEVPROPKEY( d, i )                                                        \
    DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27,   \
                       0xb6, 0x8a, 0xa5, 0xa2, 0xeb, (i) )

DEFINE_BTH_RADIO_DEVPROPKEY( Address, 1 );                         /* DEVPROP_TYPE_UINT64 */
DEFINE_BTH_RADIO_DEVPROPKEY( Manufacturer, 2 );                    /* DEVPROP_TYPE_UINT16 */
DEFINE_BTH_RADIO_DEVPROPKEY( LMPSupportedFeatures, 3 );            /* DEVPROP_TYPE_UINT64 */
DEFINE_BTH_RADIO_DEVPROPKEY( LMPVersion, 4 );                      /* DEVPROP_TYPE_BYTE */
DEFINE_BTH_RADIO_DEVPROPKEY( HCIVendorFeatures, 8 );               /* DEVPROP_TYPE_UINT64 */
DEFINE_BTH_RADIO_DEVPROPKEY( MaximumAdvertisementDataLength, 17 ); /* DEVPROP_TYPE_UINT16 */
DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 );       /* DEVPROP_TYPE_UINT64 */

typedef struct
{
    UINT_PTR handle;
} winebluetooth_radio_t;

typedef UINT16 winebluetooth_radio_props_mask_t;

#define WINEBLUETOOTH_RADIO_PROPERTY_NAME         (1)
#define WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS      (1 << 2)
#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE (1 << 3)
#define WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE  (1 << 4)
#define WINEBLUETOOTH_RADIO_PROPERTY_CLASS        (1 << 5)
#define WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER (1 << 6)
#define WINEBLUETOOTH_RADIO_PROPERTY_VERSION      (1 << 7)
#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING  (1 << 8)
#define WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE     (1 << 9)

#define WINEBLUETOOTH_RADIO_ALL_PROPERTIES                                                         \
    (WINEBLUETOOTH_RADIO_PROPERTY_NAME | WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS |                    \
     WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE | WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE |        \
     WINEBLUETOOTH_RADIO_PROPERTY_CLASS | WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER |              \
     WINEBLUETOOTH_RADIO_PROPERTY_VERSION | WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING |             \
     WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE)

typedef struct
{
    UINT_PTR handle;
} winebluetooth_device_t;

typedef UINT16 winebluetooth_device_props_mask_t;

#define WINEBLUETOOTH_DEVICE_PROPERTY_NAME           (1)
#define WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS        (1 << 1)
#define WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED      (1 << 2)
#define WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED         (1 << 3)
#define WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING (1 << 4)
#define WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED        (1 << 5)
#define WINEBLUETOOTH_DEVICE_PROPERTY_CLASS          (1 << 6)

#define WINEBLUETOOTH_DEVICE_ALL_PROPERTIES                                                 \
    (WINEBLUETOOTH_DEVICE_PROPERTY_NAME | WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS |           \
     WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED | WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED |       \
     WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING | WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED | \
     WINEBLUETOOTH_DEVICE_PROPERTY_CLASS)

union winebluetooth_property
{
    BOOL boolean;
    ULONG ulong;
    BLUETOOTH_ADDRESS address;
    WCHAR name[BLUETOOTH_MAX_NAME_SIZE];
};

struct winebluetooth_radio_properties
{
    BOOL discoverable;
    BOOL connectable;
    BOOL discovering;
    BOOL pairable;
    BLUETOOTH_ADDRESS address;
    CHAR name[BLUETOOTH_MAX_NAME_SIZE];
    ULONG class;
    USHORT manufacturer;
    BYTE version;
};

struct winebluetooth_device_properties
{
    BLUETOOTH_ADDRESS address;
    CHAR name[BLUETOOTH_MAX_NAME_SIZE];
    BOOL connected;
    BOOL paired;
    BOOL legacy_pairing;
    BOOL trusted;
    UINT32 class;
};

typedef struct
{
    UINT_PTR handle;
} winebluetooth_gatt_service_t;

typedef struct
{
    UINT_PTR handle;
} winebluetooth_gatt_characteristic_t;

NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name,
                                              SIZE_T *size );
void winebluetooth_radio_free( winebluetooth_radio_t radio );
void winebluetooth_radio_dup( winebluetooth_radio_t radio );
static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, winebluetooth_radio_t r2 )
{
    return r1.handle == r2.handle;
}
NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio,
                                           ULONG prop_flag,
                                           union winebluetooth_property *property );
NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio );
NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio );
NTSTATUS winebluetooth_radio_remove_device( winebluetooth_radio_t radio, winebluetooth_device_t device );
NTSTATUS winebluetooth_auth_agent_enable_incoming( void );

void winebluetooth_device_free( winebluetooth_device_t device );
void winebluetooth_device_dup( winebluetooth_device_t device );
static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, winebluetooth_device_t d2 )
{
    return d1.handle == d2.handle;
}
void winebluetooth_device_properties_to_info( winebluetooth_device_props_mask_t props_mask,
                                              const struct winebluetooth_device_properties *props,
                                              BTH_DEVICE_INFO *info );
NTSTATUS winebluetooth_device_disconnect( winebluetooth_device_t device );

NTSTATUS winebluetooth_auth_send_response( winebluetooth_device_t device, BLUETOOTH_AUTHENTICATION_METHOD method,
                                           UINT32 numeric_or_passkey, BOOL negative, BOOL *authenticated );
NTSTATUS winebluetooth_device_start_pairing( winebluetooth_device_t device, IRP *irp );

void winebluetooth_gatt_service_free( winebluetooth_gatt_service_t service );
static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_t s1, winebluetooth_gatt_service_t s2)
{
    return s1.handle == s2.handle;
}

void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic );
static inline BOOL winebluetooth_gatt_characteristic_equal( winebluetooth_gatt_characteristic_t c1,
                                                            winebluetooth_gatt_characteristic_t c2)
{
    return c1.handle == c2.handle;
}

struct winebluetooth_gatt_characteristic_value
{
    UINT32 size;
    UINT_PTR handle;
};

void winebluetooth_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *val, BYTE *dest );
void winebluetooth_gatt_characteristic_value_free( struct winebluetooth_gatt_characteristic_value *val );
NTSTATUS winebluetooth_gatt_characteristic_read_async( winebluetooth_gatt_characteristic_t chrc, IRP *irp );

enum winebluetooth_watcher_event_type
{
    BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN,
    BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED,
    BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED,
    BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED,
    BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED,
    BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED,
    BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED,
    BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED,
    BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED,
    BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED,
    BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED,
    BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED,
    BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_CHANGED,
    BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_READ,
};

struct winebluetooth_watcher_event_radio_added
{
    winebluetooth_radio_props_mask_t props_mask;
    struct winebluetooth_radio_properties props;
    winebluetooth_radio_t radio;
};

struct winebluetooth_watcher_event_radio_props_changed
{
    winebluetooth_radio_props_mask_t changed_props_mask;
    struct winebluetooth_radio_properties props;

    winebluetooth_radio_props_mask_t invalid_props_mask;
    winebluetooth_radio_t radio;
};

struct winebluetooth_watcher_event_device_added
{
    winebluetooth_device_props_mask_t known_props_mask;
    struct winebluetooth_device_properties props;
    winebluetooth_device_t device;
    winebluetooth_radio_t radio;
    BOOL init_entry;
};

struct winebluetooth_watcher_event_device_props_changed
{
    winebluetooth_device_props_mask_t changed_props_mask;
    struct winebluetooth_device_properties props;

    winebluetooth_device_props_mask_t invalid_props_mask;
    winebluetooth_device_t device;
};

struct winebluetooth_watcher_event_device_removed
{
    winebluetooth_device_t device;
};

struct winebluetooth_watcher_event_pairing_finished
{
    IRP *irp;
    NTSTATUS result;
};

struct winebluetooth_watcher_event_gatt_service_added
{
    winebluetooth_device_t device;
    winebluetooth_gatt_service_t service;

    UINT16 attr_handle;
    BOOL is_primary;
    GUID uuid;
};

#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_BROADCAST              1
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_READ                   (1 << 1)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE                  (1 << 2)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_NOTIFY                 (1 << 3)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_INDICATE               (1 << 4)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_SIGNED           (1 << 5)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_EXTENDED_PROPS         (1 << 6)
#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_WITHOUT_RESPONSE (1 << 7)

struct winebluetooth_watcher_event_gatt_characteristic_added
{
    winebluetooth_gatt_characteristic_t characteristic;
    winebluetooth_gatt_service_t service;
    BTH_LE_GATT_CHARACTERISTIC props;
    struct winebluetooth_gatt_characteristic_value value;
};

struct winebluetooth_watcher_event_gatt_characteristic_value_changed
{
    winebluetooth_gatt_characteristic_t characteristic;
    struct winebluetooth_gatt_characteristic_value value;
};

struct winebluetooth_watcher_event_gatt_characteristic_value_read
{
    IRP *irp;
    struct winebluetooth_gatt_characteristic_value value;
    NTSTATUS result;
};

union winebluetooth_watcher_event_data
{
    struct winebluetooth_watcher_event_radio_added radio_added;
    winebluetooth_radio_t radio_removed;
    struct winebluetooth_watcher_event_radio_props_changed radio_props_changed;
    struct winebluetooth_watcher_event_device_added device_added;
    struct winebluetooth_watcher_event_device_removed device_removed;
    struct winebluetooth_watcher_event_device_props_changed device_props_changed;
    struct winebluetooth_watcher_event_pairing_finished pairing_finished;
    struct winebluetooth_watcher_event_gatt_service_added gatt_service_added;
    winebluetooth_gatt_service_t gatt_service_removed;
    struct winebluetooth_watcher_event_gatt_characteristic_added gatt_characteristic_added;
    winebluetooth_gatt_characteristic_t gatt_characterisic_removed;
    struct winebluetooth_watcher_event_gatt_characteristic_value_changed gatt_characteristic_value_changed;
    struct winebluetooth_watcher_event_gatt_characteristic_value_read gatt_characteristic_value_read;
};

struct winebluetooth_watcher_event
{
    enum winebluetooth_watcher_event_type event_type;
    union winebluetooth_watcher_event_data event_data;
};

enum winebluetooth_event_type
{
    WINEBLUETOOTH_EVENT_WATCHER_EVENT,
    WINEBLUETOOTH_EVENT_AUTH_EVENT
};

struct winebluetooth_auth_event
{
    winebluetooth_device_t device;
    BLUETOOTH_AUTHENTICATION_METHOD method;
    UINT32 numeric_value_or_passkey;
};

struct winebluetooth_event
{
    enum winebluetooth_event_type status;
    union {
        struct winebluetooth_watcher_event watcher_event;
        struct winebluetooth_auth_event auth_event;
    } data;
};

NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result );
NTSTATUS winebluetooth_init( void );
NTSTATUS winebluetooth_shutdown( void );

#endif /* __WINE_WINEBTH_WINEBTH_H_ */
