#include "include.h"
#include "ble_init.h"
#include "ble_service.h"
#include "wireless_service.h"
#include "wireless_cmd.h"
#include "utils_ring_buffer.h"

#if FUNC_WIRELESS_EN

#define TRACE_EN                            1

#if TRACE_EN
#define TRACE(...)                          my_printf("[WIRELESS] ");\
                                            my_printf(__VA_ARGS__)
#define TRACE_R(...)                        my_print_r(__VA_ARGS__);
#else
#define TRACE(...)
#define TRACE_R(...)
#endif // TRACE_EN


/* Format: Header(1B) + [HID(n)] + [AUDIO(n)] + [CMD(n)] */
#define WIRELESS_SINGLE_PACKET_SIZE         (1 + WIRELESS_DATA_LENGTH_HID + WIRELESS_DATA_LENGTH_AUDIO + CTRL_CMD_PACKET_SIZE)


static struct {
    uint16_t                    con_handle;
    wireless_addr_info_typedef  addr_info;

    uint8_t                     transmit_done;
    uint8_t                     single_packet[WIRELESS_SINGLE_PACKET_SIZE];
    uint32_t                    data_len;
} wireless_cb AT(.ble_buf.wireless.sta);


AT(.com_text.wireless.transmit)
void wireless_packet_process(uint8_t *buf, uint32_t size)
{
    u8 head = buf[WIRELESS_HEAD_POS];
    u8 pos = WIRELESS_PAYLOAD_POS;
    u8 *cmd_data = NULL, cmd_len = 0;

    /* Unpack data */

    if (head & WIRELESS_HEADER_CTRL_BIT) {
        cmd_data = &buf[pos];
        cmd_len = cmd_data[WIRELESS_CMD_DATA_LENGTH_POS];
        pos += 2 + cmd_len;
    }

    /* Check length valid */

    if (pos != size) {
        TRACE("data length err: %d %d\n", pos, size);
        return;
    }

    /* Process */

    if (cmd_data) {
        TRACE("receive cmd - type:0x%02x data[%d]:\n", cmd_data[WIRELESS_CMD_DATA_TYPE_POS], cmd_len);
        TRACE_R(&cmd_data[WIRELESS_CMD_DATA_VALUE_POS], cmd_len);
        wireless_user_event_handle(cmd_data, cmd_len);
    }
}

static void wireless_event_packet_handler(uint8_t event_type, uint8_t *packet, uint8_t size)
{
    switch (event_type) {
        case WIRELESS_EVENT_CONNECTED:
            TRACE("[slave]connected - con_handle: 0x%04x\n", GET_LE16(&packet[0]));
            wireless_cb.con_handle = GET_LE16(&packet[0]);
            memcpy((u8 *)&wireless_cb.addr_info, &packet[2], 7);
            bsp_wireless_link_info_write(&wireless_cb.addr_info);
            wireless_cb.transmit_done = 1;
            wireless_latency_applied(wireless_cb.con_handle, false);
            break;

        case WIRELESS_EVENT_DISCONNECTED:
            TRACE("[slave]disconnected - con_handle: 0x%04x  reason: 0x%02x\n", GET_LE16(&packet[0]), packet[2]);
            wireless_cb.con_handle = 0;
            /* Update adv info and start when disc */
            wireless_adv_init(true);
            /* Reset HW state */
            sys_cb.peer_usb_is_active = 0;
            break;

        default:
            break;
    }
}

AT(.com_text.wireless.transmit)
static void wireless_receive_handler(uint16_t con_handle, uint8_t *packet, uint8_t size)
{
    if (con_handle == wireless_cb.con_handle) {
        wireless_packet_process(packet, size);
    }

    wireless_rxbuf_release(packet);

    lowpwr_pwroff_delay_reset();
    lowpwr_sleep_delay_reset();
}

void wireless_enter_sleep_hook(void)
{
    if (wireless_cb.con_handle) {
        wireless_latency_applied(wireless_cb.con_handle, true);
    }
}

void wireless_exit_sleep_hook(void)
{
    if (wireless_cb.con_handle) {
        wireless_latency_applied(wireless_cb.con_handle, false);
    }
}

void wireless_service_init(void)
{
    TRACE("slave init\n");

    memset(&wireless_cb, 0x00, sizeof(wireless_cb));

    wireless_event_handler_register(wireless_event_packet_handler);
    wireless_receive_handler_register(wireless_receive_handler);

    wireless_cmd_init();
}

AT(.text.app.proc.ble)
void wireless_service_proc(void)
{
    u8 *ptr = &wireless_cb.single_packet[WIRELESS_PAYLOAD_POS];
    u32 len = 0;
    u8 found = 0;
    u8 head = 0;

    if (wireless_cb.con_handle) {
        if (wireless_cb.transmit_done) {
            len = wireless_cmd_get_buf(ptr);
            if (len) {
                ptr += len;
                head |= WIRELESS_HEADER_CTRL_BIT;
                found = 1;
            }

            wireless_cb.single_packet[WIRELESS_HEAD_POS] = head;
            wireless_cb.data_len = ptr - wireless_cb.single_packet;
        }

        if (found || !wireless_cb.transmit_done) {
            found = wireless_send_for_con(wireless_cb.con_handle, wireless_cb.single_packet, wireless_cb.data_len);
            wireless_cb.transmit_done = (found == 0)? 1: 0;
            lowpwr_pwroff_delay_reset();
            lowpwr_sleep_delay_reset();
        }
    }
}

#endif
