/* Include Files */
#include "KTag.h"

/* Local Definitions and Constants */

#define BLE_TASK_PERIOD_IN_ms 10

#ifdef TRACE_BLE
    #define TRACE_BLE_STATE_ENTRY(state_name) do {if (State_Changed == true) { COMM_Console_Print_String("[BLE] Entering the ");COMM_Console_Print_String(state_name);COMM_Console_Print_String(" state.\n");}} while (false)
#else // TRACE_BLE
    #define TRACE_BLE_STATE_ENTRY(state_name)
#endif // TRACE_BLE

/* Public Variables */

/* Variable used to maintain connection information */
cy_stc_ble_conn_handle_t appConnHandle[CY_BLE_CONN_COUNT];

QueueHandle_t COMM_BLE_CommandQueue;

TaskHandle_t COMM_BLE_Task_Handle;

/* Private Variables */

static const char *TAG = "BLE";
static const TickType_t BLE_Task_Delay = BLE_TASK_PERIOD_IN_ms / portTICK_PERIOD_MS;
static COMM_BLE_StateID_T Current_State = COMM_BLE_DEFAULT;
static COMM_BLE_StateID_T Next_State = COMM_BLE_INITIALIZING;
static bool State_Changed = false;
static TickType_t Time_At_State_Entry_In_Ticks;
static bool Is_Quiet = false;
static bool Was_Advertising = false;
static BLE_AdvertisingData_T Cached_Advertising_Data;

static TimerHandle_t BLEUnquietTimer = NULL;
static StaticTimer_t xBLEUnquietTimerBuffer;

//! Immediate Alert Service alert level value.
volatile uint8_t COMM_BLE_IASAlertLevel = 0;

static cy_stc_ble_gapp_disc_data_t Advertising_Data;
static cy_stc_ble_gapp_scan_rsp_data_t Scan_Response_Data;
static cy_stc_ble_gapp_disc_mode_info_t Advertising_Info = {.advData = &Advertising_Data, .scanRspData = &Scan_Response_Data};

/* Private Function Prototypes */
static void BLE_EventHandler(uint32_t event, void * eventParam);
static void IASEventHandler(uint32 event, void * eventParam);
static cy_en_ble_api_result_t StartNextAdvertisement();

/* Inline Functions */
static inline uint32_t COMM_BLE_GetTimeInState_in_ms()
{
    uint32_t result = (xTaskGetTickCount() - Time_At_State_Entry_In_Ticks) * portTICK_PERIOD_MS;
    return result;
}

/* Public Functions */

//! Initializes the Bluetooth Low Energy communications.
void COMM_BLE_Init(void)
{
    COMM_BLE_CommandQueue = xQueueCreate(10, sizeof(COMM_BLE_Command_T));
    
    BLE_InitPacketBuffers();
    
    COMM_BLE_UART_Init();
    
    if (Cy_BLE_Start(BLE_EventHandler) == CY_BLE_SUCCESS)
    {
        Cy_BLE_IAS_RegisterAttrCallback(IASEventHandler);
    }
#ifdef TRACE_BLE
    else
    {
        COMM_Console_Print_String("[BLE] Cy_BLE_Start API Error!\n");
    }
#endif // TRACE_BLE
}

//! Bluetooth Low Energy communications task: Manages BLE communications, using the PSoC API functions.
/*!
 *
 */
void COMM_BLE_Task(void * pvParameters)
{
    COMM_BLE_Command_T command;
    
    while(true)
    {
        Cy_BLE_ProcessEvents();
        
        if (xQueueReceive(COMM_BLE_CommandQueue, &command, BLE_Task_Delay) == pdPASS)
        {
            if (Next_State != Current_State)
            {
                Current_State = Next_State;
                Time_At_State_Entry_In_Ticks = xTaskGetTickCount();
                State_Changed = true;
            }
            else
            {
                State_Changed = false;
            }
            
            switch (Current_State)
            {
                default:
                case COMM_BLE_DEFAULT:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_DEFAULT");
                        COMM_BLE_RequestState(COMM_BLE_INITIALIZING);
                    }
                    break;
                    
                case COMM_BLE_INITIALIZING:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_INITIALIZING");           
                        // Wait for the CY_BLE_EVT_STACK_ON event in BLE_EventHandler() to transition to COMM_BLE_IDLE.
                    }
                    break;
                    
                case COMM_BLE_IDLE:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_IDLE");
                                                
                        switch (command.ID)
                        {               
                            case COMM_BLE_SCAN_FOR_KTAG_PACKETS:
                                COMM_BLE_RequestState(COMM_BLE_SCANNING_FOR_KTAG_PACKETS);
                                break;
                                
                            case COMM_BLE_SCAN_AND_ADVERTISE:
                                COMM_BLE_RequestState(COMM_BLE_SCANNING_AND_ADVERTISING);
                                break;
                                
                            default:
                                // All other commands are ignored in this state.
                                break;
                        }
                    }
                    break;
                    
                case COMM_BLE_SCANNING_FOR_KTAG_PACKETS:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_SCANNING_FOR_KTAG_PACKETS");
                        
                        if (State_Changed == true)
                        {
                            // Handle state entry events.
                            cy_en_ble_api_result_t apiResult =
                                Cy_BLE_GAPC_StartScan(CY_BLE_SCANNING_FAST, CY_BLE_OBSERVER_CONFIGURATION_0_INDEX);
                            
                            if(apiResult != CY_BLE_SUCCESS)
                            {
#ifdef TRACE_BLE
                                COMM_Console_Print_String("[BLE] Cy_BLE_GAPC_StartScan API Error: 0x");
                                COMM_Console_Print_UInt32AsHex(apiResult);
                                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                            }
                        }
                        
                        switch (command.ID)
                        {
                            case COMM_BLE_ADVERTISE_AS_BROADCASTER:
                                COMM_BLE_RequestState(COMM_BLE_ADVERTISING_AS_BROADCASTER);
                                break;
                                
                            case COMM_BLE_SCAN_AND_ADVERTISE:
                                COMM_BLE_RequestState(COMM_BLE_SCANNING_AND_ADVERTISING);
                                break;

                            default:
                                // All other commands are ignored in this state.
                                break;
                        }
                    }
                    break;

                case COMM_BLE_ADVERTISING_AS_BROADCASTER:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_ADVERTISING_AS_BROADCASTER");
                        
                        if (State_Changed == true)
                        {
                            // Handle state entry events.
                            cy_en_ble_api_result_t apiResult =
                               Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_BROADCASTER_CONFIGURATION_0_INDEX);
        
                            if (apiResult != CY_BLE_SUCCESS)
                            {
#ifdef TRACE_BLE
                                COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StartAdvertisement API Error: 0x");
                                COMM_Console_Print_UInt32AsHex(apiResult);
                                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                            }
                        }
                        
                        switch (command.ID)
                        {
                            case COMM_BLE_STOP_SCANNING:
                                {
                                    cy_en_ble_api_result_t apiResult = Cy_BLE_GAPC_StopScan();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPC_StopScan API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    COMM_BLE_RequestState(COMM_BLE_IDLE);
                                }
                                break;
                                
                            case COMM_BLE_STOP_ADVERTISING:
                                {
                                    cy_en_ble_api_result_t apiResult = Cy_BLE_GAPP_StopAdvertisement();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StopAdvertisement API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    COMM_BLE_RequestState(COMM_BLE_SCANNING_FOR_KTAG_PACKETS);
                                }
                                break;
                                
                            case COMM_BLE_SCAN_AND_ADVERTISE:
                                COMM_BLE_RequestState(COMM_BLE_SCANNING_AND_ADVERTISING);
                                break;

                            default:
                                // All other commands are ignored in this state.
                                break;
                        }
                    }
                    break;
                    
                case COMM_BLE_ADVERTISING_AS_PERIPHERAL:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_ADVERTISING_AS_PERIPHERAL");
                        
                        if (State_Changed == true)
                        {
                            // Handle state entry events.
                            cy_en_ble_api_result_t apiResult =
                               Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
        
                            if (apiResult != CY_BLE_SUCCESS)
                            {
#ifdef TRACE_BLE
                                COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StartAdvertisement API Error: 0x");
                                COMM_Console_Print_UInt32AsHex(apiResult);
                                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                            }
                        }
                    }
                    break;
                    
                case COMM_BLE_SCANNING_AND_ADVERTISING:
                    {
                        TRACE_BLE_STATE_ENTRY("COMM_BLE_SCANNING_AND_ADVERTISING");
                        
                        if (State_Changed == true)
                        {
                            // Handle state entry events.
                            cy_en_ble_api_result_t apiResult =
                               Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_BROADCASTER_CONFIGURATION_0_INDEX);
        
                            if (apiResult != CY_BLE_SUCCESS)
                            {
#ifdef TRACE_BLE
                                COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StartAdvertisement API Error: 0x");
                                COMM_Console_Print_UInt32AsHex(apiResult);
                                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                            }
                            
                            apiResult =
                                Cy_BLE_GAPC_StartScan(CY_BLE_SCANNING_FAST, CY_BLE_OBSERVER_CONFIGURATION_0_INDEX);
                            
                            if(apiResult != CY_BLE_SUCCESS)
                            {
#ifdef TRACE_BLE
                                COMM_Console_Print_String("[BLE] Cy_BLE_GAPC_StartScan API Error: 0x");
                                COMM_Console_Print_UInt32AsHex(apiResult);
                                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                            }
                        }
                        
                        switch (command.ID)
                        {
                            case COMM_BLE_STOP_SCANNING:
                                {
                                    cy_en_ble_api_result_t apiResult = Cy_BLE_GAPC_StopScan();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPC_StopScan API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    
                                    apiResult = Cy_BLE_GAPP_StopAdvertisement();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StopAdvertisement API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    
                                    COMM_BLE_RequestState(COMM_BLE_ADVERTISING_AS_BROADCASTER);
                                }
                                break;
                                
                            case COMM_BLE_STOP_ADVERTISING:
                                {
                                    cy_en_ble_api_result_t apiResult = Cy_BLE_GAPC_StopScan();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPC_StopScan API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    
                                    apiResult = Cy_BLE_GAPP_StopAdvertisement();
            
                                    if (apiResult != CY_BLE_SUCCESS)
                                    {
#ifdef TRACE_BLE
                                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StopAdvertisement API Error: 0x");
                                        COMM_Console_Print_UInt32AsHex(apiResult);
                                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                                    }
                                    
                                    COMM_BLE_RequestState(COMM_BLE_SCANNING_FOR_KTAG_PACKETS);
                                }
                                break;

                            default:
                                // All other commands are ignored in this state.
                                break;
                        }
                    }
                    break;
            }
            COMM_BLE_UART_MaybeSendData();
            
#ifdef CY_DFU_SDK_VERSION_MAJOR
            if (COMM_BLE_IASAlertLevel != 0u)
            {
                // Disconnect from all the connected devices.
                for (uint_fast8_t i = 0; i < CY_BLE_CONN_COUNT; i++)
                {
                    if (Cy_BLE_GetConnectionState(appConnHandle[i]) == CY_BLE_CONN_STATE_CONNECTED)
                    {
                        cy_stc_ble_gap_disconnect_info_t disconnectInfoParam =
                        {
                            .bdHandle = appConnHandle[i].bdHandle,
                            .reason = CY_BLE_HCI_ERROR_OTHER_END_TERMINATED_USER
                        };
                        
                        /* Initiate disconnection from the peer device*/
                        if (Cy_BLE_GAP_Disconnect(&disconnectInfoParam) == CY_BLE_SUCCESS)
                        {
                            /* Wait for disconnection event */
                            while (Cy_BLE_GetConnectionState(appConnHandle[i]) == CY_BLE_CONN_STATE_CONNECTED)
                            {
                                /* Process BLE events */
                                Cy_BLE_ProcessEvents();
                            }
                        }
                    }
                }
                
                /* Stop BLE component. */
                Cy_BLE_Disable();
                Cy_DFU_ExecuteApp(0u);
            }
#endif // CY_DFU_SDK_VERSION_MAJOR
        }
    }
}

SystemKResult_T BLE_GetMyAddress(uint8_t * BD_ADDR)
{
    BD_ADDR[0] = cy_ble_deviceAddress.bdAddr[0];
    BD_ADDR[1] = cy_ble_deviceAddress.bdAddr[1];
    BD_ADDR[2] = cy_ble_deviceAddress.bdAddr[2];
    BD_ADDR[3] = cy_ble_deviceAddress.bdAddr[3];
    BD_ADDR[4] = cy_ble_deviceAddress.bdAddr[4];
    BD_ADDR[5] = cy_ble_deviceAddress.bdAddr[5];
    return SYSTEMK_RESULT_SUCCESS;
}

SystemKResult_T BLE_ScanAndAdvertise(void)
{
#ifdef TRACE_BLE
    KLOG_TRACE(TAG, "BLE_ScanAndAdvertise()");
#endif // TRACE_BLE
    
    if (Is_Quiet == false)
    {
        COMM_BLE_Command_T command = { .ID = COMM_BLE_SCAN_AND_ADVERTISE, .Data = (void *)0x00 };
        xQueueSend(COMM_BLE_CommandQueue, &command, 0);
    }
    else
    {
        // If we are quiet, we don't want to advertise yet,
        // but we will remember that we were asked to advertise.
        Was_Advertising = true;
    }
    
    return SYSTEMK_RESULT_SUCCESS;
}

SystemKResult_T BLE_StopAdvertising(void)
{
#ifdef TRACE_BLE
    KLOG_TRACE(TAG, "BLE_StopAdvertising()");
#endif // TRACE_BLE
    
    COMM_BLE_Command_T command = { .ID = COMM_BLE_STOP_ADVERTISING, .Data = (void *)0x00 };
    xQueueSend(COMM_BLE_CommandQueue, &command, 0);
    
    return SYSTEMK_RESULT_SUCCESS;
}

SystemKResult_T BLE_StopScanning(void)
{
#ifdef TRACE_BLE
    KLOG_TRACE(TAG, "BLE_StopScanning()");
#endif // TRACE_BLE

    COMM_BLE_Command_T command = { .ID = COMM_BLE_STOP_SCANNING, .Data = (void *)0x00 };
    xQueueSend(COMM_BLE_CommandQueue, &command, 0);

    return SYSTEMK_RESULT_SUCCESS;
}

static void Unquiet_Timer_Callback(TimerHandle_t xTimer)
{
    KLOG_INFO(TAG, "Unquiet timer expired after %lu ms.", (xTimerGetPeriod(xTimer) / portTICK_PERIOD_MS));
    BLE_Unquiet();
}

SystemKResult_T BLE_Quiet(uint32_t duration_ms)
{
#ifdef TRACE_BLE
    KLOG_TRACE(TAG, "BLE_Quiet(%lu)", duration_ms);
#endif // TRACE_BLE

    SystemKResult_T result = SYSTEMK_RESULT_NOT_READY;

    if (Is_Quiet == true)
    {
        // Already quiet; reset timer with new duration if duration_ms > 0.
        if (duration_ms > 0)
        {
            // Stop and delete existing timer if it exists.
            if (BLEUnquietTimer != NULL)
            {
                xTimerStop(BLEUnquietTimer, 0);
                xTimerDelete(BLEUnquietTimer, 0);
                BLEUnquietTimer = NULL;
            }
            
            // Create and start new timer with the new duration.
            BLEUnquietTimer = xTimerCreateStatic(
                "BLEUnquietTimer",
                pdMS_TO_TICKS(duration_ms),
                pdFALSE,
                (void *)0,
                Unquiet_Timer_Callback,
                &xBLEUnquietTimerBuffer);

            if (BLEUnquietTimer == NULL)
            {
                KLOG_ERROR(TAG, "Couldn't create the BLEUnquietTimer!");
                result = SYSTEMK_RESULT_FAILED_TO_CREATE_RTOS_TIMER;
            }
            else
            {
                if (xTimerStart(BLEUnquietTimer, 0) != pdPASS)
                {
                    KLOG_ERROR(TAG, "Couldn't start the BLEUnquietTimer!");
                    result = SYSTEMK_RESULT_FAILED_TO_START_RTOS_TIMER;
                }
            }
        }
        else
        {
            // If duration_ms is 0, stop and delete the timer to stay quiet indefinitely.
            if (BLEUnquietTimer != NULL)
            {
                xTimerStop(BLEUnquietTimer, 0);
                xTimerDelete(BLEUnquietTimer, 0);
                BLEUnquietTimer = NULL;
            }
        }
        result = SYSTEMK_RESULT_SUCCESS;
    }
    else
    {
        Is_Quiet = true;
        
        if ((Current_State == COMM_BLE_SCANNING_AND_ADVERTISING) ||
            (Current_State == COMM_BLE_ADVERTISING_AS_BROADCASTER))
        {
            result = BLE_StopAdvertising();
            Was_Advertising = true;
        }

        if (duration_ms > 0)
        {
            // Set a timer to unquiet after the specified duration.
            BLEUnquietTimer = xTimerCreateStatic(
                "BLEUnquietTimer",
                pdMS_TO_TICKS(duration_ms),
                pdFALSE,
                (void *)0,
                Unquiet_Timer_Callback,
                &xBLEUnquietTimerBuffer);

            if (BLEUnquietTimer == NULL)
            {
                KLOG_ERROR(TAG, "Couldn't create the BLEUnquietTimer!");
                result = SYSTEMK_RESULT_FAILED_TO_CREATE_RTOS_TIMER;
            }
            else
            {
                if (xTimerStart(BLEUnquietTimer, 0) != pdPASS)
                {
                    KLOG_ERROR(TAG, "Couldn't start the BLEUnquietTimer!");
                    result = SYSTEMK_RESULT_FAILED_TO_START_RTOS_TIMER;
                }
            }
        }
        result = SYSTEMK_RESULT_SUCCESS;
    }

    return result;
}

SystemKResult_T BLE_Unquiet(void)
{
#ifdef TRACE_BLE
    KLOG_TRACE(TAG, "BLE_Unquiet()");
#endif // TRACE_BLE

    SystemKResult_T result = SYSTEMK_RESULT_NOT_READY;

    if (Is_Quiet == false)
    {
        // Already unquiet; no action needed.
        return SYSTEMK_RESULT_SUCCESS;
    }
    else
    {
        Is_Quiet = false;
        if (Was_Advertising == true)
        {
            BLE_ScanAndAdvertise();
            Was_Advertising = false;
        }
        result = SYSTEMK_RESULT_SUCCESS;
    }

    return result;
}

void COMM_BLE_RequestState(COMM_BLE_StateID_T state)
{
    Next_State = state;
    COMM_BLE_Command_T command = {.ID = COMM_BLE_REQUEST_STATE_CHANGE, .Data = (void *)0x00};
    xQueueSend(COMM_BLE_CommandQueue, &command, 0);
}

SystemKResult_T BLE_SetAdvertisingData(BLE_AdvertisingData_T * data)
{ 
    SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
    
    if (data->length > BLE_MAX_ADVERTISING_BYTES)
    {
        result = SYSTEMK_RESULT_TOO_MANY_DATA;
    }
    else if (data->length < BLE_KTAG_PACKET_TOTAL_SIZE)
    {
        result = SYSTEMK_RESULT_TOO_FEW_DATA;
    }
    else
    {
        Advertising_Data.advDataLen = BLE_KTAG_PACKET_TOTAL_SIZE;
        memcpy(Advertising_Data.advData, data, BLE_KTAG_PACKET_TOTAL_SIZE);
        
#ifdef TRACE_BLE
        KLOG_TRACE(TAG, "Setting advertising data for packet type %u.", data->data[8]);
#endif // TRACE_BLE
        
        cy_en_ble_api_result_t result = Cy_BLE_GAPP_UpdateAdvScanData(&Advertising_Info);
    
        if (result != CY_BLE_SUCCESS)
        {
#ifdef TRACE_BLE
            COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_UpdateAdvScanData Error: 0x");
            COMM_Console_Print_UInt32AsHex(result);
            COMM_Console_Print_String("\n");
            
            vTaskDelay(100);
#endif // TRACE_BLE
        }
        
        // Save a copy for later.
        Cached_Advertising_Data.length = BLE_KTAG_PACKET_TOTAL_SIZE;
        memcpy(&Cached_Advertising_Data, data, BLE_KTAG_PACKET_TOTAL_SIZE);
    }
    
    return result;
}

/* Private Functions */

static void BLE_EventHandler(uint32_t event, void * eventParam)
{
    cy_en_ble_api_result_t apiResult;
    
    static cy_stc_ble_gap_sec_key_info_t keyInfo =
    {
        .localKeysFlag    = CY_BLE_GAP_SMP_INIT_ENC_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_IRK_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_CSRK_KEY_DIST,
        .exchangeKeysFlag = CY_BLE_GAP_SMP_INIT_ENC_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_IRK_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_CSRK_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_ENC_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_IRK_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_CSRK_KEY_DIST,
    };
    
    if (COMM_BLE_UART_HandleEvent(event, eventParam) == false)
    {
        // For more information, refer to the comments in cy_ble_stack.h, where all
        // these events are described in more detail.
        switch (event)
        {
            case CY_BLE_EVT_INVALID:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_INVALID\n");
#endif // TRACE_BLE
                break;


            // G E N E R I C   E V E N T S   (0x1000 to 0x1FFF)
            case CY_BLE_EVT_STACK_ON:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_STACK_ON\n");
#endif // TRACE_BLE
                COMM_BLE_RequestState(COMM_BLE_IDLE);
                break;
                
            case CY_BLE_EVT_TIMEOUT:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_TIMEOUT: 0x");
                COMM_Console_Print_UInt8AsHex(*(uint8_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_STACK_BUSY_STATUS:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_STACK_BUSY_STATUS: 0x");
                COMM_Console_Print_UInt8AsHex(*(uint8_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_MEMORY_REQUEST:
            case CY_BLE_EVT_PENDING_FLASH_WRITE:
            case CY_BLE_EVT_FLASH_CORRUPT:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
                break;
#endif // TRACE_BLE
                break;


            // H O S T   C O N T R O L   I N T E R F A C E   E V E N T S   (0x2000 to 0x2FFF)
            case CY_BLE_EVT_HARDWARE_ERROR:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_HARDWARE_ERROR: 0x");
                COMM_Console_Print_UInt8AsHex(*(uint8_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_WRITE_AUTH_PAYLOAD_TO_COMPLETE:
            case CY_BLE_EVT_READ_AUTH_PAYLOAD_TO_COMPLETE:
            case CY_BLE_EVT_GET_CHANNEL_MAP_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_LE_SET_EVENT_MASK_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_LE_PING_AUTH_TIMEOUT:
            case CY_BLE_EVT_SET_DATA_LENGTH_COMPLETE:
            case CY_BLE_EVT_SET_SUGGESTED_DATA_LENGTH_COMPLETE:
            case CY_BLE_EVT_GET_DATA_LENGTH_COMPLETE:
            case CY_BLE_EVT_DATA_LENGTH_CHANGE:
            case CY_BLE_EVT_GET_PEER_RPA_COMPLETE:
            case CY_BLE_EVT_GET_LOCAL_RPA_COMPLETE:
            case CY_BLE_EVT_SET_RPA_TO_COMPLETE:
            case CY_BLE_EVT_SET_RPA_ENABLE_COMPLETE:
            case CY_BLE_EVT_SET_HOST_CHANNEL_COMPLETE:
            case CY_BLE_EVT_ADD_DEVICE_TO_RPA_LIST_COMPLETE:
            case CY_BLE_EVT_REMOVE_DEVICE_FROM_RPA_LIST_COMPLETE:
            case CY_BLE_EVT_ADD_DEVICE_TO_WHITE_LIST_COMPLETE:
            case CY_BLE_EVT_REMOVE_DEVICE_FROM_WHITE_LIST_COMPLETE:
            case CY_BLE_EVT_GET_PHY_COMPLETE:
            case CY_BLE_EVT_SET_DEFAULT_PHY_COMPLETE:
            case CY_BLE_EVT_SET_PHY_COMPLETE:
            case CY_BLE_EVT_PHY_UPDATE_COMPLETE:
            case CY_BLE_EVT_SET_PRIVACY_MODE_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;


            // V E N D O R   E V E N T S   (0x3000 to 0x3FFF)
            case CY_BLE_EVT_LL_CNTRL_PROC_PENDING_COMPLETE:
            case CY_BLE_EVT_SOFT_RESET_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GET_DEVICE_ADDR_COMPLETE:
            case CY_BLE_EVT_GET_RSSI_COMPLETE:
            case CY_BLE_EVT_GET_TX_PWR_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
                
            case CY_BLE_EVT_SET_TX_PWR_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_SET_TX_PWR_COMPLETE\n");
#endif // TRACE_BLE
                break;
                
            case CY_BLE_EVT_GET_CLK_CONFIG_COMPLETE:
            case CY_BLE_EVT_SET_CLK_CONFIG_COMPLETE:
            case CY_BLE_EVT_RANDOM_NUM_GEN_COMPLETE:
            case CY_BLE_EVT_AES_ENCRYPT_COMPLETE:
            case CY_BLE_EVT_AES_CCM_ENCRYPT_COMPLETE:
            case CY_BLE_EVT_AES_CCM_DECRYPT_COMPLETE:
            case CY_BLE_EVT_SET_SLAVE_LATENCY_MODE_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_STACK_SHUTDOWN_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_STACK_SHUTDOWN_COMPLETE\n");
#endif // TRACE_BLE
                // If desired, wait until console transmit is complete, then call Cy_SysPm_Hibernate();
                break;

            case CY_BLE_EVT_RADIO_TEMPERATURE:
            case CY_BLE_EVT_RADIO_VOLTAGE_LEVEL:
            case CY_BLE_EVT_AES_CMAC_GEN_COMPLETE:
            case CY_BLE_EVT_SET_EVENT_MASK_COMPLETE:
            case CY_BLE_EVT_SET_CE_LENGTH_COMPLETE:
            case CY_BLE_EVT_SET_CONN_PRIORITY_COMPLETE:
            case CY_BLE_EVT_HCI_PKT_RCVD:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;


            // G E N E R I C   A C C E S S   P R O F I L E   E V E N T S   (0x4000 to 0x4FFF)                
            case CY_BLE_EVT_GAPC_SCAN_PROGRESS_RESULT:
                {
#if (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)
                    COMM_Console_Print_String("[BLE] ..SCAN_PROGRESS_RESULT ");
#endif // (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)

                    BLE_Packet_T * packet = BLE_DecodeKTagPacket((*(cy_stc_ble_gapc_adv_report_param_t *)eventParam).data,
                                                                 (*(cy_stc_ble_gapc_adv_report_param_t *)eventParam).dataLen,
                                                                 (*(cy_stc_ble_gapc_adv_report_param_t *)eventParam).peerBdAddr,
                                                                 (*(cy_stc_ble_gapc_adv_report_param_t *)eventParam).rssi);
                    
                    if (packet != NULL)
                    {
#ifdef TRACE_BLE
                        switch (packet->Generic.type)
                        {
                            case BLE_PACKET_TYPE_INSTIGATE_GAME:
                                COMM_Console_Print_String(" KTag 'Instigate Game' packet found!");
                                break;

                            case BLE_PACKET_TYPE_EVENT:
                                COMM_Console_Print_String(" KTag 'Event' packet found!");
                                break;

                            case BLE_PACKET_TYPE_TAG:
                                COMM_Console_Print_String(" KTag 'Tag' packet found!");
                                break;
                                
                            case BLE_PACKET_TYPE_CONSOLE:
                                COMM_Console_Print_String(" KTag 'Console' packet found!");
                                break;
                                
                            case BLE_PACKET_TYPE_STATUS:
                                COMM_Console_Print_String(" KTag 'Status' packet found!");
                                break;
                                
                            case BLE_PACKET_TYPE_PARAMETERS:
                                COMM_Console_Print_String(" KTag 'Parameters' packet found!");
                                break;
                                
                            case BLE_PACKET_TYPE_HELLO:
                                COMM_Console_Print_String(" KTag 'Hello' packet found!");
                                break;
                            
                            default:
                                KLOG_ERROR(TAG, " Unknown KTag packet found: 0x%02X", packet->Generic.type);
                                break;
                        }

                        COMM_Console_Print_String("   RSSI: ");
                        COMM_Console_Print_Int8(packet->Generic.RSSI);
                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                        if (packet->Generic.type == BLE_PACKET_TYPE_CONSOLE)
                        {
                            packet->Console.console_data[BLE_KTAG_PACKET_DATA_SIZE - 1] = COMM_CONSOLE_STRING_TERMINATOR;
                            COMM_Console_Execute_Internal_Command(packet->Console.console_data);
                        }
                        else
                        {
                            KEvent_T packet_received_event = {.ID = KEVENT_BLE_PACKET_RECEIVED, .Data = packet};
                            Post_KEvent(&packet_received_event);
                        }
                    }
#if (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)
                    else
                    {
                        COMM_Console_Print_String(" Not a KTag packet!\n");
                    }
#endif // (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)
                }
                break;

            case CY_BLE_EVT_GAP_AUTH_REQ:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_AUTH_REQ: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" security=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).security);
                COMM_Console_Print_String(" bonding=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).bonding);
                COMM_Console_Print_String(" ekeySize=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).ekeySize);
                COMM_Console_Print_String(" authErr=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).authErr);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                
                if(cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].security == 
                    (CY_BLE_GAP_SEC_MODE_1 | CY_BLE_GAP_SEC_LEVEL_1))
                {
                    cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].authErr = 
                        CY_BLE_GAP_AUTH_ERROR_PAIRING_NOT_SUPPORTED;
                }    
                
                cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].bdHandle = 
                    ((cy_stc_ble_gap_auth_info_t *)eventParam)->bdHandle;

                /* Pass security information for authentication in reply to an authentication request 
                 * from the master device */
                apiResult = Cy_BLE_GAPP_AuthReqReply(&cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX]);            
                if(apiResult != CY_BLE_SUCCESS)
                {
                    Cy_BLE_GAP_RemoveOldestDeviceFromBondedList();
                    apiResult = Cy_BLE_GAPP_AuthReqReply(&cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX]);            
                    if(apiResult != CY_BLE_SUCCESS)
                    {
#ifdef TRACE_BLE
                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_AuthReqReply API Error: 0x");
                        COMM_Console_Print_UInt32AsHex(apiResult);
                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                    }
                }
                break;
                
            case CY_BLE_EVT_GAP_PASSKEY_ENTRY_REQUEST:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_PASSKEY_ENTRY_REQUEST:\n");
                COMM_Console_Print_String("      Please enter the passkey displayed on the peer device.\n");
#endif // TRACE_BLE
                break;
                
            case CY_BLE_EVT_GAP_PASSKEY_DISPLAY_REQUEST:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_PASSKEY_DISPLAY_REQUEST: ");
                COMM_Console_Print_UInt32(*(uint32_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
                
            case CY_BLE_EVT_GAP_AUTH_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_AUTH_COMPLETE: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" security=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).security);
                COMM_Console_Print_String(" bonding=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).bonding);
                COMM_Console_Print_String(" ekeySize=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).ekeySize);
                COMM_Console_Print_String(" authErr=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).authErr);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAP_AUTH_FAILED:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_AUTH_FAILED: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" authErr=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_auth_info_t *)eventParam).authErr);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
               break;

            case CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP: state=");
                COMM_Console_Print_UInt8(Cy_BLE_GetAdvertisementState());
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE

                // Restore the previous;y set advertising packet.
                if (Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_ADVERTISING)
                {
                    Advertising_Data.advDataLen = BLE_KTAG_PACKET_TOTAL_SIZE;
                    memcpy(Advertising_Data.advData, &Cached_Advertising_Data, BLE_KTAG_PACKET_TOTAL_SIZE);
        
#ifdef TRACE_BLE
                    KLOG_TRACE(TAG, "Setting advertising data from cached copy for packet type %u.", Cached_Advertising_Data.data[8]);
#endif // TRACE_BLE
        
                    cy_en_ble_api_result_t result = Cy_BLE_GAPP_UpdateAdvScanData(&Advertising_Info);
                
                    if (result != CY_BLE_SUCCESS)
                    {
#ifdef TRACE_BLE
                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_UpdateAdvScanData Error: 0x");
                        COMM_Console_Print_UInt32AsHex(result);
                        COMM_Console_Print_String("\n");
                        
                        vTaskDelay(100);
#endif // TRACE_BLE
                    }
                }

#if 0
                if (Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_STOPPED)
                {
                    apiResult = StartNextAdvertisement();
                    if (apiResult != CY_BLE_SUCCESS)
                    {
#ifdef TRACE_BLE
                        COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StartAdvertisement API Error: 0x");
                        COMM_Console_Print_UInt32AsHex(apiResult);
                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                    }
                }
#endif // 0
                break;
                
            case CY_BLE_EVT_GAP_DEVICE_CONNECTED:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_DEVICE_CONNECTED: connIntv=");
                COMM_Console_Print_UInt16(((cy_stc_ble_gap_connected_param_t *)eventParam)->connIntv * 5u /4u);
                COMM_Console_Print_String(" ms\n");
#endif // TRACE_BLE          

                /* Set security keys for new device which is not already bonded */
                if(App_IsDeviceInBondList((*(cy_stc_ble_gap_connected_param_t *)eventParam).bdHandle) == 0u)
                {
                    keyInfo.SecKeyParam.bdHandle = (*(cy_stc_ble_gap_connected_param_t *)eventParam).bdHandle;
                    apiResult = Cy_BLE_GAP_SetSecurityKeys(&keyInfo);
                    if (apiResult != CY_BLE_SUCCESS)
                    {
#ifdef TRACE_BLE
                        COMM_Console_Print_String("[BLE] Cy_BLE_GAP_SetSecurityKeys API Error: 0x");
                        COMM_Console_Print_UInt32AsHex(apiResult);
                        COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                    }
                }  
                break;

            case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_DEVICE_DISCONNECTED: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_disconnect_param_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" reason=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_disconnect_param_t *)eventParam).reason);
                COMM_Console_Print_String(" status=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_gap_disconnect_param_t *)eventParam).status);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                
                apiResult = StartNextAdvertisement();
                if (apiResult != CY_BLE_SUCCESS)
                {
#ifdef TRACE_BLE
                    COMM_Console_Print_String("[BLE] Cy_BLE_GAPP_StartAdvertisement API Error: 0x");
                    COMM_Console_Print_UInt32AsHex(apiResult);
                    COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                }
                break;
                
            case CY_BLE_EVT_GAP_ENCRYPT_CHANGE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_ENCRYPT_CHANGE: 0x");
                COMM_Console_Print_UInt8AsHex(*(uint8_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
            
            case CY_BLE_EVT_GAP_CONNECTION_UPDATE_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAPC_SCAN_START_STOP:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAPC_SCAN_START_STOP\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAP_KEYINFO_EXCHNGE_CMPLT:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAP_NUMERIC_COMPARISON_REQUEST:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_NUMERIC_COMPARISON_REQUEST:\n");
                COMM_Console_Print_String("      Compare this passkey with the one displayed in your peer device and press 'y' or 'n':\n");
                COMM_Console_Print_String("      ");
                COMM_Console_Print_UInt32(*(uint32_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAP_KEYPRESS_NOTIFICATION:
            case CY_BLE_EVT_GAP_OOB_GENERATED_NOTIFICATION:
            case CY_BLE_EVT_GAP_ENHANCE_CONN_COMPLETE:
            case CY_BLE_EVT_GAPC_DIRECT_ADV_REPORT:
            case CY_BLE_EVT_GAP_SMP_NEGOTIATED_AUTH_INFO:
            case CY_BLE_EVT_GAP_DEVICE_ADDR_GEN_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAP_KEYS_GEN_COMPLETE:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_KEYS_GEN_COMPLETE\n");
#endif // TRACE_BLE
                keyInfo.SecKeyParam = (*(cy_stc_ble_gap_sec_key_param_t *)eventParam);
                Cy_BLE_GAP_SetIdAddress(&cy_ble_deviceAddress);
                break;

            case CY_BLE_EVT_GAP_RESOLVE_DEVICE_COMPLETE:
            case CY_BLE_EVT_GAP_GEN_SET_LOCAL_P256_KEYS_COMPLETE:
            case CY_BLE_EVT_GAP_CREATE_CONN_CANCEL_COMPLETE:
            case CY_BLE_EVT_GAP_CONN_ESTB:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;

            case CY_BLE_EVT_GAPP_UPDATE_ADV_SCAN_DATA_COMPLETE:
#if (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAPP_UPDATE_ADV_SCAN_DATA_COMPLETE: ");
                COMM_Console_Print_UInt8(*(uint8_t *)eventParam);
                COMM_Console_Print_String("\n");
#endif // (defined TRACE_BLE) && (defined VERBOSE_BLE_TRACE)
                break;

            case CY_BLE_EVT_GAP_ADV_TX:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GAP_ADV_TX\n");
#endif // TRACE_BLE
                break;
                
            // G E N E R I C   A T T R I B U T E   P R O F I L E   E V E N T S   (0x5000 to 0x5FFF)
            case CY_BLE_EVT_GATTC_ERROR_RSP:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GATTC_ERROR_RSP: opCode=0x");
                COMM_Console_Print_UInt8AsHex(((cy_stc_ble_gatt_err_param_t*)eventParam)->errInfo.opCode);
                COMM_Console_Print_String(" errorCode=0x");
                COMM_Console_Print_UInt8AsHex(((cy_stc_ble_gatt_err_param_t*)eventParam)->errInfo.errorCode);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
          
            case CY_BLE_EVT_GATT_CONNECT_IND:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GATT_CONNECT_IND: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_conn_handle_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" attId=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_conn_handle_t *)eventParam).attId);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                {
                    cy_stc_ble_conn_handle_t connHandle = *(cy_stc_ble_conn_handle_t*)eventParam;               
                    appConnHandle[connHandle.attId] = *(cy_stc_ble_conn_handle_t *)eventParam;
                }
                break;
                
            case CY_BLE_EVT_GATT_DISCONNECT_IND:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] CY_BLE_EVT_GATT_DISCONNECT_IND: bdHandle=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_conn_handle_t *)eventParam).bdHandle);
                COMM_Console_Print_String(" attId=0x");
                COMM_Console_Print_UInt8AsHex((*(cy_stc_ble_conn_handle_t *)eventParam).attId);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                {
                    cy_stc_ble_conn_handle_t connHandle = *(cy_stc_ble_conn_handle_t*)eventParam;
                    appConnHandle[connHandle.attId].attId    = connHandle.attId;
                    appConnHandle[connHandle.attId].bdHandle = CY_BLE_INVALID_CONN_HANDLE_VALUE;
                }
                break;
                
            case CY_BLE_EVT_GATTS_XCNHG_MTU_REQ:
                { 
                    cy_stc_ble_gatt_xchg_mtu_param_t mtu = 
                    {
                        .connHandle = ((cy_stc_ble_gatt_xchg_mtu_param_t *)eventParam)->connHandle
                    };
                    Cy_BLE_GATT_GetMtuSize(&mtu);
#ifdef TRACE_BLE
                    COMM_Console_Print_String("[BLE] CY_BLE_EVT_GATTS_XCNHG_MTU_REQ: bdHandle=0x");
                    COMM_Console_Print_UInt8AsHex(mtu.connHandle.bdHandle);
                    COMM_Console_Print_String(" attId=0x");
                    COMM_Console_Print_UInt8AsHex(mtu.connHandle.bdHandle);
                    COMM_Console_Print_String(" mtu=");
                    COMM_Console_Print_UInt16(mtu.mtu);
                    COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                }
                break;
                
            case CY_BLE_EVT_GATTC_XCHNG_MTU_RSP:
            case CY_BLE_EVT_GATTC_READ_BY_GROUP_TYPE_RSP:
            case CY_BLE_EVT_GATTC_READ_BY_TYPE_RSP:
            case CY_BLE_EVT_GATTC_FIND_INFO_RSP:
            case CY_BLE_EVT_GATTC_FIND_BY_TYPE_VALUE_RSP:
            case CY_BLE_EVT_GATTC_READ_RSP:
            case CY_BLE_EVT_GATTC_READ_BLOB_RSP:
            case CY_BLE_EVT_GATTC_READ_MULTI_RSP:
            case CY_BLE_EVT_GATTS_WRITE_REQ:
            case CY_BLE_EVT_GATTC_WRITE_RSP:
            case CY_BLE_EVT_GATTS_WRITE_CMD_REQ:
            case CY_BLE_EVT_GATTS_PREP_WRITE_REQ:
            case CY_BLE_EVT_GATTS_EXEC_WRITE_REQ:
            case CY_BLE_EVT_GATTC_EXEC_WRITE_RSP:
            case CY_BLE_EVT_GATTC_HANDLE_VALUE_NTF:
            case CY_BLE_EVT_GATTC_HANDLE_VALUE_IND:
            case CY_BLE_EVT_GATTS_HANDLE_VALUE_CNF:
            case CY_BLE_EVT_GATTS_DATA_SIGNED_CMD_REQ:
            case CY_BLE_EVT_GATTC_STOP_CMD_COMPLETE:
            case CY_BLE_EVT_GATTS_READ_CHAR_VAL_ACCESS_REQ:
            case CY_BLE_EVT_GATTC_LONG_PROCEDURE_END:              
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt16AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
                
            default:
#ifdef TRACE_BLE
                COMM_Console_Print_String("[BLE] Unhandled event: ");
                COMM_Console_Print_UInt32AsHex(event);
                COMM_Console_Print_String("\n");
#endif // TRACE_BLE
                break;
        }
    }
}

/*******************************************************************************
* Function Name: IasEventHandler
********************************************************************************
*
* Summary:
*  This is an event callback function to receive events from the BLE Component,
*  which are specific to Immediate Alert Service.
*
* Parameters:
*   event:       Write Command event from the BLE component.
*   eventParams: A structure instance of CY_BLE_GATT_HANDLE_VALUE_PAIR_T type.
*
*******************************************************************************/
static void IASEventHandler(uint32 event, void * eventParam)
{
    (void) eventParam;
    uint8_t alert;
    
    /* Alert Level Characteristic write event */
    if (event == CY_BLE_EVT_IASS_WRITE_CHAR_CMD)
    {
        /* Read the updated Alert Level value from the GATT database */
        Cy_BLE_IASS_GetCharacteristicValue(CY_BLE_IAS_ALERT_LEVEL, sizeof(alert), &alert);
        COMM_BLE_IASAlertLevel = alert;
    }   
}

static cy_en_ble_api_result_t StartNextAdvertisement()
{
    static uint8_t Current_Advertising_Index = CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX;
    cy_en_ble_api_result_t apiResult;
    
    apiResult = Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, Current_Advertising_Index);
    
    if(apiResult == CY_BLE_SUCCESS)
    {
        if (Current_Advertising_Index == CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX)
        {
            Current_Advertising_Index = CY_BLE_BROADCASTER_CONFIGURATION_0_INDEX;
            COMM_Console_Print_String("CY_BLE_BROADCASTER_CONFIGURATION_0_INDEX\n");
        }
        else
        {
            Current_Advertising_Index = CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX;
            COMM_Console_Print_String("CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX\n");
        }
    }
                
    return apiResult;
}
