#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 "ipc_session.h"
#include "dy_utils/dy_common.h"
#include "dy_utils/dy_ds.h"
#include "dy_utils/dy_ipc.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"

#define BLE_SLOT_INFO "/tmp/ble_slot_info.json"
#define RET_SUCCEED  0
#define RET_ERROR -1

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_scan_dev_s
{
    char *addr;
    char *SN;
    GDBusProxy *proxy;
    struct adapter *adapter;
} ble_scan_dev_t;

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;

    int dev_cnt;

    ble_scan_dev_t *scan_dev;
} 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
{
    char *uuid;
    char *data_str;
    char *opcode;
} last_send_data_t;

#define BLE_SESS_SNMAGIC   0x4e53 /* SN */
struct ble_sess_sn {
    unsigned short  sn_magic;  /* identifying magic */
    unsigned short  sn_len;    /* length of SN */
    unsigned int    sn_time;   /* time in seconds, since epoch */
};

#define BLE_SESS_DATAMAGIC 0x5044 /* DP */
struct ble_sess_data {
    unsigned short  sd_magic;  /* session data magic */
    unsigned short  sl_uuid;   /* length in bytes of uuid */
    unsigned short  sl_opcode; /* length in bytes of opcode */
    unsigned short  sl_data;   /* length in bytes of BLE data */
    unsigned short  sl_type;   /* type of advertising packet */
};

#define BLE_SESS_DATA_MAX 64       /* 64 packets at most */
#define BLE_SESS_BUFSIZ   0x8000   /* 32K buffer for each slave */
struct ble_sess_store {
    int num_data;                  /* number of stored BLE session data */
    int cur_offset;                /* current BLE buffer offset */
    time_t lsend_tsec;             /* last send time, seconds */
    unsigned int lsend_tmsec;      /* last send time, nanoseconds */
    unsigned char * ble_buffer;    /* buffered data */
    pthread_mutex_t store_lock;    /* for mulitprocess exclusive access lock */
};

static inline int ble_sess_datalen(const struct ble_sess_data * pdat)
{
    unsigned int ret;
    if (pdat->sd_magic != BLE_SESS_DATAMAGIC)
        return 0;
    ret  = (unsigned int) pdat->sl_uuid;
    ret += (unsigned int) pdat->sl_data;
    ret += (unsigned int) pdat->sl_opcode;
    ret += (unsigned int) sizeof(*pdat);
    if (ret & 0x1u)
        ret++;
    return (int) ret;
}

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;
    bool state_changed;
    struct adapter *adapter;
    GDBusProxy *proxy;
    long long last_ms;
    last_send_data_t last_data;
    struct ble_sess_store buf_store;
} 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;
    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;
    bool enable;
};

typedef struct ble_var_s
{
    DBusConnection *dbus_conn;
    GDBusClient *client;
    GDBusProxy *agent_manager;
    struct adapter *adapter;
    ipc_session_t *session; //内部broker的MQTT session，用于进程间通信
    char sn_str[SN_MAX_LEN];
    nodes_cfg_table_t *nodes_cfg_table; //节点信息
    int dev_id;
    char dev_name[6];
    char proxy_path[20];
    int slot;
    dev_status_table_t *ds; //下挂节点的状态信息
    template_table_t *template_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;
}

static inline ble_scan_dev_t *find_scan_dev_by_address(struct adapter *adapter, const char *address)
{
    int i;
    ble_scan_dev_t *scan_dev = NULL;
    ble_scanner_cfg_t *scan_cfg = &adapter->scan_cfg;

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

    return NULL;
}

extern unsigned int ble_gettime(time_t * psec, unsigned int * pmsec);
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 int 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);
extern int ble_send_data_to_sn(ble_var_t *var, char *sn, char *uuid, char *data_str, char *opcode, bool update_last);
extern void disconnect_dev(ble_var_t *var, ble_slave_dev_t *slave_dev);
extern void ble_send_adv_data_to_pp(ble_var_t *var, int dev_id, ble_scan_dev_t *scan_dev, const char *type, uint8_t *value, int value_len);
int get_device_connect_property(GList *devices, char *addr, dbus_bool_t *connected);
int get_adapter_discovering_property(GDBusProxy *ad_proxy, dbus_bool_t *is_discovering);
