#include "include.h"
#include "ble_client.h"
#include "hsuart_transfer.h"        /*Serial port*/

#define TRACE_EN            1
#if TRACE_EN
#define TRACE(...)          printf(__VA_ARGS__)
#define TRACE_R(...)        print_r(__VA_ARGS__)
#else
#define TRACE(...)
#define TRACE_R(...)
#endif // TRACE_EN


// 目标从机名称，实际过滤条件可根据广播包更改为其他内容
#define   SLAVE_DEV_NAME   "GW3323_BLE_SLAVE"

// Write Test UUID
u16 target_write_uuid16 = 0xfff1;


static server_info_t server_info;

static att_client_handler_t  client_handler;

static client_state_t tc_state = STA_IDLE;


//
u16 write_handler = 0;


void ble_clent_event_packet_handle(uint8_t event_type, uint8_t *packet, uint16_t size)
{
    switch(event_type){
        case HCI_EVENT_DISCONNECTION_COMPLETE:
            if (packet[1] == server_info.conn_handle) {
                TRACE("HCI_EVENT_DISCONNECTION_COMPLETE\n");
                write_handler = 0;
                server_info.conn_handle = 0;
                ble_scan_en();
            }
            return;

        case HCI_EVENT_LE_META:
            switch (packet[0]) {
                case 0x01:
                    TRACE("LE_CONNECTION_COMPLETE\n");
                    memcpy(&server_info.conn_handle, &packet[2], 2);
                    TRACE("server_info.conn_handle:%x\n",server_info.conn_handle);
                    tc_state = STA_W4_SERVICE_RESULT;
                    server_info.service_cnt = 0;
                    u8 code = ble_client_discover_primary_services(server_info.conn_handle);
                    printf("code: %x\n", code);
                    break;
            }
            return;

        case ATT_EVENT_CONNECT_PARAM_UPDATE_COMPLETE:
            TRACE("ATT_EVENT_CONNECT_PARAM_UPDATE_COMPLETE\n");
            return;

        case GAP_EVENT_ADVERTISING_REPORT: {
            adv_report_t adv_report;
            u8 *current_adv_struct;

            memcpy(&adv_report, &packet[0], 10);
            adv_report.data = &packet[10];
            adv_report.offset = 0;

            while (adv_report.offset < adv_report.data_length) {
                current_adv_struct = adv_report.data + adv_report.offset;
                if ((current_adv_struct[1] == 0x09) && (memcmp(&current_adv_struct[2], SLAVE_DEV_NAME, current_adv_struct[0] - 1) == 0)) {
                    ble_scan_dis();
                    printf("Found - %s addr: ", (adv_report.address_type)? "Random": "Public");
                    print_r(adv_report.address, 6);
                    ble_connect_by_addr(adv_report.address, adv_report.address_type, 24, 0, 400);
                }
                adv_report.offset += current_adv_struct[0] + 1;
            }

            return;
        }
    }

    switch(tc_state){
        case STA_W4_SERVICE_RESULT:
            switch(event_type){
                case GATT_EVENT_SERVICE_QUERY_RESULT:
                    TRACE("GATT_EVENT_SERVICE_QUERY_RESULT\n");
                    ble_service_query_result_get_service(packet, &server_info.service[server_info.service_cnt++]);
                    break;

                case GATT_EVENT_QUERY_COMPLETE:
                    server_info.service_index = 0;
                    server_info.characteristic_index = 0;
                    if (server_info.service_cnt != 0) {
                        TRACE("All service discover complete:%d\n",server_info.service_cnt);
                        for(uint8_t cnt=0;cnt<server_info.service_cnt;cnt++){
                            TRACE("service[%d]->uuid16:%04x,start handle:%04x,stop handle:%04x\n",cnt,server_info.service[cnt].uuid16,server_info.service[cnt].start_group_handle,server_info.service[cnt].end_group_handle);
                        }
                        TRACE("Client Discovery characteristic start\n");
                        tc_state = STA_W4_CHARACTERISTIC_RESULT;
                        server_info.characteristic_cnt = 0;
                        ble_client_discover_characteristics_for_service(server_info.conn_handle,&server_info.service[0]);
                    }
                    else{
                        tc_state = STA_IDLE;
                        server_info.service_cnt = 0;
                        server_info.characteristic_cnt = 0;
                        TRACE("Client Discovery service failed->No service\n");
                    }
                    break;

                default:
                    break;
            }
            break;

        case STA_W4_CHARACTERISTIC_RESULT:
            switch (event_type) {
                case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
                    ble_characteristic_query_result_get_characteristic(packet,&server_info.characteristic[server_info.characteristic_cnt]);
                    //
                    if (server_info.characteristic[server_info.characteristic_cnt].uuid16 == target_write_uuid16) {
                        write_handler = server_info.characteristic[server_info.characteristic_cnt].value_handle;
                    }
                    server_info.characteristic_cnt++;
                    break;

                case GATT_EVENT_QUERY_COMPLETE:
                    if (++server_info.service_index < server_info.service_cnt) {
                        tc_state = STA_W4_CHARACTERISTIC_RESULT;
                        ble_client_discover_characteristics_for_service(server_info.conn_handle,&server_info.service[server_info.service_index]);
                    } else {
                        while(server_info.characteristic_index < server_info.characteristic_cnt) {
                            if ((server_info.characteristic[server_info.characteristic_index].properties & ATT_NOTIFY) == ATT_NOTIFY) {
                                TRACE("notify uuid:%04x\n",server_info.characteristic[server_info.characteristic_index].uuid16);
                                ble_client_write_client_characteristic_configuration(server_info.conn_handle, &server_info.characteristic[server_info.characteristic_index], GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_NOTIFICATION);
                                server_info.characteristic_index++;
                                tc_state = STA_W4_CHARACTERISTIC_RESULT;
                                return;
                            } else if ((server_info.characteristic[server_info.characteristic_index].properties & ATT_INDICATE) == ATT_INDICATE) {
                                TRACE("indicate uuid:%04x\n",server_info.characteristic[server_info.characteristic_index].uuid16);
                                ble_client_write_client_characteristic_configuration(server_info.conn_handle, &server_info.characteristic[server_info.characteristic_index], GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_INDICATION);
                                server_info.characteristic_index++;
                                tc_state = STA_W4_CHARACTERISTIC_RESULT;
                                return;
                            }
                            server_info.characteristic_index++;
                        }
                        tc_state = STA_SUBSCRIBED;
                    }
                    break;

                default:
                    break;
            }
            break;

        default:
            break;
    }
}

static uint16_t ble_client_notify_callback (uint16_t con_handle, uint16_t attribute_handle, uint8_t * buffer, uint16_t buffer_size)
{
    TRACE("ble_client_notify_callback[%d]: ");
    TRACE_R(buffer, buffer_size);

    hsuart_cmd(HSUT_TRANSMIT, ENABLE);
    hsuart_dma_start(HSUT_TRANSMIT, (uint32_t)buffer, buffer_size);

    return 0;
}


static uint16_t ble_att_read_callback(uint16_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size)
{
    TRACE("attribute_handle[0x%04x]: ", attribute_handle);
    TRACE_R(buffer, buffer_size);

    return 0;
}

void ble_client_init(void)
{
    client_handler.event_handler = ble_clent_event_packet_handle;
    client_handler.notify_callback = ble_client_notify_callback;
    client_handler.read_callback = ble_att_read_callback;

    att_client_register_handler(&client_handler);
}

void ble_client_user_process(void)
{
    static u32 delay_cnt;

    if (tick_check_expire(delay_cnt, 2000)) {
        delay_cnt = tick_get();

        if (write_handler && ble_is_connect()) {
            u8 tmp[5] = {'G', 'e', 'e', 'h', 'y'};
            printf("characteristic 0xfff1 write[5]: ");
            print_r(tmp, 5);
            ble_gatt_client_write_value_of_characteristic(server_info.conn_handle, write_handler, 5, tmp);
        }
    }
}
extern u16 BtTxBuferCount;     // Send data length
extern u8 RECEIVE_BUF[HSART_RX_BUFF_LEN];  //Receive buffer
extern u8 TRANSMIT_BUF[HSART_TX_BUFF_LEN];     //transmit buffer
void hsuart_transfer_example(void)
{
    if(BtTxBuferCount>0)
    {
        hsuart_cmd(HSUT_TRANSMIT, ENABLE);
        hsuart_dma_start(HSUT_TRANSMIT, (uint32_t)TRANSMIT_BUF,BtTxBuferCount);
        ble_gatt_client_write_value_of_characteristic(server_info.conn_handle, write_handler, BtTxBuferCount, (uint32_t)TRANSMIT_BUF);

        BtTxBuferCount=0;
    }
}
