/***************************************************************************
*
*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.
*
****************************************************************************/

/*****************************header include********************************/
#include "string.h"
#include "co_math.h" // Common Maths Definition
#include "cmsis_os.h"
#include "ble_app_dbg.h"
#include "stdbool.h"
#include "app_thread.h"
#include "app_utils.h"
#include "apps.h"
#include "app.h"
#include "app_sec.h"
#include "app_ble_include.h"
#include "nvrecord.h"
#include "app_bt_func.h"
#include "hal_timer.h"
#include "app_bt.h"
#include "app_hfp.h"
#include "rwprf_config.h"
#include "nvrecord_ble.h"
#include "app_sec.h"

/************************private macro defination***************************/
#define DEBUG_BLE_STATE_MACHINE true

#if DEBUG_BLE_STATE_MACHINE
#define SET_BLE_STATE(newState)                                                                                \
    do                                                                                                         \
    {                                                                                                          \
        LOG_I("[STATE]%s->%s at line %d", ble_state2str(bleModeEnv.state), ble_state2str(newState), __LINE__); \
        bleModeEnv.state = (newState);                                                                         \
    } while (0);

#define SET_BLE_OP(newOp)                                                                       \
    do                                                                                          \
    {                                                                                           \
        LOG_I("[OP]%s->%s at line %d", ble_op2str(bleModeEnv.op), ble_op2str(newOp), __LINE__); \
        bleModeEnv.op = (newOp);                                                                \
    } while (0);
#else
#define SET_BLE_STATE(newState)        \
    do                                 \
    {                                  \
        bleModeEnv.state = (newState); \
    } while (0);

#define SET_BLE_OP(newOp)        \
    do                           \
    {                            \
        bleModeEnv.op = (newOp); \
    } while (0);
#endif


extern void bt_drv_reg_op_set_rand_seed(uint32_t seed);

/************************private type defination****************************/

/************************extern function declearation***********************/
extern uint8_t bt_addr[6];

/**********************private function declearation************************/
/*---------------------------------------------------------------------------
 *            ble_state2str
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    get the string of the ble state
 *
 * Parameters:
 *    state - state to get string
 *
 * Return:
 *    the string of the BLE state
 */
static char *ble_state2str(uint8_t state);

/*---------------------------------------------------------------------------
 *            ble_op2str
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    get the string of the ble operation
 *
 * Parameters:
 *    op - operation to get string
 *
 * Return:
 *    the string of the BLE operation
 */
static char *ble_op2str(uint8_t op);

/*---------------------------------------------------------------------------
 *            ble_adv_user2str
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    get the string of the ble_adv_user
 *
 * Parameters:
 *    op - operation to get string
 *
 * Return:
 *    the string of the ble_adv_user
 */
static char *ble_adv_user2str(enum BLE_ADV_USER_E user);

/*---------------------------------------------------------------------------
 *            ble_adv_config_param
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    configure BLE adv related parameter in @advParam for further use
 *
 * Parameters:
 *    advType - advertisment mode, see @ for more info
 *    advInterval - advertisement interval in MS
 *
 * Return:
 *    void
 */
static void ble_adv_config_param(uint8_t advType, uint16_t advInterval);

/*---------------------------------------------------------------------------
 *            ble_adv_is_allowed
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    check if BLE advertisment is allowed or not
 *
 * Parameters:
 *    void
 *
 * Return:
 *    true - if advertisement is allowed
 *    flase -  if adverstisement is not allowed
 */
static bool ble_adv_is_allowed(void);

/*---------------------------------------------------------------------------
 *            ble_start_adv
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    start BLE advertisement
 *
 * Parameters:
 *    param - see @BLE_ADV_PARAM_T to get more info
 *
 * Return:
 *    void
 */
static void ble_start_adv(void *param);

/*---------------------------------------------------------------------------
 *            ble_start_adv_failed_cb
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    callback function of start BLE advertisement failed
 *
 * Parameters:
 *    void
 *
 * Return:
 *    void
 */
static void ble_start_adv_failed_cb(void);

/*---------------------------------------------------------------------------
 *            ble_start_scan
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    start BLE scan api
 *
 * Parameters:
 *    param - see @BLE_SCAN_PARAM_T to get more info
 *
 * Return:
 *    void
 */
static void ble_start_scan(void *param);

/*---------------------------------------------------------------------------
 *            ble_start_connect
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    start BLE connection
 *
 * Parameters:
 *    bleBdAddr - address of BLE device to connect
 *
 * Return:
 *    void
 */
static void ble_start_connect(uint8_t *bleBdAddr);

/*---------------------------------------------------------------------------
 *            ble_stop_all_activities
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    stop all BLE pending operations, stop ongoing adv and scan
 *    NOTE: will not disconnect BLE connections
 *
 * Parameters:
 *    void
 *
 * Return:
 *    void
 */
static void ble_stop_all_activities(void);

/*---------------------------------------------------------------------------
 *            ble_execute_pending_op
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    execute pended BLE op
 *
 * Parameters:
 *    void
 *
 * Return:
 *    void
 */
static void ble_execute_pending_op(void);

/*---------------------------------------------------------------------------
 *            ble_switch_activities
 *---------------------------------------------------------------------------
 *
 *Synopsis:
 *    switch BLE activities after last state complete
 *
 * Parameters:
 *    void
 *
 * Return:
 *    void
 */
static void ble_switch_activities(void);

/************************private variable defination************************/
static BLE_MODE_ENV_T bleModeEnv;
static BLE_ADV_PARAM_T advParam;
static BLE_SCAN_PARAM_T scanParam;

/****************************function defination****************************/
// common used function
#ifdef USE_LOG_I_ID
static inline char *ble_state2str(uint8_t state)
{
    return (char *)(uint32_t)state;
}
static inline char *ble_op2str(uint8_t op)
{
    return (char *)(uint32_t)op;
}
static inline char *ble_adv_user2str(enum BLE_ADV_USER_E user)
{
    return (char *)(uint32_t)user;
}

#else
static char *ble_state2str(uint8_t state)
{
    char *str = NULL;

#define CASES(state)          \
    case state:               \
        str = "[" #state "]"; \
        break

    switch (state)
    {
        CASES(STATE_IDLE);
        CASES(ADVERTISING);
        CASES(STARTING_ADV);
        CASES(STOPPING_ADV);
        CASES(SCANNING);
        CASES(STARTING_SCAN);
        CASES(STOPPING_SCAN);
        CASES(CONNECTING);
        CASES(STARTING_CONNECT);
        CASES(STOPPING_CONNECT);

    default:
        str = "[INVALID]";
        break;
    }

    return str;
}

static char *ble_op2str(uint8_t op)
{
    char *str = NULL;

#define CASEO(op)          \
    case op:               \
        str = "[" #op "]"; \
        break

    switch (op)
    {
        CASEO(OP_IDLE);
        CASEO(START_ADV);
        CASEO(START_SCAN);
        CASEO(START_CONNECT);
        CASEO(STOP_ADV);
        CASEO(STOP_SCAN);
        CASEO(STOP_CONNECT);

    default:
        str = "[INVALID]";
        break;
    }

    return str;
}

static char *ble_adv_user2str(enum BLE_ADV_USER_E user)
{
#define CASE_S(s) \
    case s:       \
        return "[" #s "]";
#define CASE_D() \
    default:     \
        return "[INVALID]";

    switch (user)
    {
        CASE_S(USER_STUB)
        CASE_S(USER_GFPS)
        CASE_S(USER_GSOUND)
        CASE_S(USER_AI)
        CASE_S(USER_INTERCONNECTION)
        CASE_S(USER_TILE)
        CASE_S(USER_OTA)
        CASE_D()
    }
}
#endif


void app_ble_mode_init(void)
{
    LOG_I("%s", __func__);

    memset(&bleModeEnv, 0, sizeof(bleModeEnv));
    SET_BLE_STATE(STATE_IDLE);
    SET_BLE_OP(OP_IDLE);

    bleModeEnv.bleEnv = &app_env;
}

// ble advertisement used functions
static void ble_adv_config_param(uint8_t advType, uint16_t advInterval)
{
    uint8_t avail_space;
    memset(&advParam, 0, sizeof(advParam));

    advParam.advType = advType;
    advParam.advInterval = advInterval;
    advParam.withFlag = true;

    // connectable adv is not allowed if max connection reaches
    if (app_is_arrive_at_max_ble_connections() && (GAPM_ADV_UNDIRECT == advType))
    {
        LOG_W("will change adv type to none-connectable because max ble connection reaches");
        advParam.advType = GAPM_ADV_NON_CONN;
    }

    for (uint8_t user = 0; user < BLE_ADV_USER_NUM; user++)
    {
        if (bleModeEnv.bleDataFillFunc[user])
        {
            bleModeEnv.bleDataFillFunc[user]((void *)&advParam);

            // check if the adv/scan_rsp data length is legal
            if(advParam.withFlag)
            {
                ASSERT(BLE_ADV_DATA_WITH_FLAG_LEN >= advParam.advDataLen, "[BLE][ADV]adv data exceed");
            }
            else
            {
                ASSERT(BLE_ADV_DATA_WITHOUT_FLAG_LEN >= advParam.advDataLen, "[BLE][ADV]adv data exceed");
            }
            ASSERT(SCAN_RSP_DATA_LEN >= advParam.scanRspDataLen, "[BLE][ADV]scan response data exceed");
        }
    }

    if(advParam.withFlag)
    {
        avail_space = BLE_ADV_DATA_WITH_FLAG_LEN - advParam.advDataLen - BLE_ADV_DATA_STRUCT_HEADER_LEN;
    }
    else
    {
        avail_space = BLE_ADV_DATA_WITHOUT_FLAG_LEN - advParam.advDataLen - BLE_ADV_DATA_STRUCT_HEADER_LEN;
    }

    // Check if data can be added to the adv Data
    if (avail_space > 2)
    {
        avail_space = co_min(avail_space, bleModeEnv.bleEnv->dev_name_len);
        advParam.advData[advParam.advDataLen++] = avail_space + 1;
        // Fill Device Name Flag
        advParam.advData[advParam.advDataLen++] =
            (avail_space == bleModeEnv.bleEnv->dev_name_len) ? '\x08' : '\x09';
        // Copy device name
        memcpy(&advParam.advData[advParam.advDataLen], bleModeEnv.bleEnv->dev_name, avail_space);
        // Update adv Data Length
        advParam.advDataLen += avail_space;
    }
}

static bool ble_adv_is_allowed(void)
{
    bool allowed_adv = true;
    if (!app_is_stack_ready())
    {
        LOG_I("reason: stack not ready");
        allowed_adv = false;
    }

    if (app_is_power_off_in_progress())
    {
        LOG_I("reason: in power off mode");
        allowed_adv = false;
    }

    if (bleModeEnv.advSwitch)
    {
        LOG_I("adv switched off:%d", bleModeEnv.advSwitch);
        allowed_adv = false;
    }

    if (btapp_hfp_is_sco_active())
    {
        LOG_I("SCO ongoing");
        allowed_adv = false;
    }

    if (false == allowed_adv)
    {
        app_ble_stop_activities();
    }

    return allowed_adv;
}

static void ble_start_adv(void *param)
{
    switch (bleModeEnv.state)
    {
    case ADVERTISING:
        SET_BLE_STATE(STOPPING_ADV);
        SET_BLE_OP(START_ADV);
        appm_stop_advertising();
        break;

    case SCANNING:
        SET_BLE_STATE(STOPPING_SCAN);
        SET_BLE_OP(START_ADV);
        appm_stop_scanning();
        break;

    case CONNECTING:
        SET_BLE_STATE(STOPPING_CONNECT);
        SET_BLE_OP(START_ADV);
        appm_stop_connecting();
        break;

    case STARTING_ADV:
    case STARTING_SCAN:
    case STARTING_CONNECT:
    case STOPPING_ADV:
    case STOPPING_SCAN:
    case STOPPING_CONNECT:
        SET_BLE_OP(START_ADV);
        break;

    case STATE_IDLE:
        if (!ble_adv_is_allowed())
        {
            LOG_I("[ADV] not allowed.");
            if (START_ADV == bleModeEnv.op)
            {
                SET_BLE_OP(OP_IDLE);
            }
            break;
        }

        memcpy(&bleModeEnv.advInfo, param, sizeof(bleModeEnv.advInfo));
        appm_start_advertising(&bleModeEnv.advInfo);

        SET_BLE_STATE(STARTING_ADV);
        break;

    default:
        break;
    }
}

static void ble_start_adv_failed_cb(void)
{
    if (STARTING_ADV == bleModeEnv.state)
    {
        SET_BLE_STATE(STATE_IDLE);
    }

    // start pending op(start adv again)
}

static void ble_start_scan(void *param)
{
    switch (bleModeEnv.state)
    {
    case ADVERTISING:
        SET_BLE_STATE(STOPPING_ADV);
        SET_BLE_OP(START_SCAN);
        appm_stop_advertising();
        break;

    case SCANNING:
        SET_BLE_STATE(STOPPING_SCAN);
        SET_BLE_OP(START_SCAN);
        appm_stop_scanning();
        break;

    case CONNECTING:
        SET_BLE_STATE(STOPPING_CONNECT);
        SET_BLE_OP(START_SCAN);
        appm_stop_connecting();
        break;

    case STARTING_ADV:
    case STARTING_SCAN:
    case STARTING_CONNECT:
    case STOPPING_ADV:
    case STOPPING_SCAN:
    case STOPPING_CONNECT:
        SET_BLE_OP(START_SCAN);
        break;

    case STATE_IDLE:

        SET_BLE_STATE(STARTING_SCAN);
        memcpy(&bleModeEnv.scanInfo, param, sizeof(BLE_SCAN_PARAM_T));
        appm_start_scanning(bleModeEnv.scanInfo.scanInterval,
                            bleModeEnv.scanInfo.scanWindow,
                            bleModeEnv.scanInfo.scanType);
        break;

    default:
        break;
    }
}

static void ble_start_connect(uint8_t *bleBdAddr)
{
    SET_BLE_OP(START_CONNECT);

    if ((CONNECTING != bleModeEnv.state) &&
        (STARTING_CONNECT != bleModeEnv.state))
    {
        switch (bleModeEnv.state)
        {
        case ADVERTISING:
            SET_BLE_STATE(STOPPING_ADV);
            appm_stop_advertising();
            break;

        case SCANNING:
            SET_BLE_STATE(STOPPING_SCAN);
            appm_stop_scanning();
            break;

        case STATE_IDLE:
            SET_BLE_STATE(STARTING_CONNECT);
            struct gap_bdaddr bdAddr;
            memcpy(bdAddr.addr.addr, bleBdAddr, BTIF_BD_ADDR_SIZE);
            bdAddr.addr_type = 0;
            LOG_I("Master paired with mobile dev is scanned, connect it via BLE.");
            appm_start_connecting(&bdAddr);
            break;

        default:
            break;
        }
    }
}

static void ble_stop_all_activities(void)
{
    switch (bleModeEnv.state)
    {
    case ADVERTISING:
        SET_BLE_OP(OP_IDLE);
        SET_BLE_STATE(STOPPING_ADV);
        appm_stop_advertising();
        break;

    case SCANNING:
        SET_BLE_OP(OP_IDLE);
        SET_BLE_STATE(STOPPING_SCAN);
        appm_stop_scanning();
        break;

    case CONNECTING:
        SET_BLE_OP(OP_IDLE);
        SET_BLE_STATE(STOPPING_CONNECT);
        appm_stop_connecting();
        break;

    case STARTING_ADV:
        SET_BLE_OP(STOP_ADV);
        break;

    case STARTING_SCAN:
        SET_BLE_OP(STOP_SCAN);
        break;

    case STARTING_CONNECT:
        SET_BLE_OP(STOP_CONNECT);
        break;

    case STOPPING_ADV:
    case STOPPING_SCAN:
    case STOPPING_CONNECT:
        SET_BLE_OP(OP_IDLE);
        break;

    default:
        break;
    }
}

static void ble_execute_pending_op(void)
{
    LOG_I("%s", __func__);
    uint8_t op = bleModeEnv.op;
    SET_BLE_OP(OP_IDLE);

    switch (op)
    {
    case START_ADV:
        ble_start_adv(&advParam);
        break;

    case START_SCAN:
        ble_start_scan(&scanParam);
        break;

    case START_CONNECT:
        ble_start_connect(bleModeEnv.bleAddrToConnect);
        break;

    case STOP_ADV:
    case STOP_SCAN:
    case STOP_CONNECT:
        ble_stop_all_activities();
        break;

    default:
        break;
    }
}

static void ble_switch_activities(void)
{
    switch (bleModeEnv.state)
    {
    case STARTING_ADV:
        SET_BLE_STATE(ADVERTISING);
        break;

    case STARTING_SCAN:
        SET_BLE_STATE(SCANNING);
        break;

    case STARTING_CONNECT:
        SET_BLE_STATE(CONNECTING);
        break;

    case STOPPING_ADV:
        SET_BLE_STATE(STATE_IDLE);
        break;

    case STOPPING_SCAN:
        SET_BLE_STATE(STATE_IDLE);
        break;

    case STOPPING_CONNECT:
        SET_BLE_STATE(STATE_IDLE);
        break;

    default:
        break;
    }

    ble_execute_pending_op();
}

// BLE advertisement event callbacks
void app_advertising_started(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_switch_activities);
}

void app_advertising_starting_failed(void)
{
    memset(&bleModeEnv.advInfo, 0, sizeof(bleModeEnv.advInfo));
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_start_adv_failed_cb);
}

void app_advertising_stopped(void)
{
    memset(&bleModeEnv.advInfo, 0, sizeof(bleModeEnv.advInfo));
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              ( uint32_t )ble_switch_activities);
}

// BLE adv data updated event callback
void app_adv_data_updated(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
            0,
            (uint32_t)ble_switch_activities);
}

// BLE scan event callbacks
void app_scanning_started(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_switch_activities);
}

void app_scanning_stopped(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_switch_activities);
}

// BLE connect event callbacks
void app_connecting_started(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_switch_activities);
}

void app_connecting_stopped(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)ble_switch_activities);
}

/**
 * @brief : callback function of BLE connect failed
 *
 */
static void app_ble_connecting_failed_handler(void)
{
    if ((CONNECTING == bleModeEnv.state) ||
        (STOPPING_CONNECT == bleModeEnv.state) ||
        (STARTING_CONNECT == bleModeEnv.state) )
    {
        SET_BLE_STATE(STATE_IDLE);
    }
}

void app_connecting_failed(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)app_ble_connecting_failed_handler);
}
void app_ble_connected_evt_handler(uint8_t conidx, const uint8_t *pPeerBdAddress)
{
    ble_evnet_t event;

    if ((ADVERTISING == bleModeEnv.state) ||
        (STARTING_ADV == bleModeEnv.state) ||
        (CONNECTING == bleModeEnv.state))
    {
        SET_BLE_STATE(STATE_IDLE);
    }

    if (START_CONNECT == bleModeEnv.op)
    {
        SET_BLE_OP(OP_IDLE);
    }

    event.evt_type = BLE_CONNECT_EVENT;
    event.p.connect_handled.conidx = conidx;
    event.p.connect_handled.peer_bdaddr = pPeerBdAddress;
    app_ble_core_global_handle(&event, NULL);


    app_ble_refresh_adv_state(BLE_ADVERTISING_INTERVAL);
    app_stop_fast_connectable_ble_adv_timer();
}

void app_ble_disconnected_evt_handler(uint8_t conidx)
{
    ble_evnet_t event;

    event.evt_type = BLE_DISCONNECT_EVENT;
    event.p.disconnect_handled.conidx = conidx;
    app_ble_core_global_handle(&event, NULL);

    app_ble_refresh_adv_state(BLE_ADVERTISING_INTERVAL);
}

// BLE APIs for external use
void app_ble_data_fill_enable(enum BLE_ADV_USER_E user, bool enable)
{
    LOG_I("%s user %d%s enable %d", __func__, user, ble_adv_user2str(user), enable);

    ASSERT(user < BLE_ADV_USER_NUM, "%s user %d", __func__, user);
    if (enable)
    {
        bleModeEnv.adv_user_enable |= (1 << user);
    }
    else
    {
        bleModeEnv.adv_user_enable &= ~(1 << user);
    }
}

bool app_ble_get_data_fill_enable(enum BLE_ADV_USER_E user)
{
    bool enable = bleModeEnv.adv_user_enable & (1 << user);
    LOG_I("%s user %d enable %d", __func__, user, enable);
    return enable;
}


/**
 * @brief : callback function of BLE scan starting failed
 *
 */
static void app_ble_scanning_starting_failed_handler(void)
{
    if (STARTING_SCAN == bleModeEnv.state)
    {
        SET_BLE_STATE(STATE_IDLE);
    }
}

void app_scanning_starting_failed(void)
{
    app_bt_start_custom_function_in_bt_thread(0,
                                              0,
                                              (uint32_t)app_ble_scanning_starting_failed_handler);
}

// BLE APIs for external use
void app_ble_register_data_fill_handle(enum BLE_ADV_USER_E user, BLE_DATA_FILL_FUNC_T func, bool enable)
{
    bool needUpdateAdv = false;

    if (BLE_ADV_USER_NUM <= user)
    {
        LOG_W("invalid user");
    }
    else
    {
        if (func != bleModeEnv.bleDataFillFunc[user] &&
            NULL != func)
        {
            needUpdateAdv = true;
            bleModeEnv.bleDataFillFunc[user] = func;
        }
    }

    bleModeEnv.adv_user_register |= (1 << user);
    if (needUpdateAdv)
    {
        app_ble_data_fill_enable(user, enable);
    }
}

void app_ble_system_ready(void)
{
    uint32_t generatedSeed = hal_sys_timer_get();

    for (uint8_t index = 0; index < sizeof(bt_addr); index++)
    {
        generatedSeed ^= (((uint32_t)(bt_addr[index])) << (hal_sys_timer_get() & 0xF));
    }

    bt_drv_reg_op_set_rand_seed(generatedSeed);

#if defined(ENHANCED_STACK)
    app_notify_stack_ready(STACK_READY_BLE);
#else
    app_notify_stack_ready(STACK_READY_BLE | STACK_READY_BT);
#endif
}

static void ble_adv_refreshing(void *param)
{
    BLE_ADV_PARAM_T *pAdvParam = (BLE_ADV_PARAM_T *)param;
    // four conditions that we just need to update the ble adv data instead of restarting ble adv
    // 1. BLE advertising is on
    // 2. No on-going BLE operation
    // 3. BLE adv type is the same
    // 4. BLE adv interval is the same
    if ((ADVERTISING == bleModeEnv.state) && \
        (OP_IDLE == bleModeEnv.op) && \
        bleModeEnv.advInfo.advType == pAdvParam->advType && \
        bleModeEnv.advInfo.advInterval == pAdvParam->advInterval)
    {
        memcpy(&bleModeEnv.advInfo, param, sizeof(bleModeEnv.advInfo));
        SET_BLE_STATE(STARTING_ADV);
        appm_update_adv_data(pAdvParam->advData,
                             pAdvParam->advDataLen,
                             pAdvParam->scanRspData,
                             pAdvParam->scanRspDataLen);
    }
    else
    {
        // otherwise, restart ble adv
        ble_start_adv(param);
    }
}

static bool app_ble_start_adv(uint8_t advType, uint16_t advInterval)
{
    uint32_t adv_user_enable = bleModeEnv.adv_user_enable;
    LOG_I("[ADV]type:%d, interval:%d ca:%p", advType, advInterval, __builtin_return_address(0));

    if (!ble_adv_is_allowed())
    {
        LOG_I("[ADV] not allowed.");
        return false;
    }

    ble_adv_config_param(advType, advInterval);

    LOG_I("%s old_user_enable 0x%x new 0x%x", __func__, adv_user_enable, bleModeEnv.adv_user_enable);
    if (!bleModeEnv.adv_user_enable)
    {
        LOG_I("no adv user enable");
        LOG_I("[ADV] not allowed.");
        app_ble_stop_activities();
        return false;
    }

    // param of adv request is exactly same as current adv
    if (ADVERTISING == bleModeEnv.state &&
        !memcmp(&bleModeEnv.advInfo, &advParam, sizeof(advParam)))
    {
        LOG_I("reason: adv param not changed");
        LOG_I("[ADV] not allowed.");
        return false;
    }

    LOG_I("[ADV_LEN] %d [DATA]:", advParam.advDataLen);
    DUMP8("%02x ", advParam.advData, advParam.advDataLen);
    LOG_I("[SCAN_RSP_LEN] %d [DATA]:", advParam.scanRspDataLen);
    DUMP8("%02x ", advParam.scanRspData, advParam.scanRspDataLen);

    ble_adv_refreshing(&advParam);

    return true;
}

void app_ble_start_connectable_adv(uint16_t advInterval)
{
    LOG_D("%s", __func__);
    app_bt_start_custom_function_in_bt_thread(
        (uint32_t)GAPM_ADV_UNDIRECT, (uint32_t)advInterval,
        (uint32_t)app_ble_start_adv);
}

void app_ble_refresh_adv_state(uint16_t advInterval)
{
    LOG_D("%s", __func__);
    app_bt_start_custom_function_in_bt_thread(
        (uint32_t)GAPM_ADV_UNDIRECT, (uint32_t)advInterval,
        (uint32_t)app_ble_start_adv);
}

void app_ble_start_scan(enum BLE_SCAN_FILTER_POLICY scanFilterPolicy, uint16_t scanWindow, uint16_t scanInterval)
{
    scanParam.scanWindow = scanWindow;
    scanParam.scanInterval = scanInterval;
    scanParam.scanType = scanFilterPolicy; //BLE_SCAN_ALLOW_ADV_WLST

    app_bt_start_custom_function_in_bt_thread((uint32_t)(&scanParam),
                                              0,
                                              (uint32_t)ble_start_scan);
}

void app_ble_start_connect(uint8_t *bdAddrToConnect)
{
    memcpy(bleModeEnv.bleAddrToConnect, bdAddrToConnect, BTIF_BD_ADDR_SIZE);
    app_bt_start_custom_function_in_bt_thread((uint32_t)bleModeEnv.bleAddrToConnect,
                                              0,
                                              (uint32_t)ble_start_connect);
}

bool app_ble_is_connection_on(uint8_t index)
{
    return (BLE_CONNECTED == 
        bleModeEnv.bleEnv->context[index].connectStatus);
}

bool app_ble_is_any_connection_exist(void)
{
    bool ret = false;
    for (uint8_t i = 0; i < BLE_CONNECTION_MAX; i++)
    {
        if (app_ble_is_connection_on(i))
        {
            ret = true;
        }
    }

    return ret;
}

void app_ble_start_disconnect(uint8_t conIdx)
{
    if (BLE_CONNECTED == bleModeEnv.bleEnv->context[conIdx].connectStatus)
    {
        LOG_I("will disconnect connection:%d", conIdx);
        bleModeEnv.bleEnv->context[conIdx].connectStatus = BLE_DISCONNECTING;
        app_bt_start_custom_function_in_bt_thread((uint32_t)conIdx,
                                                  0,
                                                  (uint32_t)appm_disconnect);
    }
    else
    {
        LOG_I("will not execute disconnect since state is:%d",
              bleModeEnv.bleEnv->context[conIdx].connectStatus);
    }
}

void app_ble_disconnect_all(void)
{
    for (uint8_t i = 0; i < BLE_CONNECTION_MAX; i++)
    {
        app_ble_start_disconnect(i);
    }
}

void app_ble_stop_activities(void)
{
    LOG_I("%s %p", __func__, __builtin_return_address(0));

    app_stop_fast_connectable_ble_adv_timer();
    if (bleModeEnv.state != OP_IDLE)
    {
        app_bt_start_custom_function_in_bt_thread(0,
                                                  0,
                                                  (uint32_t)ble_stop_all_activities);
    }
}

void app_ble_force_switch_adv(uint8_t user, bool onOff)
{
    ASSERT(user < BLE_SWITCH_USER_NUM, "ble switch user exceed");

    if (onOff)
    {
        bleModeEnv.advSwitch &= ~(1 << user);
        app_ble_refresh_adv_state(BLE_ADVERTISING_INTERVAL);
    }
    else if ((bleModeEnv.advSwitch & (1 << user)) == 0)
    {
        bleModeEnv.advSwitch |= (1 << user);
        app_ble_stop_activities();

        // disconnect all of the BLE connections if box is closed
        if (BLE_SWITCH_USER_BOX == user)
        {
            app_ble_disconnect_all();
        }
    }

    LOG_I("%s user %d onoff %d switch 0x%x", __func__, user, onOff, bleModeEnv.advSwitch);
}

bool app_ble_is_in_advertising_state(void)
{
    return (ADVERTISING == bleModeEnv.state) ||
           (STARTING_ADV == bleModeEnv.state) ||
           (STOPPING_ADV == bleModeEnv.state);
}

static uint32_t POSSIBLY_UNUSED ble_get_manufacture_data_ptr(uint8_t *advData,
                                                             uint32_t dataLength,
                                                             uint8_t *manufactureData)
{
    uint8_t followingDataLengthOfSection;
    uint8_t rawContentDataLengthOfSection;
    uint8_t flag;
    while (dataLength > 0)
    {
        followingDataLengthOfSection = *advData++;
        dataLength--;
        if (dataLength < followingDataLengthOfSection)
        {
            return 0; // wrong adv data format
        }

        if (followingDataLengthOfSection > 0)
        {
            flag = *advData++;
            dataLength--;

            rawContentDataLengthOfSection = followingDataLengthOfSection - 1;
            if (BLE_ADV_MANU_FLAG == flag)
            {
                uint32_t lengthToCopy;
                if (dataLength < rawContentDataLengthOfSection)
                {
                    lengthToCopy = dataLength;
                }
                else
                {
                    lengthToCopy = rawContentDataLengthOfSection;
                }

                memcpy(manufactureData, advData - 2, lengthToCopy + 2);
                return lengthToCopy + 2;
            }
            else
            {
                advData += rawContentDataLengthOfSection;
                dataLength -= rawContentDataLengthOfSection;
            }
        }
    }

    return 0;
}

//received adv data
void app_adv_reported_scanned(struct gapm_adv_report_ind *ptInd)
{
    /*
    LOG_I("Scanned RSSI %d BD addr:", (int8_t)ptInd->report.rssi);
    DUMP8("0x%02x ", ptInd->report.adv_addr.addr, BTIF_BD_ADDR_SIZE);
    LOG_I("Scanned adv data:");
    DUMP8("0x%02x ", ptInd->report.data, ptInd->report.data_len);
    */

    ble_adv_data_parse(ptInd->report.adv_addr.addr,
                       (int8_t)ptInd->report.rssi,
                       ptInd->report.data,
                       (unsigned char)ptInd->report.data_len);
}

void app_ibrt_ui_disconnect_ble(void)
{
    app_ble_disconnect_all();
}

uint32_t app_ble_get_user_register(void)
{
    return bleModeEnv.adv_user_register;
}

enum BLE_STATE_E app_ble_get_current_state(void)
{
    return bleModeEnv.state;
}

enum BLE_OP_E app_ble_get_current_operation(void)
{
    return bleModeEnv.op;
}

void app_ble_get_runtime_adv_param(uint8_t* pAdvType, uint16_t* pAdvIntervalMs)
{
    *pAdvType = advParam.advType;
    *pAdvIntervalMs = advParam.advInterval;
}

