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

/* Local Definitions and Constants */

#define UART_CIRCULAR_BUFFER_SIZE 1024
#define BLE_UART_BUFFER_CHARACTERISTIC_SIZE 20

/* Public Variables */

/* Private Variables */

static uint_fast16_t UART_Tx_Notifications_Enabled = 0;

static uint8_t UART_Tx_Buffer_Storage[UART_CIRCULAR_BUFFER_SIZE];
static UTIL_CircularBuffer_T UART_Tx_Buffer;
static uint8_t BLE_UART_Tx_Buffer[BLE_UART_BUFFER_CHARACTERISTIC_SIZE];
static uint8_t Rx_Buffer[BLE_UART_BUFFER_CHARACTERISTIC_SIZE + 1];

/* Private Function Prototypes */

/* Public Functions */

void COMM_BLE_UART_Init(void)
{
    UTIL_InitCircularBuffer(&UART_Tx_Buffer, UART_Tx_Buffer_Storage, UART_CIRCULAR_BUFFER_SIZE);
}

//! Sends a message over the BLE UART.
void COMM_BLE_UART_PutString(const char8 * string, uint16_t length)
{
    for (uint8_t i = 0; i < length; i++)
    {
        (void) UTIL_PushToCircularBuffer(&UART_Tx_Buffer, *string++);
    }
}

//! Sends a single character over the BLE UART.
void COMM_BLE_UART_PutChar(char8 character)
{
    (void) UTIL_PushToCircularBuffer(&UART_Tx_Buffer, character);
}

void COMM_BLE_UART_MaybeSendData(void)
{ 
    int8_t length = 0;
    
    if (UTIL_IsCircularBufferEmpty(&UART_Tx_Buffer) == false)
    {
        while ((length < BLE_UART_BUFFER_CHARACTERISTIC_SIZE) && (UTIL_IsCircularBufferEmpty(&UART_Tx_Buffer) == false))
        {
            uint8_t value;
            if (UTIL_PopFromCircularBuffer(&UART_Tx_Buffer, &value) == UTIL_CIRCULARBUFFERRESULT_SUCCESS)
            {
                BLE_UART_Tx_Buffer[length] = value;
                length++;
            }
        }
    }
    
    if (length > 0)
    {    
        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_gatt_handle_value_pair_t tempHandle;
                
                tempHandle.attrHandle = CY_BLE_NORDIC_UART_SERVICE_TX_CHAR_HANDLE;
                tempHandle.value.val = (uint8 *) BLE_UART_Tx_Buffer;
                tempHandle.value.actualLen = length;
                tempHandle.value.len = length;
 
                Cy_BLE_GATTS_WriteAttributeValueLocal(&tempHandle);
            }
        }
            
        // Send notification to each client that has TX notifications enabled.
        for (uint_fast8_t i = 0; i < CY_BLE_CONN_COUNT; i++)
        {
            if ((Cy_BLE_GetConnectionState(appConnHandle[i]) >=  CY_BLE_CONN_STATE_CONNECTED) &&
                 Cy_BLE_GATTS_IsNotificationEnabled(&appConnHandle[i], 
                 CY_BLE_NORDIC_UART_SERVICE_TX_TXCCCD_DESC_HANDLE))
            {
                cy_stc_ble_gatt_handle_value_pair_t tempHandle;
                
                tempHandle.attrHandle = CY_BLE_NORDIC_UART_SERVICE_TX_CHAR_HANDLE;
                tempHandle.value.val = (uint8 *) BLE_UART_Tx_Buffer;
                tempHandle.value.actualLen = length;
                tempHandle.value.len = length;
                
                Cy_BLE_GATTS_SendNotification(&appConnHandle[i], &tempHandle);
            }
        }
    }
}

//! BLE event handler for the BLE UART feature.
/*!
 *  This function should be called *before* events are handled in the event handler passed to
 *  Cy_BLE_Start().  If it returns `false`, then the rest of the event handler should proceed.
 *
 *  \param  event       BLE stack event code received from the BLE middleware (one of cy_en_ble_event_t).
 *  \param  eventParam  pointer to an event-specific data structure containing the relevant event information.
 *  \return             true if this handler has completely handled the event, and no further
 *                      handling is necessary; false otherwise.
 */
bool COMM_BLE_UART_HandleEvent(uint32 event, void * eventParam)
{
    static cy_stc_ble_gatts_write_cmd_req_param_t * writeReqParameter;
    bool handled = false;
    
    /* Take an action based on the current event */
    switch ((cy_en_ble_event_t)event)
    {
        // Handle a write request.
        case CY_BLE_EVT_GATTS_WRITE_REQ:
        
            writeReqParameter = (cy_stc_ble_gatts_write_cmd_req_param_t*)eventParam;
            
            // Request to write the UART.
            // https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.2.0%2Fble_sdk_app_nus_eval.html&cp=5_5_0_4_1_2_24
            if (writeReqParameter->handleValPair.attrHandle == CY_BLE_NORDIC_UART_SERVICE_RX_CHAR_HANDLE)
            {
                // Only update the value and write the response if the requested write is allowed.
                if (CY_BLE_GATT_ERR_NONE == Cy_BLE_GATTS_WriteAttributeValuePeer(&writeReqParameter->connHandle, &writeReqParameter->handleValPair))
                {
                    uint16_t i;
                    
                    for (i = 0; (i < BLE_UART_BUFFER_CHARACTERISTIC_SIZE) && (i < writeReqParameter->handleValPair.value.len); i++)
                    {
                        Rx_Buffer[i] = writeReqParameter->handleValPair.value.val[i];
                    }
                    
                    // NULL-terminate the buffer.
                    Rx_Buffer[i] = 0x00;
                    
                    Cy_BLE_GATTS_WriteRsp(writeReqParameter->connHandle);
                    
                    COMM_Console_Execute_Internal_Command(Rx_Buffer);
                }
                
                handled = true;
            }
            
            // Request for UART Tx notifications.
            if (writeReqParameter->handleValPair.attrHandle == CY_BLE_NORDIC_UART_SERVICE_TX_TXCCCD_DESC_HANDLE)
            {        
                if (CY_BLE_GATT_ERR_NONE == Cy_BLE_GATTS_WriteAttributeValuePeer(&writeReqParameter->connHandle, &writeReqParameter->handleValPair))
                {
                    UART_Tx_Notifications_Enabled = writeReqParameter->handleValPair.value.val[0] & 0x01;
                    
                    if (UART_Tx_Notifications_Enabled)
                    {
                        COMM_Console_Print_String("[BLE] UART Tx notifications enabled.\n");                       
                    }
                    else
                    {
                        COMM_Console_Print_String("[BLE] UART Tx notifications disabled.\n");
                    }
                    
                    Cy_BLE_GATTS_WriteRsp(writeReqParameter->connHandle);
                }
                
                handled = true;
            }
            break;
            
        default:
            // (`handled` is already set to false.)
            break;
    }
    
    return handled;
}

/* Private Functions */
