/******************************************************************************
 * Filename:       Spectrum_Data.c
 *
 * Description:    This file contains the implementation of the service.
 *
 *                 Generated by:
 *                 BDS version: 1.1.3139.0
 *                 Plugin:      Texas Instruments BLE SDK GATT Server plugin 1.0.9
 *                 Time:        Thu Jan 11 2018 13:32:51 GMT-07:00
 *

 * Copyright (c) 2015-2017, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **********************************************************************************/


/*********************************************************************
 * INCLUDES
 */
#include <string.h>

//#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Diags.h>
#ifdef UARTLOG_ENABLE
#  include "UartLog.h"
#endif

#include <icall.h>
#include "util.h"
/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"

#include "spectrum_data.h"

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

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

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

/*********************************************************************
* GLOBAL VARIABLES
*/

// Spectrum_Data Service UUID
CONST uint8_t SpectrumDataUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(SPECTRUM_DATA_SERV_UUID), HI_UINT16(SPECTRUM_DATA_SERV_UUID)
};

// TX_Data UUID
CONST uint8_t sd_TX_DataUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(SD_TX_DATA_UUID), HI_UINT16(SD_TX_DATA_UUID)
};

// TX_Format UUID
CONST uint8_t sd_TX_FormatUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(SD_TX_FORMAT_UUID), HI_UINT16(SD_TX_FORMAT_UUID)
};

// TX_Read UUID
CONST uint8_t sd_TX_ReadUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(SD_TX_READ_UUID), HI_UINT16(SD_TX_READ_UUID)
};


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

static SpectrumDataCBs_t *pAppCBs = NULL;

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

// Service declaration
static CONST gattAttrType_t SpectrumDataDecl = { ATT_BT_UUID_SIZE, SpectrumDataUUID };

// Characteristic "TX Data" Properties (for declaration)
static uint8_t sd_TX_DataProps = GATT_PROP_READ;

// Characteristic "TX Data" Value variable
static uint8_t sd_TX_DataVal[SD_TX_DATA_LEN] = {0};

// Length of data in characteristic "TX Data" Value variable, initialized to minimal size.
static uint16_t sd_TX_DataValLen = SD_TX_DATA_LEN_MIN;



// Characteristic "TX Format" Properties (for declaration)
static uint8_t sd_TX_FormatProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "TX Format" Value variable
static uint8_t sd_TX_FormatVal[SD_TX_FORMAT_LEN] = {0};

// Length of data in characteristic "TX Format" Value variable, initialized to minimal size.
static uint16_t sd_TX_FormatValLen = SD_TX_FORMAT_LEN_MIN;



// Characteristic "TX Read" Properties (for declaration)
static uint8_t sd_TX_ReadProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "TX Read" Value variable
static uint8_t sd_TX_ReadVal[SD_TX_READ_LEN] = {0};

// Length of data in characteristic "TX Read" Value variable, initialized to minimal size.
static uint16_t sd_TX_ReadValLen = SD_TX_READ_LEN_MIN;



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

static gattAttribute_t Spectrum_DataAttrTbl[] =
{
  // Spectrum_Data Service Declaration
  {
    { ATT_BT_UUID_SIZE, primaryServiceUUID },
    GATT_PERMIT_READ,
    0,
    (uint8_t *)&SpectrumDataDecl
  },
    // TX Data Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &sd_TX_DataProps
    },
      // TX Data Characteristic Value
      {
        { ATT_BT_UUID_SIZE, sd_TX_DataUUID },
        GATT_PERMIT_READ,
        0,
        sd_TX_DataVal
      },
    // TX Format Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &sd_TX_FormatProps
    },
      // TX Format Characteristic Value
      {
        { ATT_BT_UUID_SIZE, sd_TX_FormatUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        sd_TX_FormatVal
      },
    // TX Read Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &sd_TX_ReadProps
    },
      // TX Read Characteristic Value
      {
        { ATT_BT_UUID_SIZE, sd_TX_ReadUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        sd_TX_ReadVal
      },
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t Spectrum_Data_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 Spectrum_Data_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                            uint8_t *pValue, uint16_t len, uint16_t offset,
                                            uint8_t method );

/*********************************************************************
 * PROFILE CALLBACKS
 */
// Simple Profile Service Callbacks
CONST gattServiceCBs_t Spectrum_DataCBs =
{
  Spectrum_Data_ReadAttrCB,  // Read callback function pointer
  Spectrum_Data_WriteAttrCB, // Write callback function pointer
  NULL                       // Authorization callback function pointer
};

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

/*
 * SpectrumData_AddService- Initializes the SpectrumData service by registering
 *          GATT attributes with the GATT server.
 *
 *    rspTaskId - The ICall Task Id that should receive responses for Indications.
 */
extern bStatus_t SpectrumData_AddService( uint8_t rspTaskId )
{
  uint8_t status;

  // Register GATT attribute list and CBs with GATT Server App
  status = GATTServApp_RegisterService( Spectrum_DataAttrTbl,
                                        GATT_NUM_ATTRS( Spectrum_DataAttrTbl ),
                                        GATT_MAX_ENCRYPT_KEY_SIZE,
                                        &Spectrum_DataCBs );
  Log_info1("Registered service, %d attributes", (IArg)GATT_NUM_ATTRS( Spectrum_DataAttrTbl ));
  return ( status );
}

/*
 * SpectrumData_RegisterAppCBs - Registers the application callback function.
 *                    Only call this function once.
 *
 *    appCallbacks - pointer to application callbacks.
 */
bStatus_t SpectrumData_RegisterAppCBs( SpectrumDataCBs_t *appCallbacks )
{
  if ( appCallbacks )
  {
    pAppCBs = appCallbacks;
    Log_info1("Registered callbacks to application. Struct %p", (IArg)appCallbacks);
    return ( SUCCESS );
  }
  else
  {
    Log_warning0("Null pointer given for app callbacks.");
    return ( FAILURE );
  }
}

/*
 * SpectrumData_SetParameter - Set a SpectrumData parameter.
 *
 *    param - Profile parameter ID
 *    len   - length of data to write
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t SpectrumData_SetParameter( uint8_t param, uint16_t len, void *value )
{
  bStatus_t ret = SUCCESS;
  uint8_t  *pAttrVal;
  uint16_t *pValLen;
  uint16_t valMinLen;
  uint16_t valMaxLen;

  switch ( param )
  {
    case SD_TX_DATA_ID:
      pAttrVal  =  sd_TX_DataVal;
      pValLen   = &sd_TX_DataValLen;
      valMinLen =  SD_TX_DATA_LEN_MIN;
      valMaxLen =  SD_TX_DATA_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"TX_Data", (IArg)len);
      break;

    case SD_TX_FORMAT_ID:
      pAttrVal  =  sd_TX_FormatVal;
      pValLen   = &sd_TX_FormatValLen;
      valMinLen =  SD_TX_FORMAT_LEN_MIN;
      valMaxLen =  SD_TX_FORMAT_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"TX_Format", (IArg)len);
      break;

    case SD_TX_READ_ID:
      pAttrVal  =  sd_TX_ReadVal;
      pValLen   = &sd_TX_ReadValLen;
      valMinLen =  SD_TX_READ_LEN_MIN;
      valMaxLen =  SD_TX_READ_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"TX_Read", (IArg)len);
      break;

    default:
      Log_error1("SetParameter: Parameter #%d not valid.", (IArg)param);
      return INVALIDPARAMETER;
  }

  // Check bounds, update value and send notification or indication if possible.
  if ( len <= valMaxLen && len >= valMinLen )
  {
    memcpy(pAttrVal, value, len);
    *pValLen = len; // Update length for read and get.
  }
  else
  {
    Log_error3("Length outside bounds: Len: %d MinLen: %d MaxLen: %d.", (IArg)len, (IArg)valMinLen, (IArg)valMaxLen);
    ret = bleInvalidRange;
  }

  return ret;
}


/*
 * SpectrumData_GetParameter - Get a SpectrumData parameter.
 *
 *    param - Profile parameter ID
 *    len   - pointer to a variable that contains the maximum length that can be written to *value.
              After the call, this value will contain the actual returned length.
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t SpectrumData_GetParameter( uint8_t param, uint16_t *len, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case SD_TX_FORMAT_ID:
      *len = MIN(*len, sd_TX_FormatValLen);
      memcpy(value, sd_TX_FormatVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"TX_Format", (IArg)*len);
      break;

    case SD_TX_READ_ID:
      *len = MIN(*len, sd_TX_ReadValLen);
      memcpy(value, sd_TX_ReadVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"TX_Read", (IArg)*len);
      break;

    default:
      Log_error1("GetParameter: Parameter #%d not valid.", (IArg)param);
      ret = INVALIDPARAMETER;
      break;
  }
  return ret;
}

/*********************************************************************
 * @internal
 * @fn          Spectrum_Data_findCharParamId
 *
 * @brief       Find the logical param id of an attribute in the service's attr table.
 *
 *              Works only for Characteristic Value attributes and
 *              Client Characteristic Configuration Descriptor attributes.
 *
 * @param       pAttr - pointer to attribute
 *
 * @return      uint8_t paramID (ref Spectrum_Data.h) or 0xFF if not found.
 */
static uint8_t Spectrum_Data_findCharParamId(gattAttribute_t *pAttr)
{
  // Is this a Client Characteristic Configuration Descriptor?
  if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
    return Spectrum_Data_findCharParamId(pAttr - 1); // Assume the value attribute precedes CCCD and recurse

  // Is this attribute in "TX Data"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, sd_TX_DataUUID, pAttr->type.len))
    return SD_TX_DATA_ID;

  // Is this attribute in "TX Format"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, sd_TX_FormatUUID, pAttr->type.len))
    return SD_TX_FORMAT_ID;

  // Is this attribute in "TX Read"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, sd_TX_ReadUUID, pAttr->type.len))
    return SD_TX_READ_ID;

  else
    return 0xFF; // Not found. Return invalid.
}

/*********************************************************************
 * @fn          Spectrum_Data_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
 * @param       method - type of read message
 *
 * @return      SUCCESS, blePending or Failure
 */
static bStatus_t Spectrum_Data_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;
  uint16_t valueLen;
  uint8_t paramID = 0xFF;

  // Find settings for the characteristic to be read.
  paramID = Spectrum_Data_findCharParamId( pAttr );
  switch ( paramID )
  {
    case SD_TX_DATA_ID:
      valueLen = sd_TX_DataValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"TX_Data",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TX Data can be inserted here */
      break;

    case SD_TX_FORMAT_ID:
      valueLen = sd_TX_FormatValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"TX_Format",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TX Format can be inserted here */
      break;

    case SD_TX_READ_ID:
      valueLen = sd_TX_ReadValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"TX_Read",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TX Read can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check bounds and return the value
  if ( offset > valueLen )  // Prevent malicious ATT ReadBlob offsets.
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else
  {
    *pLen = MIN(maxLen, valueLen - offset);  // Transmit as much as possible
    memcpy(pValue, pAttr->pValue + offset, *pLen);
  }

  return status;
}

/*********************************************************************
 * @fn      Spectrum_Data_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
 * @param   method - type of write message
 *
 * @return  SUCCESS, blePending or Failure
 */
static bStatus_t Spectrum_Data_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   paramID = 0xFF;
  uint8_t   changeParamID = 0xFF;
  uint16_t writeLenMin;
  uint16_t writeLenMax;
  uint16_t *pValueLenVar;

  // Find settings for the characteristic to be written.
  paramID = Spectrum_Data_findCharParamId( pAttr );
  switch ( paramID )
  {
    case SD_TX_FORMAT_ID:
      writeLenMin  = SD_TX_FORMAT_LEN_MIN;
      writeLenMax  = SD_TX_FORMAT_LEN;
      pValueLenVar = &sd_TX_FormatValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"TX_Format",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TX Format can be inserted here */
      break;

    case SD_TX_READ_ID:
      writeLenMin  = SD_TX_READ_LEN_MIN;
      writeLenMax  = SD_TX_READ_LEN;
      pValueLenVar = &sd_TX_ReadValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"TX_Read",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TX Read can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check whether the length is within bounds.
  if ( offset >= writeLenMax )
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else if ( offset + len > writeLenMax )
  {
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else if ( offset + len < writeLenMin && ( method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ ) )
  {
    // Refuse writes that are lower than minimum.
    // Note: Cannot determine if a Reliable Write (to several chars) is finished, so those will
    //       only be refused if this attribute is the last in the queue (method is execute).
    //       Otherwise, reliable writes are accepted and parsed piecemeal.
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else
  {
    // Copy pValue into the variable we point to from the attribute table.
    memcpy(pAttr->pValue + offset, pValue, len);

    // Only notify application and update length if enough data is written.
    //
    // Note: If reliable writes are used (meaning several attributes are written to using ATT PrepareWrite),
    //       the application will get a callback for every write with an offset + len larger than _LEN_MIN.
    // Note: For Long Writes (ATT Prepare + Execute towards only one attribute) only one callback will be issued,
    //       because the write fragments are concatenated before being sent here.
    if ( offset + len >= writeLenMin )
    {
      changeParamID = paramID;
      *pValueLenVar = offset + len; // Update data length.
    }
  }

  // Let the application know something changed (if it did) by using the
  // callback it registered earlier (if it did).
  if (changeParamID != 0xFF)
    if ( pAppCBs && pAppCBs->pfnChangeCb )
      pAppCBs->pfnChangeCb( connHandle, SPECTRUM_DATA_SERV_UUID, paramID, pValue, len+offset ); // Call app function from stack task context.

  return status;
}
