/********************************** (C) COPYRIGHT *******************************
 * File Name          : OTAprofile.C
 * Author             : WCH
 * Version            : V1.0
 * Date               : 2018/12/10
 * Description        : OTA upgrade Bluetooth communication interface
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for 
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "CONFIG.h"
#include "OTAprofile.h"
#include "debug.h"
#include "ota.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// Simple GATT Profile Service UUID: 0xFFF0
const uint8_t OTAProfileServUUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(OTAPROFILE_SERV_UUID), HI_UINT16(OTAPROFILE_SERV_UUID)};

// Characteristic 1 UUID: 0xFFF1
const uint8_t OTAProfilechar1UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(OTAPROFILE_CHAR_UUID), HI_UINT16(OTAPROFILE_CHAR_UUID)};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static OTAProfileCBs_t *OTAProfile_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables
 */

// Simple Profile Service attribute
static const gattAttrType_t OTAProfileService = {ATT_BT_UUID_SIZE, OTAProfileServUUID};

// Simple Profile Characteristic 1 Properties
static uint8_t OTAProfileCharProps = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_WRITE_NO_RSP;

// Characteristic 1 Value
static uint8_t OTAProfileChar = 0;

// Simple Profile Characteristic 1 User Description
static uint8_t OTAProfileCharUserDesp[12] = "OTA Channel";

// write and read buffer
static uint8_t OTAProfileReadLen;
static uint8_t OTAProfileReadBuf[IAP_LEN];
static uint8_t OTAProfileWriteLen;
static uint8_t OTAProfileWriteBuf[IAP_LEN];

/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t OTAProfileAttrTbl[4] = {
    // Simple Profile Service
    {
        {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */
        GATT_PERMIT_READ,                       /* permissions */
        0,                                      /* handle */
        (uint8_t *)&OTAProfileService           /* pValue */
    },

    // Characteristic Declaration
    {
        {ATT_BT_UUID_SIZE, characterUUID},
        GATT_PERMIT_READ,
        0,
        &OTAProfileCharProps},

    // Characteristic Value
    {
        {ATT_BT_UUID_SIZE, OTAProfilechar1UUID},
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        &OTAProfileChar},

    // Characteristic User Description
    {
        {ATT_BT_UUID_SIZE, charUserDescUUID},
        GATT_PERMIT_READ,
        0,
        OTAProfileCharUserDesp},
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t OTAProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                       uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method);
static bStatus_t OTAProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                        uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method);

/*********************************************************************
 * PROFILE CALLBACKS
 */
// OTA Profile Service Callbacks
gattServiceCBs_t OTAProfileCBs = {
    OTAProfile_ReadAttrCB,  // Read callback function pointer
    OTAProfile_WriteAttrCB, // Write callback function pointer
    NULL                    // Authorization callback function pointer
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      OTAProfile_AddService
 *
 * @brief   OTA Profile initialization
 *
 * @param   services    - Service control
 *
 * @return  Initialization state
 */
bStatus_t OTAProfile_AddService(uint32_t services)
{
    uint8_t status = SUCCESS;

    if(services & OTAPROFILE_SERVICE)
    {
        // Register GATT attribute list and CBs with GATT Server App
        status = GATTServApp_RegisterService(OTAProfileAttrTbl,
                                             GATT_NUM_ATTRS(OTAProfileAttrTbl),
                                             GATT_MAX_ENCRYPT_KEY_SIZE,
                                             &OTAProfileCBs);
    }

    return (status);
}

/*********************************************************************
 * @fn      OTAProfile_RegisterAppCBs
 *
 * @brief   OTA Profile read and write recovery function registration
 *
 * @param   appCallbacks    - Function structure pointer
 *
 * @return  Function execution status
 */
bStatus_t OTAProfile_RegisterAppCBs(OTAProfileCBs_t *appCallbacks)
{
    if(appCallbacks)
    {
        OTAProfile_AppCBs = appCallbacks;

        return (SUCCESS);
    }
    else
    {
        return (bleAlreadyInRequestedMode);
    }
}

/*********************************************************************
 * @fn          OTAProfile_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static bStatus_t OTAProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                       uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method)
{
    bStatus_t status = SUCCESS;

    if(pAttr->type.len == ATT_BT_UUID_SIZE)
    {
        // 16-bit UUID
        uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);

        switch(uuid)
        {
            case OTAPROFILE_CHAR_UUID:
            {
                *pLen = 0;
                if(OTAProfileReadLen)
                {
                    *pLen = OTAProfileReadLen;
                    tmos_memcpy(pValue, OTAProfileReadBuf, OTAProfileReadLen);
                    OTAProfileReadLen = 0;
                    if(OTAProfile_AppCBs && OTAProfile_AppCBs->pfnOTAProfileRead)
                    {
                        OTAProfile_AppCBs->pfnOTAProfileRead(OTAPROFILE_CHAR);
                    }
                }
                break;
            }
            default:
            {
                // Should never get here! (characteristics 3 and 4 do not have read permissions)
                *pLen = 0;
                status = ATT_ERR_ATTR_NOT_FOUND;
                break;
            }
        }
    }
    else
    {
        // 128-bit UUID
        *pLen = 0;
        status = ATT_ERR_INVALID_HANDLE;
    }

    return (status);
}

/*********************************************************************
 * @fn      OTAProfile_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
static bStatus_t OTAProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                        uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method)
{
    bStatus_t status = SUCCESS;
    //uint8_t notifyApp = 0xFF;

    if(pAttr->type.len == ATT_BT_UUID_SIZE)
    {
        // 16-bit UUID
        uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);

        switch(uuid)
        {
            case OTAPROFILE_CHAR_UUID:
            {
                //Write the value
                if(status == SUCCESS)
                {
                    uint16_t i;
                    uint8_t *p_rec_buf;

                    OTAProfileWriteLen = len;
                    p_rec_buf = pValue;
                    for(i = 0; i < OTAProfileWriteLen; i++)
                        OTAProfileWriteBuf[i] = p_rec_buf[i];
                }
                break;
            }

            default:
                // Should never get here! (characteristics 2 and 4 do not have write permissions)
                status = ATT_ERR_ATTR_NOT_FOUND;
                break;
        }
    }
    else
    {
        // 128-bit UUID
        status = ATT_ERR_INVALID_HANDLE;
    }

    if(OTAProfileWriteLen && OTAProfile_AppCBs && OTAProfile_AppCBs->pfnOTAProfileWrite)
    {
        OTAProfile_AppCBs->pfnOTAProfileWrite(OTAPROFILE_CHAR, OTAProfileWriteBuf, OTAProfileWriteLen);
        OTAProfileWriteLen = 0;
    }

    return (status);
}

/*********************************************************************
 * @fn      OTAProfile_SendData
 *
 * @brief   OTA Profile channel send data
 *
 * @param   paramID     - OTA channel selection
 * @param   p_data      - Data pointer
 * @param   send_len    - Send data length
 *
 * @return  Function execution status
 */
bStatus_t OTAProfile_SendData(unsigned char paramID, unsigned char *p_data, unsigned char send_len)
{
    bStatus_t status = SUCCESS;

    /* Data length exceeds range */
    if(send_len > 20)
        return 0xfe;

    OTAProfileReadLen = send_len;
    tmos_memcpy(OTAProfileReadBuf, p_data, OTAProfileReadLen);

    return status;
}

/*********************************************************************
*********************************************************************/
