/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include <string.h>
#include "cmsis_os.h"
#include "app_tws_ibrt_trace.h"
#include "bluetooth.h"
#include "crc16.h"
#include "heap_api.h"
#include "hci_api.h"
#include "me_api.h"
#include "app_tws_ibrt.h"
#include "app_tws_besaud.h"
#include "app_vendor_cmd_evt.h"
#include "tws_role_switch.h"
#include "l2cap_api.h"
#include "rfcomm_api.h"
#include "conmgr_api.h"
#include "bt_if.h"
#include "app_ibrt_if.h"
#include "app_tws_ibrt_cmd_handler.h"
#include "app_tws_ibrt.h"
#include "bt_drv_interface.h"
#include "bt_drv_reg_op.h"
#include "app_ibrt_peripheral_manager.h"
#include "app_ibrt_ui.h"
#if defined(IBRT)
osThreadId app_ibrt_peripheral_tid;
static void app_ibrt_peripheral_thread(void const *argument);
osThreadDef(app_ibrt_peripheral_thread, osPriorityHigh,1, 2048,"app_ibrt_peripheral_thread");

#define TWS_PERIPHERAL_DEVICE_MAILBOX_MAX (10)
osMailQDef (app_ibrt_peripheral_mailbox, TWS_PERIPHERAL_DEVICE_MAILBOX_MAX, TWS_PD_MSG_BLOCK);
static osMailQId app_ibrt_peripheral_mailbox = NULL;
static uint8_t app_ibrt_peripheral_mailbox_cnt = 0;

static int app_ibrt_peripheral_mailbox_init(void)
{
    app_ibrt_peripheral_mailbox = osMailCreate(osMailQ(app_ibrt_peripheral_mailbox), NULL);
    if (app_ibrt_peripheral_mailbox == NULL)  {
        TRACE(0,"Failed to Create app_ibrt_peripheral_mailbox\n");
        return -1;
    }
    app_ibrt_peripheral_mailbox_cnt = 0;
    return 0;
}

int app_ibrt_peripheral_mailbox_put(TWS_PD_MSG_BLOCK* msg_src)
{
    if(!msg_src){
        TRACE(0,"msg_src is a null pointer in app_ibrt_peripheral_mailbox_put!");
        return -1;
    }
    
    osStatus status;
    TWS_PD_MSG_BLOCK *msg_p = NULL;
    msg_p = (TWS_PD_MSG_BLOCK*)osMailAlloc(app_ibrt_peripheral_mailbox, 0);
    ASSERT(msg_p, "osMailAlloc error");
    msg_p->msg_body.message_id = msg_src->msg_body.message_id;
    msg_p->msg_body.message_ptr = msg_src->msg_body.message_ptr;
    msg_p->msg_body.message_Param0 = msg_src->msg_body.message_Param0;
    msg_p->msg_body.message_Param1 = msg_src->msg_body.message_Param1;
    msg_p->msg_body.message_Param2 = msg_src->msg_body.message_Param2;

    status = osMailPut(app_ibrt_peripheral_mailbox, msg_p);
    if (osOK == status)
        app_ibrt_peripheral_mailbox_cnt++;
    return (int)status;
}

int app_ibrt_peripheral_mailbox_free(TWS_PD_MSG_BLOCK* msg_p)
{
    if(!msg_p){
        TRACE(0,"msg_p is a null pointer in app_ibrt_peripheral_mailbox_free!");
        return -1;
    }
    osStatus status;

    status = osMailFree(app_ibrt_peripheral_mailbox, msg_p);
    if (osOK == status)
        app_ibrt_peripheral_mailbox_cnt--;

    return (int)status;
}

int app_ibrt_peripheral_mailbox_get(TWS_PD_MSG_BLOCK** msg_p)
{
    if(!msg_p){
        TRACE(0,"msg_p is a null pointer in app_ibrt_peripheral_mailbox_get!");
        return -1;
    }

    osEvent evt;
    evt = osMailGet(app_ibrt_peripheral_mailbox, osWaitForever);
    if (evt.status == osEventMail) {
        *msg_p = (TWS_PD_MSG_BLOCK *)evt.value.p;
        return 0;
    }
    return -1;
}

#ifdef BES_AUTOMATE_TEST
#define APP_IBRT_PERIPHERAL_BUF_SIZE 2048
static heap_handle_t app_ibrt_peripheral_heap;
uint8_t app_ibrt_peripheral_buf[APP_IBRT_PERIPHERAL_BUF_SIZE];
bool app_ibrt_auto_test_started = false;
void app_ibrt_peripheral_heap_init(void)
{
    app_ibrt_auto_test_started = true;
    app_ibrt_peripheral_heap = heap_register(app_ibrt_peripheral_buf, APP_IBRT_PERIPHERAL_BUF_SIZE);
}

void *app_ibrt_peripheral_heap_malloc(uint32_t size)
{
    void *ptr = heap_malloc(app_ibrt_peripheral_heap,size);
    ASSERT(ptr, "%s size:%d", __func__, size);
    return ptr;
}

void *app_ibrt_peripheral_heap_cmalloc(uint32_t size)
{
    void *ptr = heap_malloc(app_ibrt_peripheral_heap,size);
    ASSERT(ptr, "%s size:%d", __func__, size);
    memset(ptr, 0, size);
    return ptr;
}

void *app_ibrt_peripheral_heap_realloc(void *rmem, uint32_t newsize)
{
    void *ptr = heap_realloc(app_ibrt_peripheral_heap, rmem, newsize);
    ASSERT(ptr, "%s rmem:%p size:%d", __func__, rmem,newsize);
    return ptr;
}

void app_ibrt_peripheral_heap_free(void *rmem)
{
    ASSERT(rmem, "%s rmem:%p", __func__, rmem);
    heap_free(app_ibrt_peripheral_heap, rmem);
}
#endif

void app_ibrt_peripheral_auto_test_stop(void)
{
#ifdef BES_AUTOMATE_TEST
    app_ibrt_auto_test_started = false;
#endif
}

/*****************************************************************************
 Prototype    : app_ibrt_peripheral_thread
 Description  : peripheral manager thread
 Input        : void const *argument
 Output       : None
 Return Value :
 Calls        :
 Called By    :

 History        :
 Date         : 2019/4/18
 Author       : bestechnic
 Modification : Created function

*****************************************************************************/
typedef void (*app_ibrt_peripheral_cb0)(void);
typedef void (*app_ibrt_peripheral_cb1)(void *);
typedef void (*app_ibrt_peripheral_cb2)(void *, void *);
void app_ibrt_peripheral_automate_test_handler(uint8_t *cmd_buf, uint32_t cmd_len);

void app_ibrt_peripheral_thread(void const *argument)
{
    while(1){
        TWS_PD_MSG_BLOCK *msg_p = NULL;
        if ((!app_ibrt_peripheral_mailbox_get(&msg_p))&&(!argument)) {
            switch(msg_p->msg_body.message_id){
                case 0:
                    if(msg_p->msg_body.message_ptr){
                        ((app_ibrt_peripheral_cb0)(msg_p->msg_body.message_ptr))();
                    }
                    break;
                case 1:
                    if(msg_p->msg_body.message_ptr){
                        ((app_ibrt_peripheral_cb1)(msg_p->msg_body.message_ptr))((void *)msg_p->msg_body.message_Param0);
                    }
                    break;
                case 2:
                    if(msg_p->msg_body.message_ptr){
                        ((app_ibrt_peripheral_cb2)(msg_p->msg_body.message_ptr))((void *)msg_p->msg_body.message_Param0,
                                                                                 (void *)msg_p->msg_body.message_Param1);
                    }
                    break;
                case 0xfe:
                    app_ibrt_peripheral_automate_test_handler((uint8_t*)msg_p->msg_body.message_Param0,
                                                              (uint32_t)msg_p->msg_body.message_Param1);
                    break;
                case 0xff: {// ibrt test
                    char ibrt_cmd[20] = {0};
                    memcpy(ibrt_cmd+0, &msg_p->msg_body.message_Param0, 4);
                    memcpy(ibrt_cmd+4, &msg_p->msg_body.message_Param1, 4);
                    memcpy(ibrt_cmd+8, &msg_p->msg_body.message_Param2, 4);
                    TRACE(1,"ibrt_ui_log: %s\n", ibrt_cmd);
                    app_ibrt_ui_test_cmd_handler((unsigned char*)ibrt_cmd, strlen(ibrt_cmd)+1);
                    }
                    break;
                default:
                    break;
            }
            app_ibrt_peripheral_mailbox_free(msg_p);
        }
    }
}

void app_ibrt_peripheral_automate_test_handler(uint8_t* cmd_buf, uint32_t cmd_len)
{
#ifdef BES_AUTOMATE_TEST
    AUTO_TEST_CMD_T *test_cmd = (AUTO_TEST_CMD_T *)cmd_buf;
    static uint8_t last_group_code = 0xFF;
    static uint8_t last_operation_code = 0xFF;

    //TRACE(4, "%s group 0x%x op 0x%x times %d len %d", __func__, 
                //test_cmd->group_code, test_cmd->opera_code, test_cmd->test_times, test_cmd->param_len);
    //TRACE(2, "last group 0x%x last op 0x%x", last_group_code, last_operation_code);
    if (last_group_code != test_cmd->group_code || last_operation_code != test_cmd->opera_code)
    {
        for (uint8_t i=0; i<test_cmd->test_times; i++)
        {
            last_group_code = test_cmd->group_code;
            last_operation_code = test_cmd->opera_code;
            app_ibrt_ui_automate_test_cmd_handler(test_cmd->group_code, test_cmd->opera_code, test_cmd->param, test_cmd->param_len);
        }
    }
    app_ibrt_peripheral_heap_free(cmd_buf);
#endif
}

extern "C" void app_ibrt_peripheral_automate_test(const char* ibrt_cmd, uint32_t cmd_len)
{
#ifdef BES_AUTOMATE_TEST
    uint16_t crc16_rec = 0;
    uint16_t crc16_result = 0;
    uint8_t *cmd_buf = NULL;
    uint32_t _cmd_data_len = 0;
    uint32_t _cmd_data_min_len = sizeof(AUTO_TEST_CMD_T)+AUTOMATE_TEST_CMD_CRC_RECORD_LEN;
    TWS_PD_MSG_BLOCK msg;

    if (ibrt_cmd && cmd_len>=_cmd_data_min_len && cmd_len<=(_cmd_data_min_len+AUTOMATE_TEST_CMD_PARAM_MAX_LEN))
    {
        _cmd_data_len = cmd_len-AUTOMATE_TEST_CMD_CRC_RECORD_LEN;
        crc16_rec = *(uint16_t *)(&ibrt_cmd[_cmd_data_len]);
        crc16_result = _crc16(crc16_result, (const unsigned char *)ibrt_cmd, _cmd_data_len);
        //DUMP8("0x%x ", ibrt_cmd, cmd_len);
        //TRACE(4, "%s crc16 rec 0x%x result 0x%x buf_len %d", __func__, crc16_rec, crc16_result, cmd_len);
        if (crc16_rec == crc16_result && app_ibrt_auto_test_started)
        {
            app_ibrt_auto_test_inform_cmd_received(ibrt_cmd[0], ibrt_cmd[1]);
            cmd_buf = (uint8_t *)app_ibrt_peripheral_heap_cmalloc(_cmd_data_len);
            memcpy(cmd_buf, ibrt_cmd, _cmd_data_len);
            msg.msg_body.message_id = 0xfe;
            msg.msg_body.message_Param0 = (uint32_t)cmd_buf;
            msg.msg_body.message_Param1 = _cmd_data_len;
            app_ibrt_peripheral_mailbox_put(&msg);
        }
        return;
    }

    //ASSERT(0, "%s ibrt_cmd %p cmd_len %d", __func__, ibrt_cmd, cmd_len);
#endif
}

extern "C" void app_ibrt_peripheral_perform_test(const char* ibrt_cmd)
{
    TWS_PD_MSG_BLOCK msg;
    msg.msg_body.message_id = 0xff;
    memcpy(&msg.msg_body.message_Param0, ibrt_cmd+0, 4);
    memcpy(&msg.msg_body.message_Param1, ibrt_cmd+4, 4);
    memcpy(&msg.msg_body.message_Param2, ibrt_cmd+8, 4);
    app_ibrt_peripheral_mailbox_put(&msg);
}

void app_ibrt_peripheral_run0(uint32_t ptr)
{
    TWS_PD_MSG_BLOCK msg;
    msg.msg_body.message_id = 0;
    msg.msg_body.message_ptr = ptr;
    app_ibrt_peripheral_mailbox_put(&msg);
}

void app_ibrt_peripheral_run1(uint32_t ptr, uint32_t param0)
{
    TWS_PD_MSG_BLOCK msg;
    msg.msg_body.message_id = 1;
    msg.msg_body.message_ptr = ptr;
    msg.msg_body.message_Param0 = param0;
    app_ibrt_peripheral_mailbox_put(&msg);
}

void app_ibrt_peripheral_run2(uint32_t ptr, uint32_t param0, uint32_t param1)
{
    TWS_PD_MSG_BLOCK msg;
    msg.msg_body.message_id = 2;
    msg.msg_body.message_ptr = ptr;
    msg.msg_body.message_Param0 = param0;
    msg.msg_body.message_Param1 = param1;
    app_ibrt_peripheral_mailbox_put(&msg);
}

void app_ibrt_peripheral_thread_init(void)
{
    if (app_ibrt_peripheral_mailbox_init())
        return;

    app_ibrt_peripheral_tid = osThreadCreate(osThread(app_ibrt_peripheral_thread), NULL);
    if (app_ibrt_peripheral_tid == NULL)  {
        TRACE(0,"Failed to Create app_ibrt_peripheral_thread\n");
        return;
    }

#ifdef BES_AUTOMATE_TEST
    app_ibrt_peripheral_heap_init();
#endif
    return;
}
#endif

