/***************************************************************************
 *
 * 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.
 *
 ****************************************************************************/
/**
 ****************************************************************************************
 * @addtogroup APPTASK
 * @{
 ****************************************************************************************
 */

/*
 * INCLUDE FILES
 ****************************************************************************************
 */

#include "rwip_config.h"          // SW configuration

#if (BLE_APP_PRESENT)

#include "app_task.h"             // Application Manager Task API
#include "app.h"                  // Application Manager Definition
#include "gapc_task.h"            // GAP Controller Task API
#include "gapm_task.h"            // GAP Manager Task API
#include "arch.h"                 // Platform Definitions
#include <string.h>
#include "ke_timer.h"             // Kernel timer
#include "gattc_task.h"
#include "app_ble_include.h"
#include "../l2cm/l2cm_int.h" 

#include "co_utils.h"
#if (BLE_APP_SEC)
#include "app_sec.h"              // Security Module Definition
#endif //(BLE_APP_SEC)

#if (BLE_APP_HT)
#include "app_ht.h"               // Health Thermometer Module Definition
#include "htpt_task.h"
#endif //(BLE_APP_HT)

#if (BLE_APP_DIS)
#include "app_dis.h"              // Device Information Module Definition
#include "diss_task.h"
#endif //(BLE_APP_DIS)

#if (BLE_APP_BATT)
#include "app_batt.h"             // Battery Module Definition
#include "bass_task.h"
#endif //(BLE_APP_BATT)

#if (BLE_APP_HID)
#include "app_hid.h"              // HID Module Definition
#include "hogpd_task.h"
#endif //(BLE_APP_HID)

#if (BLE_APP_HR)
#include "app_hrps.h"
#endif

#if (BLE_APP_VOICEPATH)
#include "app_voicepath_ble.h"       // Voice Path Module Definition
#endif // (BLE_APP_VOICEPATH)

#if (BLE_APP_DATAPATH_SERVER)
#include "app_datapath_server.h"       // Data Path Server Module Definition
#include "datapathps_task.h"
#endif // (BLE_APP_DATAPATH_SERVER)

#if (BLE_APP_AI_VOICE)
#include "app_ai_ble.h"       // ama Voice Module Definition
#endif // (BLE_APP_AI_VOICE)

#if (BLE_APP_OTA)
#include "app_ota.h"       // OTA Module Definition
#include "ota_task.h"
#endif // (BLE_APP_OTA)

#if (BLE_APP_TOTA)
#include "app_tota_ble.h"       // TOTA Module Definition
#include "tota_task.h"
#endif // (BLE_APP_TOTA)

#if (BLE_APP_ANCC)
#include "app_ancc.h"       // ANC Module Definition
#include "app_ancc_task.h"
#include "ancc_task.h"
#endif // (BLE_APP_ANCC)

#if (BLE_APP_AMS)
#include "app_amsc.h"       // AMS Module Definition
#include "app_amsc_task.h"
#include "amsc_task.h"
#endif // (BLE_APP_AMS)
#if (BLE_APP_GFPS)
#include "app_gfps.h"       // google fast pair service provider
#include "gfps_provider_task.h"
#endif // (BLE_APP_GFPS)
#ifdef BLE_APP_AM0
#include "am0_app.h"             // Audio Mode 0 Application
#endif //defined(BLE_APP_AM0)

#if (DISPLAY_SUPPORT)
#include "app_display.h"          // Application Display Definition
#endif //(DISPLAY_SUPPORT)

#include "bt_drv_interface.h"
#include "ble_app_dbg.h"
#include "nvrecord_ble.h"
#include "app_fp_rfcomm.h"

#if (BLE_APP_TILE)
#include "tile_target_ble.h"
#include "tile_gatt_server.h"
#endif

/*
 * LOCAL FUNCTION DEFINITIONS
 ****************************************************************************************
 */
#define APP_CONN_PARAM_INTERVEL_MIN    (20)

uint8_t ble_stack_ready = 0;

extern bool app_factorymode_get(void);

static uint8_t appm_get_handler(const struct ke_state_handler *handler_list,
                                ke_msg_id_t msgid,
                                void *param,
                                ke_task_id_t src_id)
{
    // Counter
    uint8_t counter;

    // Get the message handler function by parsing the message table
    for (counter = handler_list->msg_cnt; 0 < counter; counter--)
    {
        struct ke_msg_handler handler = (struct ke_msg_handler)(*(handler_list->msg_table + counter - 1));

        if ((handler.id == msgid) ||
            (handler.id == KE_MSG_DEFAULT_HANDLER))
        {
            // If handler is NULL, message should not have been received in this state
            ASSERT_ERR(handler.func);

            return (uint8_t)(handler.func(msgid, param, TASK_APP, src_id));
        }
    }

    // If we are here no handler has been found, drop the message
    return (KE_MSG_CONSUMED);
}

/*
 * MESSAGE HANDLERS
 ****************************************************************************************
 */

/**
 ****************************************************************************************
 * @brief
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int app_adv_timeout_handler(ke_msg_id_t const msgid,
                                   void const *param,
                                   ke_task_id_t const dest_id,
                                   ke_task_id_t const src_id)
{
    #if (BLE_APP_HID)
    #else
    // Stop advertising
    appm_stop_advertising();
    #endif

    return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles ready indication from the GAP. - Reset the stack
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapm_device_ready_ind_handler(ke_msg_id_t const msgid,
                                         void const *param,
                                         ke_task_id_t const dest_id,
                                         ke_task_id_t const src_id)
{
    if (ble_stack_ready)
    {
        return KE_MSG_CONSUMED;
    }
    
#ifdef __FACTORY_MODE_SUPPORT__
    if (app_factorymode_get())
    {
        return (KE_MSG_CONSUMED);
    }
#endif

    BLE_APP_FUNC_ENTER();

    // Application has not been initialized
    ASSERT_ERR(ke_state_get(dest_id) == APPM_INIT);

    ble_stack_ready = 1;

    // Reset the stack
    struct gapm_reset_cmd* cmd = KE_MSG_ALLOC(GAPM_RESET_CMD,
                                              TASK_GAPM, TASK_APP,
                                              gapm_reset_cmd);

    cmd->operation = GAPM_RESET;

    ke_msg_send(cmd);

    BLE_APP_FUNC_LEAVE();

    return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles GAP manager command complete events.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapm_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gapm_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    BLE_APP_FUNC_ENTER();    
    BLE_APP_DBG("param->operation: %d status is %d app_env.next_svc is %d", 
        param->operation, param->status, app_env.next_svc);

    switch(param->operation)
    {
        // Reset completed
        case (GAPM_RESET):
        {
            if(param->status == GAP_ERR_NO_ERROR)
            {
                #if (BLE_APP_HID)
                app_hid_start_mouse();
                #endif //(BLE_APP_HID)

                // Set Device configuration
                struct gapm_set_dev_config_cmd* cmd = KE_MSG_ALLOC(GAPM_SET_DEV_CONFIG_CMD,
                                                                   TASK_GAPM, TASK_APP,
                                                                   gapm_set_dev_config_cmd);
                // Set the operation
                cmd->operation = GAPM_SET_DEV_CONFIG;
                // Set the device role - Peripheral
                cmd->role      = GAP_ROLE_ALL;
                // Set Data length parameters
                cmd->sugg_max_tx_octets = APP_MAX_TX_OCTETS;
                cmd->sugg_max_tx_time   = APP_MAX_TX_TIME;
                cmd->pairing_mode = GAPM_PAIRING_LEGACY;
                #ifdef CFG_SEC_CON
                cmd->pairing_mode |= GAPM_PAIRING_SEC_CON;
                #endif
                cmd->max_mtu = 512;
                cmd->att_cfg = 0;
                cmd->att_cfg |= GAPM_MASK_ATT_SVC_CHG_EN;
                #if (BLE_APP_HID)
                // Enable Slave Preferred Connection Parameters present 
                cmd->att_cfg |= GAPM_MASK_ATT_SLV_PREF_CON_PAR_EN;
                #endif //(BLE_APP_HID)

                #ifdef BLE_APP_AM0
                cmd->addr_type   = GAPM_CFG_ADDR_HOST_PRIVACY;
                cmd->audio_cfg   = GAPM_MASK_AUDIO_AM0_SUP;
                #endif // BLE_APP_AM0

                // load IRK
                memcpy(cmd->irk.key, app_env.loc_irk, KEY_LEN);

                // Send message
                ke_msg_send(cmd);
            }
            else
            {
                ASSERT_ERR(0);
            }
        }
        break;
        case (GAPM_PROFILE_TASK_ADD):
        {
           // ASSERT_INFO(param->status == GAP_ERR_NO_ERROR, param->operation, param->status);
            // Add the next requested service
            if (!appm_add_svc())
            {
                // Go to the ready state
                ke_state_set(TASK_APP, APPM_READY);

                // No more service to add
                app_ble_system_ready();
            }
        }
        break;
        // Device Configuration updated
        case (GAPM_SET_DEV_CONFIG):
        {
            ASSERT_INFO(param->status == GAP_ERR_NO_ERROR, param->operation, param->status);

            // Go to the create db state
            ke_state_set(TASK_APP, APPM_CREATE_DB);

            // Add the first required service in the database
            // and wait for the PROFILE_ADDED_IND
            appm_add_svc();
        }
        break;

        case (GAPM_ADV_NON_CONN):
        case (GAPM_ADV_UNDIRECT):
        #if !(BLE_APP_HID)
        case (GAPM_ADV_DIRECT):
        #endif// !(BLE_APP_HID)
        case (GAPM_ADV_DIRECT_LDC):
        {
            LOG_I("adv evt cmp status 0x%x", param->status);
            ASSERT(GAP_ERR_ADV_DATA_INVALID != param->status, 
                "The BLE adv data or scan rsp data is invalid! Better check their length.");
                
            if (GAP_ERR_CANCELED == param->status)
            {
                app_advertising_stopped();
            }
        else if (GAP_ERR_NO_ERROR == param->status)
        {
            if (ke_state_get(TASK_APP) == APPM_ADVERTISING)
            {
                app_advertising_started();
            }
            else
            {
                app_advertising_stopped();
            }
        }
        else
        {
            app_advertising_starting_failed();
        }
        
        break;
    }
    case GAPM_UPDATE_ADVERTISE_DATA:
    {
        app_adv_data_updated();
        break;
    }
        case GAPM_SCAN_ACTIVE:
        case GAPM_SCAN_PASSIVE:
        {
            LOG_I("scan evt cmp status %d", param->status);
            if (GAP_ERR_CANCELED == param->status)
            {
                app_scanning_stopped();
            }
            else if (GAP_ERR_NO_ERROR == param->status)
            {
                app_scanning_started();
            }
            else
            {
                app_scanning_starting_failed();
            }
        }
        break;
        case GAPM_CONNECTION_DIRECT:
        case GAPM_CONNECTION_AUTO:
        case GAPM_CONNECTION_SELECTIVE:
        case GAPM_CONNECTION_NAME_REQUEST:
        case GAPM_CONNECTION_GENERAL:
        {
            BLE_GAP_DBG("connecting cmp status %d", param->status);
            if (GAP_ERR_CANCELED == param->status)
            {
                app_connecting_stopped();
            }
            else if (GAP_ERR_NO_ERROR == param->status)
            {
                app_connecting_started();
            }
            else
            {
                app_connecting_failed();
            }
        }
        break;

        #if (BLE_APP_HID)
        case (GAPM_ADV_DIRECT):
        {
            if (param->status == GAP_ERR_TIMEOUT)
            {
                ke_state_set(TASK_APP, APPM_READY);
            }
        } break;
        #endif //(BLE_APP_HID)
        case GAPM_RESOLV_ADDR:
        {
            LOG_I("Resolve result %d", param->status);
            if (GAP_ERR_NOT_FOUND == param->status)
            {
                appm_random_ble_addr_solved(false, NULL);
            }
            break;
        }
        default:
        {
            // Drop the message
        }
        break;
    }

    BLE_APP_FUNC_LEAVE();

    return (KE_MSG_CONSUMED);
}

static int gapc_get_dev_info_req_ind_handler(ke_msg_id_t const msgid,
        struct gapc_get_dev_info_req_ind const *param,
        ke_task_id_t const dest_id,
        ke_task_id_t const src_id)
{
    switch(param->req)
    {
        case GAPC_DEV_NAME:
        {
            struct gapc_get_dev_info_cfm * cfm = KE_MSG_ALLOC_DYN(GAPC_GET_DEV_INFO_CFM,
                                                    src_id, dest_id,
                                                    gapc_get_dev_info_cfm, APP_DEVICE_NAME_MAX_LEN);
            cfm->req = param->req;
            cfm->info.name.length = appm_get_dev_name(cfm->info.name.value);

            // Send message
            ke_msg_send(cfm);
        } break;

        case GAPC_DEV_APPEARANCE:
        {
            // Allocate message
            struct gapc_get_dev_info_cfm *cfm = KE_MSG_ALLOC(GAPC_GET_DEV_INFO_CFM,
                                                             src_id, dest_id,
                                                             gapc_get_dev_info_cfm);
            cfm->req = param->req;
            // Set the device appearance
            #if (BLE_APP_HT)
            // Generic Thermometer - TODO: Use a flag
            cfm->info.appearance = 728;
            #elif (BLE_APP_HID)
            // HID Mouse
            cfm->info.appearance = 962;
            #else
            // No appearance
            cfm->info.appearance = 0;
            #endif

            // Send message
            ke_msg_send(cfm);
        } break;

        case GAPC_DEV_SLV_PREF_PARAMS:
        {
            // Allocate message
            struct gapc_get_dev_info_cfm *cfm = KE_MSG_ALLOC(GAPC_GET_DEV_INFO_CFM,
                    src_id, dest_id,
                                                            gapc_get_dev_info_cfm);
            cfm->req = param->req;
            // Slave preferred Connection interval Min
            cfm->info.slv_params.con_intv_min = 8;
            // Slave preferred Connection interval Max
            cfm->info.slv_params.con_intv_max = 10;
            // Slave preferred Connection latency
            cfm->info.slv_params.slave_latency  = 0;
            // Slave preferred Link supervision timeout
            cfm->info.slv_params.conn_timeout    = 200;  // 2s (500*10ms)

            // Send message
            ke_msg_send(cfm);
        } break;

        default: /* Do Nothing */ break;
    }

    return (KE_MSG_CONSUMED);
}
/**
 ****************************************************************************************
 * @brief Handles GAPC_SET_DEV_INFO_REQ_IND message.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapc_set_dev_info_req_ind_handler(ke_msg_id_t const msgid,
        struct gapc_set_dev_info_req_ind const *param,
        ke_task_id_t const dest_id,
        ke_task_id_t const src_id)
{
    // Set Device configuration
    struct gapc_set_dev_info_cfm* cfm = KE_MSG_ALLOC(GAPC_SET_DEV_INFO_CFM, src_id, dest_id,
                                                     gapc_set_dev_info_cfm);
    // Reject to change parameters
    cfm->status = GAP_ERR_REJECTED;
    cfm->req = param->req;
    // Send message
    ke_msg_send(cfm);

    return (KE_MSG_CONSUMED);
}

static void POSSIBLY_UNUSED gapc_refresh_remote_dev_feature(uint8_t conidx)
{
    // Send a GAPC_GET_INFO_CMD in order to read the device name characteristic value
    struct gapc_get_info_cmd *p_cmd = KE_MSG_ALLOC(GAPC_GET_INFO_CMD,
                                                   KE_BUILD_ID(TASK_GAPC, conidx), TASK_GAPM,
                                                   gapc_get_info_cmd);

    // request peer device name.
    p_cmd->operation = GAPC_GET_PEER_FEATURES;

    // send command
    ke_msg_send(p_cmd);
}

static void POSSIBLY_UNUSED gpac_exchange_data_packet_length(uint8_t conidx)
{
#if defined(CHIP_BEST2300) || defined(CHIP_BEST2300P)
    return;
#endif
    
    struct gapc_set_le_pkt_size_cmd *set_le_pakt_size_req = KE_MSG_ALLOC(GAPC_SET_LE_PKT_SIZE_CMD,
                                                                         KE_BUILD_ID(TASK_GAPC, conidx),
                                                                         TASK_APP,
                                                                         gapc_set_le_pkt_size_cmd);

    set_le_pakt_size_req->operation = GAPC_SET_LE_PKT_SIZE;
    set_le_pakt_size_req->tx_octets = APP_MAX_TX_OCTETS;
    set_le_pakt_size_req->tx_time   = APP_MAX_TX_TIME;
    // Send message
    ke_msg_send(set_le_pakt_size_req);
}

static int gapc_peer_features_ind_handler(ke_msg_id_t const msgid,
                                          struct gapc_peer_features_ind* param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)
{
    LOG_I("Peer dev feature is:");
    DUMP8("0x%02x ", param->features, GAP_LE_FEATS_LEN);
    uint8_t conidx = KE_IDX_GET(src_id);

    if (param->features[0] & GAPC_EXT_DATA_LEN_MASK)
    {
        gpac_exchange_data_packet_length(conidx);
    }
    return (KE_MSG_CONSUMED);
}

void app_exchange_remote_feature(uint8_t conidx)
{
    APP_BLE_CONN_CONTEXT_T *pContext = &(app_env.context[conidx]);

    LOG_I("connectStatus:%d, isFeatureExchanged:%d, isGotSolvedBdAddr:%d",
          pContext->connectStatus,
          pContext->isFeatureExchanged,
          pContext->isGotSolvedBdAddr);

    if ((BLE_CONNECTED == pContext->connectStatus) && !pContext->isFeatureExchanged)
    {
        if (pContext->isGotSolvedBdAddr)
        {
            gapc_refresh_remote_dev_feature(conidx);
            pContext->isFeatureExchanged = true;
        }
    }
}

/**
 ****************************************************************************************
 * @brief Handles connection complete event from the GAP. Enable all required profiles
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapc_connection_req_ind_handler(ke_msg_id_t const msgid,
                                           struct gapc_connection_req_ind* param,
                                           ke_task_id_t const dest_id,
                                           ke_task_id_t const src_id)
{
    uint8_t conidx = KE_IDX_GET(src_id);

    APP_BLE_CONN_CONTEXT_T* pContext = &(app_env.context[conidx]);
    
    pContext->connectStatus = BLE_CONNECTED;

    APP_BLE_NEGOTIATED_CONN_PARAM_T connParam;
    connParam.con_interval = param->con_interval;
    connParam.sup_to= param->sup_to;
    connParam.con_latency = param->con_latency;
    app_ble_save_negotiated_conn_param(conidx, &connParam);

    ke_state_set(dest_id, APPM_CONNECTED);

    app_env.conn_cnt++;

    if (app_is_resolving_ble_bd_addr())
    {
        LOG_I("A ongoing ble addr solving is in progress, refuse the new connection.");
        appm_disconnect(conidx);
        return KE_MSG_CONSUMED;
    }

    LOG_I("[CONNECT]device info:");
    LOG_I("peer addr:");
    DUMP8("%02x ", param->peer_addr.addr, 6);
    LOG_I("peer addr type:%d", param->peer_addr_type);
    LOG_I("connection index:%d, isGotSolvedBdAddr:%d", conidx, pContext->isGotSolvedBdAddr);
    LOG_I("conn interval:%d, timeout:%d", param->con_interval, param->sup_to);

    BLE_APP_FUNC_ENTER();

    // Check if the received Connection Handle was valid
    if (conidx != GAP_INVALID_CONIDX)
    {
        pContext->peerAddrType = param->peer_addr_type;
        memcpy(pContext->bdAddr, param->peer_addr.addr, BD_ADDR_LEN);
        
        // Retrieve the connection info from the parameters
        pContext->conhdl = param->conhdl;

        if (BLE_RANDOM_ADDR == pContext->peerAddrType)
        {
            pContext->isGotSolvedBdAddr = false;
        }
        else
        {
            pContext->isGotSolvedBdAddr = true;
        }

        // Clear the advertising timeout timer
        if (ke_timer_active(APP_ADV_TIMEOUT_TIMER, TASK_APP))
        {
            ke_timer_clear(APP_ADV_TIMEOUT_TIMER, TASK_APP);
        }

    #if (BLE_APP_SEC)
        app_sec_reset_env_on_connection();
    #endif

        // Send connection confirmation
        struct gapc_connection_cfm *cfm = KE_MSG_ALLOC(GAPC_CONNECTION_CFM,
                KE_BUILD_ID(TASK_GAPC, conidx), TASK_APP,
                gapc_connection_cfm);

    #if (BLE_APP_SEC)
        cfm->auth      = app_sec_get_bond_status() ? BLE_AUTHENTICATION_LEVEL : GAP_AUTH_REQ_NO_MITM_NO_BOND;
    #else // !(BLE_APP_SEC)
        cfm->auth      = GAP_AUTH_REQ_NO_MITM_NO_BOND;
    #endif // (BLE_APP_SEC)
        // Send the message
        ke_msg_send(cfm);

        // We are now in connected State
        ke_state_set(dest_id, APPM_CONNECTED);
        app_exchange_remote_feature(conidx);

        app_ble_connected_evt_handler(conidx, param->peer_addr.addr);
    }

#if (BLE_APP_TILE)
    app_tile_connected_evt_handler(conidx, param);
#endif

    app_env.context[conidx].connInterval = param->con_interval;
    
    app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_DEFAULT, true);
    BLE_APP_FUNC_LEAVE();

    return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles GAP controller command complete events.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapc_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gapc_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    switch(param->operation)
    {
        case (GAPC_UPDATE_PARAMS):
        {
            if (param->status != GAP_ERR_NO_ERROR)
            {
//                appm_disconnect();
            }
        } break;

        default:
        {
        } break;
    }

    return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles disconnection complete event from the GAP.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gapc_disconnect_ind_handler(ke_msg_id_t const msgid,
                                      struct gapc_disconnect_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    LOG_I("[DISCONNECT] device info:");
    uint8_t conidx = KE_IDX_GET(src_id);
    LOG_I("connection index:%d, reason:0x%x", conidx, param->reason);

    app_env.context[conidx].isBdAddrResolvingInProgress = false;
    app_env.context[conidx].isGotSolvedBdAddr = false;

    app_env.context[conidx].connectStatus = BLE_DISCONNECTED;
    app_env.context[conidx].isFeatureExchanged = false;
    app_env.context[conidx].connInterval = 0;
    l2cm_buffer_reset(conidx);
    
    // Go to the ready state
    ke_state_set(TASK_APP, APPM_READY);

    app_env.conn_cnt--;

    #if (BLE_VOICEPATH)
    app_voicepath_disconnected_evt_handler(conidx);
    #endif

    #if (BLE_DATAPATH_SERVER)
    app_datapath_server_disconnected_evt_handler(conidx);
    #endif

    #if (BLE_OTA)
    app_ota_disconnected_evt_handler(conidx);
    #endif
    
    #if (BLE_APP_TOTA)
    app_tota_disconnected_evt_handler(conidx);
    #endif

    #if(BLE_APP_GFPS)
    app_gfps_disconnected_evt_handler(conidx);
    #endif
    

    #if (BLE_AI_VOICE)
    app_ai_disconnected_evt_handler(conidx);
    #endif

    #if (BLE_APP_TILE)
    app_tile_disconnected_evt_handler(conidx);
    #endif

    app_ble_disconnected_evt_handler(conidx);

    app_ble_reset_conn_param_mode(conidx);
    return (KE_MSG_CONSUMED);
}


static int gapm_profile_added_ind_handler(ke_msg_id_t const msgid,
                                          struct gapm_profile_added_ind *param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)
{
    LOG_I("prf_task_id %d is added.", param->prf_task_id);

    return KE_MSG_CONSUMED;
}

/**
 ****************************************************************************************
 * @brief Handles reception of all messages sent from the lower layers to the application
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int appm_msg_handler(ke_msg_id_t const msgid,
                            void *param,
                            ke_task_id_t const dest_id,
                            ke_task_id_t const src_id)
{
    // Retrieve identifier of the task from received message
    ke_task_id_t src_task_id = MSG_T(msgid);
    // Message policy
    uint8_t msg_pol          = KE_MSG_CONSUMED;


    switch (src_task_id)
    {
        case (TASK_ID_GAPC):
        {
            #if (BLE_APP_SEC)
            if ((msgid >= GAPC_BOND_CMD) &&
                (msgid <= GAPC_SECURITY_IND))
            {
                // Call the Security Module
                msg_pol = appm_get_handler(&app_sec_table_handler, msgid, param, src_id);
            }
            #endif //(BLE_APP_SEC)
            // else drop the message
        } break;

        case (TASK_ID_GATTC):
        {
            // Service Changed - Drop
        } break;

        #if (BLE_APP_HT)
        case (TASK_ID_HTPT):
        {
            // Call the Health Thermometer Module
            msg_pol = appm_get_handler(&app_ht_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_HT)

        #if (BLE_APP_DIS)
        case (TASK_ID_DISS):
        {
            // Call the Device Information Module
            msg_pol = appm_get_handler(&app_dis_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_DIS)

        #if (BLE_APP_HID)
        case (TASK_ID_HOGPD):
        {
            // Call the HID Module
            msg_pol = appm_get_handler(&app_hid_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_HID)

        #if (BLE_APP_BATT)
        case (TASK_ID_BASS):
        {
            // Call the Battery Module
            msg_pol = appm_get_handler(&app_batt_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_BATT)

        #if defined(BLE_APP_AM0)
        case (TASK_ID_AM0):
        {
            // Call the Audio Mode 0 Module
            msg_pol = appm_get_handler(&am0_app_table_handler, msgid, param, src_id);
        } break;
        case (TASK_ID_AM0_HAS):
        {
            // Call the Audio Mode 0 Module
            msg_pol = appm_get_handler(&am0_app_has_table_handler, msgid, param, src_id);
        } break;
        #endif // defined(BLE_APP_AM0)

        #if (BLE_APP_HR)
        case (TASK_ID_HRPS):
        {
            // Call the HRPS Module
            msg_pol = appm_get_handler(&app_hrps_table_handler, msgid, param, src_id);
        } break;
        #endif
        
        #if (BLE_APP_VOICEPATH)
        case (TASK_ID_VOICEPATH):
        {
            // Call the Voice Path Module
            msg_pol = appm_get_handler(app_voicepath_ble_get_msg_handler_table(), msgid, param, src_id);
        } break;
        #endif //(BLE_APP_VOICEPATH)

        #if (BLE_APP_DATAPATH_SERVER)
        case (TASK_ID_DATAPATHPS):
        {
            // Call the Data Path Module
            msg_pol = appm_get_handler(&app_datapath_server_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_DATAPATH_SERVER)
        #if (BLE_APP_TILE)
        case (TASK_ID_TILE):
        {
        // Call the TILE Module
            msg_pol = appm_get_handler(&app_tile_table_handler, msgid, param, src_id);
        } 
        break;
        #endif

        #if (BLE_APP_AI_VOICE)
        case (TASK_ID_AI):
        {
            // Call the AI Voice
            msg_pol = appm_get_handler(app_ai_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_AI_VOICE)

        #if (BLE_APP_OTA)
        case (TASK_ID_OTA):
        {
            // Call the OTA
            msg_pol = appm_get_handler(&app_ota_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_OTA)

        #if (BLE_APP_TOTA)
        case (TASK_ID_TOTA):
        {
            // Call the TOTA
            msg_pol = appm_get_handler(&app_tota_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_TOTA)
        
        #if (BLE_APP_ANCC)
        case (TASK_ID_ANCC):
        {
            // Call the ANCC
            msg_pol = appm_get_handler(&app_ancc_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_ANCC)

        #if (BLE_APP_AMS)
        case (TASK_ID_AMSC):
        {
            // Call the AMS
            msg_pol = appm_get_handler(&app_amsc_table_handler, msgid, param, src_id);
        } break;
        #endif //(BLE_APP_AMS)
        #if (BLE_APP_GFPS)
        case (TASK_ID_GFPSP):
        {
            msg_pol = appm_get_handler(&app_gfps_table_handler, msgid, param, src_id);
        } break;
        #endif

        default:
        {
            #if (BLE_APP_HT)
            if (msgid == APP_HT_MEAS_INTV_TIMER)
            {
                msg_pol = appm_get_handler(&app_ht_table_handler, msgid, param, src_id);
            }
            #endif //(BLE_APP_HT)

            #if (BLE_APP_HID)
            if (msgid == APP_HID_MOUSE_TIMEOUT_TIMER)
            {
                msg_pol = appm_get_handler(&app_hid_table_handler, msgid, param, src_id);
            }
            #endif //(BLE_APP_HID)
        } break;
    }

    return (msg_pol);
}

static int gapm_adv_report_ind_handler(ke_msg_id_t const msgid,
                                          struct gapm_adv_report_ind *param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)    
{
    app_adv_reported_scanned(param);
    return KE_MSG_CONSUMED;
}

static int gapm_addr_solved_ind_handler(ke_msg_id_t const msgid,
                                          struct gapm_addr_solved_ind *param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)    
{
    /// Indicate that resolvable random address has been solved
    appm_random_ble_addr_solved(true, param->irk.key);
    return KE_MSG_CONSUMED;
}

__STATIC int gattc_mtu_changed_ind_handler(ke_msg_id_t const msgid,
                                        struct gattc_mtu_changed_ind const *param,
                                        ke_task_id_t const dest_id,
                                        ke_task_id_t const src_id)
{
    uint8_t conidx = KE_IDX_GET(src_id);
    
    LOG_I("MTU has been negotiated as %d conidx %d", param->mtu, conidx);
    
    
#if (BLE_APP_DATAPATH_SERVER)
    app_datapath_server_mtu_exchanged_handler(conidx, param->mtu);
#endif

#if (BLE_VOICEPATH)
    app_voicepath_mtu_exchanged_handler(conidx, param->mtu);
#endif

#if (BLE_OTA)
    app_ota_mtu_exchanged_handler(conidx, param->mtu);
#endif

#if (BLE_APP_TOTA)
    app_tota_mtu_exchanged_handler(conidx, param->mtu);
#endif

#if (BLE_AI_VOICE)
    app_ai_mtu_exchanged_handler(conidx, param->mtu);
#endif

    return (KE_MSG_CONSUMED);
}

#define APP_CONN_PARAM_INTERVEL_MAX    (30)
__STATIC int gapc_conn_param_update_req_ind_handler(ke_msg_id_t const msgid,
                                        struct gapc_param_update_req_ind const *param,
                                        ke_task_id_t const dest_id,
                                        ke_task_id_t const src_id)
{
    bool accept = true;
    ble_evnet_t event;

    LOG_I("Receive the conn param update request: min %d max %d latency %d timeout %d",
          param->intv_min,
          param->intv_max,
          param->latency,
          param->time_out);

    struct gapc_param_update_cfm* cfm = KE_MSG_ALLOC(GAPC_PARAM_UPDATE_CFM, src_id, dest_id,
                                            gapc_param_update_cfm);

    event.evt_type = BLE_CONN_PARAM_UPDATE_REQ_EVENT;
    event.p.conn_param_update_req_handled.intv_min = param->intv_min;
    event.p.conn_param_update_req_handled.intv_max = param->intv_max;
    event.p.conn_param_update_req_handled.latency  = param->latency;
    event.p.conn_param_update_req_handled.time_out = param->time_out;
    
    app_ble_core_global_handle(&event, &accept);
    LOG_I("%s ret %d ", __func__, accept);

    cfm->accept = accept;

#ifdef GFPS_ENABLED
    // if fastpair doesn't have the requirement of finishing
    // pairing in a really short period, just comment out this 
    // code block to avoid audio glitch if this event happens during music
    // playback and interval is smaller than 15ms
    if (param->intv_min < (uint16_t)(15/1.25))
    {
        LOG_I("accept");
        cfm->accept = true;
        fp_update_ble_connect_param_start(KE_IDX_GET(src_id));
    }
    else
    {
        fp_update_ble_connect_param_stop(KE_IDX_GET(src_id));
    }
#endif

    ke_msg_send(cfm);

    return (KE_MSG_CONSUMED);
}

static int gapc_conn_param_updated_handler(ke_msg_id_t const msgid,
                                          struct gapc_param_updated_ind* param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)
{
    uint8_t conidx = KE_IDX_GET(src_id);
    LOG_I("Conidx %d conn parameter is updated as interval %d timeout %d",
          conidx,
          param->con_interval,
          param->sup_to);

    APP_BLE_NEGOTIATED_CONN_PARAM_T connParam;
    connParam.con_interval = param->con_interval;
    connParam.sup_to= param->sup_to;
    connParam.con_latency = param->con_latency;
    app_ble_save_negotiated_conn_param(conidx, &connParam);
    
#if (BLE_VOICEPATH)
    app_voicepath_ble_conn_parameter_updated(conidx, param->con_interval, param->con_latency);
#endif
#if BLE_APP_TILE
    app_tile_ble_conn_parameter_updated(conidx, param);
#endif

    app_env.context[conidx].connInterval = param->con_interval;
    
    if (param->con_interval >= 32)
    {
        if (app_ble_is_parameter_mode_enabled(conidx, BLE_CONN_PARAM_MODE_OTA))
        {
            app_ble_parameter_mode_clear(conidx, BLE_CONN_PARAM_MODE_OTA); 
            app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_OTA_SLOWER, true); 
        }
        else if (app_ble_is_parameter_mode_enabled(conidx, BLE_CONN_PARAM_MODE_AI_STREAM_ON))
        {
            app_ble_parameter_mode_clear(conidx, BLE_CONN_PARAM_MODE_AI_STREAM_ON);
            app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_AI_STREAM_ON, true); 
        }
    }
    
    return (KE_MSG_CONSUMED);
}

static int gapm_dev_addr_ind_handler(ke_msg_id_t const msgid,
                                          struct gapm_dev_bdaddr_ind *param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id)
{
    ble_evnet_t event;

    // Indicate that a new random BD address set in lower layers
    LOG_I("New dev addr:");
    DUMP8("%02x ", param->addr.addr.addr, 6);

#ifdef GFPS_ENABLED
    app_fp_msg_send_updated_ble_addr();
    app_gfps_update_random_salt();
#endif


    event.evt_type = BLE_SET_RANDOM_BD_ADDR_EVENT;
    event.p.set_random_bd_addr_handled.new_bdaddr = param->addr.addr.addr;
    app_ble_core_global_handle(&event, NULL);

    return KE_MSG_CONSUMED;
}


/*
 * GLOBAL VARIABLES DEFINITION
 ****************************************************************************************
 */
/* Default State handlers definition. */
KE_MSG_HANDLER_TAB(appm)
{
    // Note: first message is latest message checked by kernel so default is put on top.
    {KE_MSG_DEFAULT_HANDLER,    (ke_msg_func_t)appm_msg_handler},

    {APP_ADV_TIMEOUT_TIMER,     (ke_msg_func_t)app_adv_timeout_handler},

    {GAPM_DEVICE_READY_IND,     (ke_msg_func_t)gapm_device_ready_ind_handler},
    {GAPM_CMP_EVT,              (ke_msg_func_t)gapm_cmp_evt_handler},
    {GAPC_GET_DEV_INFO_REQ_IND, (ke_msg_func_t)gapc_get_dev_info_req_ind_handler},
    {GAPC_SET_DEV_INFO_REQ_IND, (ke_msg_func_t)gapc_set_dev_info_req_ind_handler},
    {GAPC_CONNECTION_REQ_IND,   (ke_msg_func_t)gapc_connection_req_ind_handler},
    {GAPC_CMP_EVT,              (ke_msg_func_t)gapc_cmp_evt_handler},
    {GAPC_DISCONNECT_IND,       (ke_msg_func_t)gapc_disconnect_ind_handler},
    {GAPM_PROFILE_ADDED_IND,    (ke_msg_func_t)gapm_profile_added_ind_handler},
    {GATTC_MTU_CHANGED_IND,     (ke_msg_func_t)gattc_mtu_changed_ind_handler},
    {GAPC_PARAM_UPDATE_REQ_IND, (ke_msg_func_t)gapc_conn_param_update_req_ind_handler},
    {GAPC_PARAM_UPDATED_IND,    (ke_msg_func_t)gapc_conn_param_updated_handler},
    {GAPM_ADV_REPORT_IND,       (ke_msg_func_t)gapm_adv_report_ind_handler},
    {GAPC_PEER_FEATURES_IND,    (ke_msg_func_t)gapc_peer_features_ind_handler},
    {GAPM_ADDR_SOLVED_IND,      (ke_msg_func_t)gapm_addr_solved_ind_handler},
    {GAPM_DEV_BDADDR_IND,       (ke_msg_func_t)gapm_dev_addr_ind_handler},
};


/* Defines the place holder for the states of all the task instances. */
ke_state_t appm_state[APP_IDX_MAX];

const struct ke_task_desc TASK_DESC_APP = {appm_msg_handler_tab, appm_state, APP_IDX_MAX, ARRAY_LEN(appm_msg_handler_tab)};

#endif //(BLE_APP_PRESENT)

/// @} APPTASK
