#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wordexp.h>

#include <glib.h>

#include "src/shared/shell.h"
#include "src/shared/util.h"
#include "src/shared/mainloop.h"
#include "gdbus/gdbus.h"
#include "agent.h"
#include "gatt.h"

#include "dy_utils/dy_mqtt.h"
#include "dy_utils/dy_common.h"

#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF

#define DISTANCE_VAL_INVALID 0x7FFF

#define APP_PATH "/org/bluez/app"
#define PROFILE_INTERFACE "org.bluez.GattProfile1"
#define SERVICE_INTERFACE "org.bluez.GattService1"
#define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
#define DESC_INTERFACE "org.bluez.GattDescriptor1"
#define AD_PATH "/org/bluez/advertising"
#define AD_IFACE "org.bluez.LEAdvertisement1"
#define GATT_MGR_IFACE "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"

struct adapter;
struct ad;
typedef struct ble_var_s ble_var_t;

typedef enum scan_state_e
{
    BLE_SCAN_NONE = 0,
    BLE_SCAN_START,
    BLE_SCAN_FIND_DEVICE,
    BLE_SCAN_STOP
} scan_state;

typedef struct ble_scanner_cfg_s
{
    /*
    bluez DBus default scan paramter:
        Type: Active (0x01)
        Interval: 22.500 msec (0x0024)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
    */
    bool enable;

    /*
     * Filter by service UUIDs, empty means match _any_ UUID.
    */
    char **UUIDs;

    size_t uuids_len;

    /*
        RSSI threshold value.
    */
    uint16_t RSSI;

    int16_t Pathloss;

    /*
        string Transport (Default "auto")
        Transport parameter determines the type of
        scan.

        Possible values:
            "auto"	- interleaved scan
            "bredr"	- BR/EDR inquiry
            "le"	- LE scan only

        If "le" or "bredr" Transport is requested,
        and the controller doesn't support it,
        org.bluez.Error.Failed error will be returned.
        If "auto" transport is requested, scan will use
        LE, BREDR, or both, depending on what's
        currently enabled on the controller.
    */
    char *Transport;

    /*
        bool DuplicateData (Default: true)

        Disables duplicate detection of advertisement
        data.

        When enabled PropertiesChanged signals will be
        generated for either ManufacturerData and
        ServiceData everytime they are discovered.
    */
    bool DuplicateData;

    scan_state state;

    struct adapter *adapter;

    int scan_fd;

    /*
	0x00 Duplicate filtering disabled.
	0x01 Duplicate filtering enabled
	The Filter_Duplicates parameter controls whether the Link Layer should filter
	out duplicate advertising reports (Filtering_Enabled) to the Host, or if the Link
	Layer should generate advertising reports for each packet received
	(Filtering_Disabled). 
	*/
    uint8_t filter_dup;

    /*
	0x00 Passive Scanning. No scanning PDUs shall be sent (default)
	0x01 Active scanning. Scanning PDUs may be sent.
	*/
    uint8_t scan_type;
    /*
	The LE_Scan_Window parameter shall always be set to a value
	smaller or equal to the value set for the LE_Scan_Interval parameter. If they
	are set to the same value scanning should be run continuously
	*/
    /*
	This is defined as the time interval from when the Controller
	started its last LE scan until it begins the subsequent LE scan
	Time Range: 2.5 ms to 10.24 s
	*/
    uint16_t interval;

    /*
	The duration of the LE scan. LE_Scan_Window shall be less
	than or equal to LE_Scan_Interval
	Time Range: 2.5 ms to 10.24 s
	*/
    uint16_t window;
    /*
	0x00 Accept all advertising and scan response PDUs except directed advertising PDUs not addressed to this device (default)
	0x01 Accept only advertising and scan response PDUs from devices where
		the advertiser’s address is in the White List. Directed advertising PDUs
		which are not addressed to this device shall be ignored
	0x02 Accept all advertising and scan response PDUs except directed advertising PDUs where the identity address corresponding to TargetA does
		not address this device.
		Note: Directed advertising PDUs where TargetA is a resolvable private
		address that cannot be resolved are also accepted.
	0x03 Accept all advertising and scan response PDUs except:
		• advertising and scan response PDUs where the advertiser's identity
		address is not in the White List; and
		• directed advertising PDUs where the identity address corresponding
		to TargetA does not address this device.
		Note: Directed advertising PDUs where TargetA is a resolvable private
		address that cannot be resolved are also accepted.
	*/
    uint8_t filter_policy; // do not filter duplicates
                           /*
	Own_Address_Type parameter indicates the type of address being used in the
	scan request packets.
	*/
    uint8_t own_type;
} ble_scanner_cfg_t;

struct ad_data
{
    uint8_t data[25];
    uint8_t len;
};

struct service_data
{
    char *uuid;
    struct ad_data data;
};

struct manufacturer_data
{
    uint16_t id;
    struct ad_data data;
};

struct data
{
    uint8_t type;
    struct ad_data data;
};

typedef struct ble_advertising_cfg_s
{
    /*
    < HCI Command: LE Set Advertising Parameters (0x08|0x0006) plen 15                                    #5 [hci0] 7.673498
        Min advertising interval: 1280.000 msec (0x0800)
        Max advertising interval: 1280.000 msec (0x0800)
        Type: Connectable undirected - ADV_IND (0x00)
        Own address type: Public (0x00)
        Direct address type: Public (0x00)
        Direct address: 00:00:00:00:00:00 (OUI 00-00-00)
        Channel map: 37, 38, 39 (0x07)
        Filter policy: Allow Scan Request from Any, Allow Connect Request from Any (0x00)
    */
    bool enable;

    /*
    string Type
			Determines the type of advertising packet requested.
			Possible values: "broadcast" or "peripheral"
    */
    char *type;

    /*
    Local name to be used in the advertising report. If the
			string is too big to fit into the packet it will be
			truncated.

			If this property is available 'local-name' cannot be
			present in the Includes.
    */
    char *local_name;

    /*
    		uint16 Appearance

			Appearance to be used in the advertising report.

			Possible values: as found on GAP Service.
    */
    uint16_t local_appearance;
    /*
    uint16_t Duration

			Duration of the advertisement in seconds. If there are
			other applications advertising no duration is set the
			default is 2 seconds.
    */
    uint16_t duration;
    /*
    		uint16_t Timeout

			Timeout of the advertisement in seconds. This defines
			the lifetime of the advertisement.
    */
    uint16_t timeout;

    /*
    uint16 DiscoverableTimeout [Experimental]

			The discoverable timeout in seconds. A value of zero
			means that the timeout is disabled and it will stay in
			discoverable/limited mode forever.

			Note: This property shall not be set when Type is set
			to broadcast.

    */
    uint16_t discoverable_to;
    /*
    array{string} ServiceUUIDs

			List of UUIDs to include in the "Service UUID" field of
			the Advertising Data.
    */
    char **uuids;
    size_t uuids_len;
    /*
    dict ServiceData

			Service Data elements to include. The keys are the
			UUID to associate with the data.
    */
    struct service_data service;
    /*
    dict ManufacturerData

			Manufactuer Data fields to include in
			the Advertising Data.  Keys are the Manufacturer ID
			to associate with the data.
    */
    struct manufacturer_data manufacturer;
    /*
    dict Data [Experimental]
			Advertising Type to include in the Advertising
			Data. Key is the advertising type and value is the
			data as byte array.

			Note: Types already handled by other properties shall
			not be used.

			Possible values:
				<type> <byte array>
				...

			Example:
				<Transport Discovery> <Organization Flags...>
				0x26                   0x01         0x01...
    */
    struct data data;

    /*
    bool Discoverable [Experimental]

			Advertise as general discoverable. When present this
			will override adapter Discoverable property.

			Note: This property shall not be set when Type is set
			to broadcast.
    */
    bool discoverable;

    /*
    array{string} Includes

			List of features to be included in the advertising
			packet.

			Possible values: as found on
					LEAdvertisingManager.SupportedIncludes
    */
    bool tx_power;
    bool appearance;

    struct adapter *adapter;
} ble_advertising_cfg_t;

typedef enum slave_dev_state_e
{
    SLAVE_DEV_NONE = 0,
    SLAVE_DEV_CONNECTING,
    SLAVE_DEV_CONNECTED,
} slave_dev_state;

typedef struct ble_slave_dev_s
{
    char *addr;
    /*
    string AddressType

    The Bluetooth device Address Type. This is
    address type that should be used for initial
    connection. If this parameter is not present
    BR/EDR device is created.

    Possible values:
        "public" - Public address
        "random" - Random address
    */
    char *AddressType;
    char *SN;
    slave_dev_state state;
    struct adapter *adapter;
    GDBusProxy *proxy;
    long long last_ms;
} ble_slave_dev_t;

typedef struct ble_slave_cfg_s
{
    int dev_cnt;
    ble_slave_dev_t *slave;
    struct adapter *adapter;
} ble_slave_cfg_t;

typedef struct ble_gatt_service_desc_s
{
    char *uuid;
    /*
    array{string} Flags [read-only]

    Defines how the descriptor value can be used.

    Possible values:

        "read"
        "write"
        "encrypt-read"
        "encrypt-write"
        "encrypt-authenticated-read"
        "encrypt-authenticated-write"
        "secure-read" (Server Only)
        "secure-write" (Server Only)
        "authorize"
    */
    char **Flags;
} ble_gatt_service_desc_t;

typedef struct ble_gatt_service_char_s
{
    char *uuid;
    /*
    array{string} Flags [read-only]

    Defines how the characteristic value can be used. See
    Core spec "Table 3.5: Characteristic Properties bit
    field", and "Table 3.8: Characteristic Extended
    Properties bit field". Allowed values:

        "broadcast"
        "read"
        "write-without-response"
        "write"
        "notify"
        "indicate"
        "authenticated-signed-writes"
        "reliable-write"
        "writable-auxiliaries"
        "encrypt-read"
        "encrypt-write"
        "encrypt-authenticated-read"
        "encrypt-authenticated-write"
        "secure-read" (Server only)
        "secure-write" (Server only)
        "authorize"
    */
    char **Flags;
    uint16_t num_desc;
    ble_gatt_service_desc_t *desc;
} ble_gatt_service_char_t;

typedef struct ble_gatt_service_cfg_s
{
    char *uuid;
    bool primary;
    uint16_t num_handles;
    uint16_t num_char;
    ble_gatt_service_char_t *chars;
} ble_gatt_service_cfg_t;

typedef struct ble_gatt_server_cfg_s
{
    bool enable;
    int service_cnt;
    ble_gatt_service_cfg_t *services;
    int id;
    struct adapter *adapter;
} ble_gatt_server_cfg_t;

struct adapter
{
    GDBusProxy *proxy;
    GDBusProxy *ad_proxy;
    GDBusProxy *gm_proxy;
    GList *devices;
    GList *local_services;
    bool enable;
    bool discover_stated;
    ble_scanner_cfg_t scan_cfg;
    ble_advertising_cfg_t adv_cfg;
    ble_slave_cfg_t slave_cfg;
    ble_gatt_server_cfg_t server_cfg;
    ble_var_t *var;
    struct ad *ad;
    int dev_changed_ts;
    int dev_id;
};

typedef struct ble_var_s
{
    DBusConnection *dbus_conn;
    GDBusClient *client;
    GDBusProxy *agent_manager;
    GList *ctrl_list;
    int ctrl_cnt;
    mqtt_session_t *session; //内部broker的MQTT session，用于进程间通信
    char sn_str[SN_MAX_LEN];
    nodes_cfg_table_t *nodes_cfg_table; //节点信息
} ble_var_t;

static inline void proxy_free(gpointer data)
{
    printf("free proxy %p\n", data);
    g_free(data);
}

static inline GDBusProxy *find_proxy_by_address(GList *source, const char *address)
{
    GList *list;

    for (list = g_list_first(source); list; list = g_list_next(list))
    {
        GDBusProxy *proxy = list->data;
        DBusMessageIter iter;
        const char *str;

        if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
            continue;

        dbus_message_iter_get_basic(&iter, &str);

        if (!strcasecmp(str, address))
            return proxy;
    }

    return NULL;
}

extern struct adapter *find_parent(GDBusProxy *device);

static inline ble_slave_dev_t *find_slave_dev_by_address(struct adapter *adapter, const char *address)
{
    int i;
    ble_slave_dev_t *slave_dev = NULL;
    ble_slave_cfg_t *slave_cfg = &adapter->slave_cfg;

    for (i = 0; i < slave_cfg->dev_cnt; i++)
    {
        slave_dev = &slave_cfg->slave[i];
        if (!strcasecmp(slave_dev->addr, address))
        {
            return slave_dev;
        }
    }

    return NULL;
}

extern void ble_send_raw_data_to_pp(ble_var_t *var, int dev_id, ble_slave_dev_t *slave, char *uuid, char *ops, uint8_t *value, int value_len);
extern bool ble_load_cfg(ble_var_t *var, struct adapter *adapter, GDBusProxy *proxy);
extern bool start_work(ble_var_t *var);
extern bool start_scan(ble_scanner_cfg_t *scan_cfg);
extern bool stop_scan(ble_scanner_cfg_t *scan_cfg);
extern bool start_connect(ble_slave_cfg_t *slave_cfg);
extern bool start_advertise(struct adapter *adapter, ble_advertising_cfg_t *adv_cfg);
extern bool start_services(struct adapter *adapter, ble_gatt_server_cfg_t *server_cfg);
extern void ad_unregister(struct adapter *adapter, GDBusProxy *manager);
extern void my_gatt_add_manager(GDBusProxy *proxy);
extern void my_gatt_remove_manager(GDBusProxy *proxy);
extern void gatt_register_application(struct adapter *adapter);
extern void write_attribute_bin(GDBusProxy *proxy, unsigned char *buf, int len);
extern bool ble_mqtt_init(ble_var_t *var);
extern bool start_hci_scan(ble_scanner_cfg_t *scan_cfg, struct adapter *adapter, bool start);
extern void start_discovery_reply(DBusMessage *message, void *user_data);
extern void *ble_thread_loop(void *param);