/**************************************************************************************************
 
  Phyplus Microelectronics Limited confidential and proprietary. 
  All rights reserved.

  IMPORTANT: All rights of this software belong to Phyplus Microelectronics 
  Limited ("Phyplus"). Your use of this Software is limited to those 
  specific rights granted under  the terms of the business contract, the 
  confidential agreement, the non-disclosure agreement and any other forms 
  of agreements as a customer or a partner of Phyplus. You may not use this 
  Software unless you agree to abide by the terms of these agreements. 
  You acknowledge that the Software may not be modified, copied, 
  distributed or disclosed unless embedded on a Phyplus Bluetooth Low Energy 
  (BLE) integrated circuit, either as a product or is integrated into your 
  products.  Other than for the aforementioned purposes, you may not use, 
  reproduce, copy, prepare derivative works of, modify, distribute, perform, 
  display or sell this Software and/or its documentation for any purposes.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  PHYPLUS OR ITS SUBSIDIARIES BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
  
**************************************************************************************************/



/*********************************************************************
 * INCLUDES
 */

#include "OSAL.h"
#include "gatt.h"
#include "hci.h"
#include "gapgattserver.h"
#include "gattservapp.h"
#include "gatt_uuid.h"
#include "gatt_profile_uuid.h"
#include "linkdb.h"
#include "peripheral.h"
#include "gapbondmgr.h"
#include "devinfoservice.h"
#include "battservice.h"
#include "hiddev.h"
#include "hidkbdservice.h"
//#include "touch_key.h"
#include "log.h"

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

// Battery measurement period in ms
#define DEFAULT_BATT_PERIOD                   15000

// TRUE to run scan parameters refresh notify test
#define DEFAULT_SCAN_PARAM_NOTIFY_TEST        TRUE

// Advertising intervals (units of 625us, 160=100ms)
#define HID_INITIAL_ADV_INT_MIN               48
#define HID_INITIAL_ADV_INT_MAX               80
#define HID_HIGH_ADV_INT_MIN                  32
#define HID_HIGH_ADV_INT_MAX                  48
#define HID_LOW_ADV_INT_MIN                   1600
#define HID_LOW_ADV_INT_MAX                   1600

// Advertising timeouts in sec
#define HID_INITIAL_ADV_TIMEOUT               60
#define HID_HIGH_ADV_TIMEOUT                  5
#define HID_LOW_ADV_TIMEOUT                   0

// Heart Rate Task Events
#define START_DEVICE_EVT                      0x0001
#define BATT_PERIODIC_EVT                     0x0002
#define HID_IDLE_EVT                          0x0004
#define HID_SEND_REPORT_EVT                   0x0008
#define HID_TEST_EVT                          0x0100

#define reportQEmpty()                        ( firstQIdx == lastQIdx )

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

#define HID_DEV_DATA_LEN                      8

#ifdef HID_DEV_RPT_QUEUE_LEN
  #define HID_DEV_REPORT_Q_SIZE               (HID_DEV_RPT_QUEUE_LEN+1)
#else
  #define HID_DEV_REPORT_Q_SIZE               (10+1)
#endif

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

typedef struct
{
 uint8 id;
 uint8 type;
 uint8 len;
 uint8 data[HID_DEV_DATA_LEN];
} hidDevReport_t;

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

// Task ID
uint8 hidDevTaskId;

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

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

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

// GAP State
static gaprole_States_t hidDevGapState = GAPROLE_INIT;

// TRUE if connection is secure
static uint8 hidDevConnSecure = FALSE;

// GAP connection handle
static uint16 gapConnHandle;

// TRUE if pairing in progress
static uint8 hidDevPairingStarted = FALSE;

// Status of last pairing
static uint8 pairingStatus = SUCCESS;

static hidRptMap_t *pHidDevRptTbl;

static uint8 hidDevRptTblLen;

static hidDevCB_t *pHidDevCB;

static hidDevCfg_t *pHidDevCfg;

// Whether to change to the preferred connection parameters
static uint8 updateConnParams = TRUE;

// Pending reports
static uint8 firstQIdx = 0;
static uint8 lastQIdx = 0;
static hidDevReport_t hidDevReportQ[HID_DEV_REPORT_Q_SIZE];

// Last report sent out
static attHandleValueNoti_t lastNoti = { 0 };

/*********************************************************************
 * LOCAL FUNCTIONS
 */

static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg );
static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg );
static void hidDevDisconnected( void );
static void hidDevBattPeriodicTask( void );
static hidRptMap_t *hidDevRptByHandle( uint16 handle );
static hidRptMap_t *hidDevRptById( uint8 id, uint8 type );
static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle );
static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData );
static hidDevReport_t *hidDevDequeueReport( void );
static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData );
static void hidDevHighAdvertising( void );
static void hidDevLowAdvertising( void );
static void hidDevInitialAdvertising( void );
static uint8 hidDevBondCount( void );
static void hidDevStartIdleTimer( void );
static void hidDevStopIdleTimer( void );

/*********************************************************************
 * PROFILE CALLBACKS
 */




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

/*********************************************************************
 * @fn      HidDev_Init
 *
 * @brief   Initialization function for the Hid Dev Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notificaiton ... ).
 *
 * @param   task_id - the ID assigned by OSAL.  This ID should be
 *                    used to send messages and set timers.
 *
 * @return  none
 */
void HidDev_Init( uint8 task_id )
{
  hidDevTaskId = task_id;

  // Setup the GAP Bond Manager
  {
    uint8 syncWL = TRUE;

    // If a bond is created, the HID Device should write the address of the
    // HID Host in the HID Device controller's white list and set the HID
    // Device controller's advertising filter policy to 'process scan and
    // connection requests only from devices in the White List'.
    VOID GAPBondMgr_SetParameter( GAPBOND_AUTO_SYNC_WL, sizeof( uint8 ), &syncWL );
  }

  // Set up services
  GGS_AddService( GATT_ALL_SERVICES );         // GAP
  GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes
  DevInfo_AddService( );
  Batt_AddService();
  Batt_Register(NULL);
  //touch_init(on_key);

  // Setup a delayed profile startup
  osal_set_event( hidDevTaskId, START_DEVICE_EVT );
}

/*********************************************************************
 * @fn      HidDev_ProcessEvent
 *
 * @brief   Hid Dev Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *
 * @param   task_id  - The OSAL assigned task ID.
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event.
 *
 * @return  events not processed
 */
uint16 HidDev_ProcessEvent( uint8 task_id, uint16 events )
{

  VOID task_id; // OSAL required parameter that isn't used in this function
  LOG("%s\n",__FUNCTION__);

  if ( events & SYS_EVENT_MSG )
  {
    uint8 *pMsg;

    if ( (pMsg = osal_msg_receive( hidDevTaskId )) != NULL )
    {
      hidDev_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

      // Release the OSAL message
      VOID osal_msg_deallocate( pMsg );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & START_DEVICE_EVT )
  {
    // Start the Device
    //VOID GAPRole_StartDevice( &hidDev_PeripheralCBs );

    // Register with bond manager after starting device
    //GAPBondMgr_Register( (gapBondCBs_t *) &hidDevBondCB );

    return ( events ^ START_DEVICE_EVT );
  }

  if ( events & HID_IDLE_EVT )
  {
    if ( hidDevGapState == GAPROLE_CONNECTED )
    {
      // if pairing in progress then restart timer
      if ( hidDevPairingStarted )
      {
        hidDevStartIdleTimer();
      }
      // else disconnect
      else
      {
        GAPRole_TerminateConnection();
      }
    }

    return ( events ^ HID_IDLE_EVT );
  }

  if ( events & BATT_PERIODIC_EVT )
  {
    // Perform periodic battery task
    hidDevBattPeriodicTask();

    return ( events ^ BATT_PERIODIC_EVT );
  }

  if ( events & HID_SEND_REPORT_EVT )
  {
    // if connection is secure
    if ( hidDevConnSecure )
    {
      hidDevReport_t *pReport = hidDevDequeueReport();

      if ( pReport != NULL )
      {
        // Send report
        hidDevSendReport( pReport->id, pReport->type, pReport->len, pReport->data );
      }

      return ( reportQEmpty() ? events ^ HID_SEND_REPORT_EVT : events );
    }

    return ( events ^ HID_SEND_REPORT_EVT );
  }

  return 0;
}

/*********************************************************************
 * @fn      HidDev_Register
 *
 * @brief   Register a callback function with HID Dev.
 *
 * @param   pCfg - Parameter configuration.
 * @param   pfnServiceCB - Callback function.
 *
 * @return  None.
 */
void HidDev_Register( hidDevCfg_t *pCfg, hidDevCB_t *pCBs )
{
  pHidDevCB = pCBs;
  pHidDevCfg = pCfg;
}

/*********************************************************************
 * @fn      HidDev_RegisterReports
 *
 * @brief   Register the report table with HID Dev.
 *
 * @param   numReports - Length of report table.
 * @param   pRpt - Report table.
 *
 * @return  None.
 */
void HidDev_RegisterReports( uint8 numReports, hidRptMap_t *pRpt )
{
  pHidDevRptTbl = pRpt;
  hidDevRptTblLen = numReports;
}

/*********************************************************************
 * @fn      HidDev_Report
 *
 * @brief   Send a HID report.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
void HidDev_Report( uint8 id, uint8 type, uint8 len, uint8*pData )
{
  // if connected
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    // if connection is secure
    if ( hidDevConnSecure )
    {
      // Make sure there're no pending reports
      if ( reportQEmpty() )
      {
        // send report
        hidDevSendReport( id, type, len, pData );

        return; // we're done
      }
    }
  }
  // else if not already advertising
  else if ( hidDevGapState != GAPROLE_ADVERTISING )
  {
    // if bonded
    if ( hidDevBondCount() > 0 )
    {
      // start high duty cycle advertising
      hidDevHighAdvertising();
    }
    // else not bonded
    else
    {
      // start initial advertising
      hidDevInitialAdvertising();
    }
  }

  // hidDev task will send report when secure connection is established
  hidDevEnqueueReport( id, type, len, pData );
}

/*********************************************************************
 * @fn      HidDev_Close
 *
 * @brief   Close the connection or stop advertising.
 *
 * @return  None.
 */
void HidDev_Close( void )
{
  uint8 param;

  // if connected then disconnect
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    GAPRole_TerminateConnection();
  }
  // else stop advertising
  else
  {
    param = FALSE;
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
  }
}

/*********************************************************************
 * @fn      HidDev_SetParameter
 *
 * @brief   Set a HID Dev parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   pValue - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t HidDev_SetParameter( uint8 param, uint8 len, void *pValue )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {
    case HIDDEV_ERASE_ALLBONDS:
      if ( len == 0 )
      {
        // See if the last report sent out wasn't a release key
        if ( osal_isbufset( lastNoti.value, 0x00, lastNoti.len ) == FALSE )
        {
          // Send a release report before disconnecting, otherwise
          // the last pressed key would get 'stuck' on the HID Host.
          osal_memset( lastNoti.value, 0x00, lastNoti.len );

          GATT_Notification( gapConnHandle, &lastNoti, FALSE );
        }

        // Drop connection
        if ( hidDevGapState == GAPROLE_CONNECTED )
        {
          GAPRole_TerminateConnection();
        }

        // Flush report queue
        firstQIdx = lastQIdx = 0;

        // Erase bonding info
        GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL );
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn      HidDev_GetParameter
 *
 * @brief   Get a HID Dev parameter.
 *
 * @param   param - Profile parameter ID
 * @param   pValue - pointer to data to get.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t HidDev_GetParameter( uint8 param, void *pValue )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {

    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn      HidDev_PasscodeRsp
 *
 * @brief   Respond to a passcode request.
 *
 * @param   status - SUCCESS if passcode is available, otherwise
 *                   see @ref SMP_PAIRING_FAILED_DEFINES.
 * @param   passcode - integer value containing the passcode.
 *
 * @return  none
 */
void HidDev_PasscodeRsp( uint8 status, uint32 passcode )
{
  // Send passcode response
  GAPBondMgr_PasscodeRsp( gapConnHandle, status, passcode );
}

/*********************************************************************
 * @fn          HidDev_ReadAttrCB
 *
 * @brief       HID Dev attribute read callback.
 *
 * @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
 */
uint8 HidDev_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                         uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
{
  bStatus_t   status = SUCCESS;
  hidRptMap_t *pRpt;

  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

  // Only report map is long
  if ( offset > 0 && uuid != REPORT_MAP_UUID )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }

  if ( uuid == REPORT_UUID ||
       uuid == BOOT_KEY_INPUT_UUID ||
       uuid == BOOT_KEY_OUTPUT_UUID ||
       uuid == BOOT_MOUSE_INPUT_UUID )
  {
    // find report ID in table
    if ( (pRpt = hidDevRptByHandle(pAttr->handle)) != NULL )
    {
      // execute report callback
      status  = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                        HID_DEV_OPER_READ, pLen, pValue );
    }
    else
    {
      *pLen = 0;
    }
  }
  else if ( uuid == REPORT_MAP_UUID )
  {
    // verify offset
    if ( offset >= hidReportMapLen )
    {
      status = ATT_ERR_INVALID_OFFSET;
    }
    else
    {
      // determine read length
      *pLen = MIN( maxLen, (hidReportMapLen - offset) );

      // copy data
      osal_memcpy( pValue, pAttr->pValue + offset, *pLen );
    }
  }
  else if ( uuid == HID_INFORMATION_UUID )
  {
    *pLen = HID_INFORMATION_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_INFORMATION_LEN );
  }
  else if ( uuid == GATT_REPORT_REF_UUID )
  {
    *pLen = HID_REPORT_REF_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_REPORT_REF_LEN );
  }
  else if ( uuid == PROTOCOL_MODE_UUID )
  {
    *pLen = HID_PROTOCOL_MODE_LEN;
    pValue[0] = pAttr->pValue[0];
  }
  else if ( uuid == GATT_EXT_REPORT_REF_UUID )
  {
    *pLen = HID_EXT_REPORT_REF_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_EXT_REPORT_REF_LEN );
  }

  // restart idle timer
  if ( status == SUCCESS )
  {
    hidDevStartIdleTimer();
  }

  return ( status );
}

/*********************************************************************
 * @fn      HidDev_WriteAttrCB
 *
 * @brief   HID Dev attribute read callback.
 *
 * @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
 */
bStatus_t HidDev_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                              uint8 *pValue, uint8 len, uint16 offset )
{
  bStatus_t status = SUCCESS;
  hidRptMap_t *pRpt;

  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }

  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

  if ( uuid == REPORT_UUID ||
       uuid == BOOT_KEY_OUTPUT_UUID )
  {
    // find report ID in table
    if ((pRpt = hidDevRptByHandle(pAttr->handle)) != NULL)
    {
      // execute report callback
      status  = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                        HID_DEV_OPER_WRITE, &len, pValue );
    }
  }
  else if ( uuid == HID_CTRL_PT_UUID )
  {
    // Validate length and value range
    if ( len == 1 )
    {
      if ( pValue[0] == HID_CMD_SUSPEND ||  pValue[0] == HID_CMD_EXIT_SUSPEND )
      {
        // execute HID app event callback
        (*pHidDevCB->evtCB)( (pValue[0] == HID_CMD_SUSPEND) ?
                             HID_DEV_SUSPEND_EVT : HID_DEV_EXIT_SUSPEND_EVT );
      }
      else
      {
        status = ATT_ERR_INVALID_VALUE;
      }
    }
    else
    {
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
  }
  else if ( uuid == GATT_CLIENT_CHAR_CFG_UUID )
  {
    status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                             offset, GATT_CLIENT_CFG_NOTIFY );
    if ( status == SUCCESS )
    {
      uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );

      // find report ID in table
      if ( (pRpt = hidDevRptByCccdHandle(pAttr->handle)) != NULL )
      {
        // execute report callback
        (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                (charCfg == GATT_CLIENT_CFG_NOTIFY) ?
                                  HID_DEV_OPER_ENABLE : HID_DEV_OPER_DISABLE,
                                &len, pValue );
      }
    }
  }
  else if ( uuid == PROTOCOL_MODE_UUID )
  {
    if ( len == HID_PROTOCOL_MODE_LEN )
    {
      if ( pValue[0] == HID_PROTOCOL_MODE_BOOT ||
           pValue[0] == HID_PROTOCOL_MODE_REPORT )
      {
        pAttr->pValue[0] = pValue[0];

        // execute HID app event callback
        (*pHidDevCB->evtCB)( (pValue[0] == HID_PROTOCOL_MODE_BOOT) ?
                             HID_DEV_SET_BOOT_EVT : HID_DEV_SET_REPORT_EVT );
      }
      else
      {
        status = ATT_ERR_INVALID_VALUE;
      }
    }
    else
    {
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
  }

  // restart idle timer
  if (status == SUCCESS)
  {
    hidDevStartIdleTimer();
  }

  return ( status );
}

/*********************************************************************
 * @fn      hidDev_ProcessOSALMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
  case GATT_MSG_EVENT:
      hidDevProcessGattMsg( (gattMsgEvent_t *) pMsg );
      break;

  default:
      break;
  }
}

/*********************************************************************
 * @fn      hidDevProcessGattMsg
 *
 * @brief   Process GATT messages
 *
 * @return  none
 */
static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg )
{

}

/*********************************************************************
 * @fn          hidDevHandleConnStatusCB
 *
 * @brief       Reset client char config.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
static void hidDevHandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
  uint8           i;
  hidRptMap_t     *p = pHidDevRptTbl;
  uint16          retHandle;
  gattAttribute_t *pAttr;

  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
           ( !linkDB_Up( connHandle ) ) ) )
    {
      for ( i = hidDevRptTblLen; i > 0; i--, p++ )
      {
        if ( p->cccdHandle != 0 )
        {
          if ( (pAttr = GATT_FindHandle(p->cccdHandle, &retHandle)) != NULL )
          {
            GATTServApp_InitCharCfg( connHandle, (gattCharCfg_t *) pAttr->pValue );
          }
        }
      }
    }
  }
}

/*********************************************************************
 * @fn      hidDevDisconnected
 *
 * @brief   Handle disconnect.
 *
 * @return  none
 */
static void hidDevDisconnected( void )
{
  // Stop idle timer
  hidDevStopIdleTimer();

  // Reset client characteristic configuration descriptors
  Batt_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );
  //ScanParam_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );
  hidDevHandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );

  // Reset state variables
  hidDevConnSecure = FALSE;
  hidProtocolMode = HID_PROTOCOL_MODE_REPORT;
  hidDevPairingStarted = FALSE;

  // Reset last report sent out
  osal_memset( &lastNoti, 0, sizeof( attHandleValueNoti_t ) );

  // if bonded and normally connectable start advertising
  if ( ( hidDevBondCount() > 0 ) &&
       ( pHidDevCfg->hidFlags & HID_FLAGS_NORMALLY_CONNECTABLE ) )
  {
    hidDevLowAdvertising();
  }
}

/*********************************************************************
 * @fn      hidDevGapStateCB
 *
 * @brief   Notification from the profile of a state change.
 *
 * @param   newState - new state
 *
 * @return  none
 */
void hidDevGapStateCB( gaprole_States_t newState )
{
  LOG("%s, %d\n",__FUNCTION__, newState);
  // if connected
  if ( newState == GAPROLE_CONNECTED )
  {
    // get connection handle
    GAPRole_GetParameter( GAPROLE_CONNHANDLE, &gapConnHandle );

    // connection not secure yet
    hidDevConnSecure = FALSE;

    // don't start advertising when connection is closed
    uint8 param = FALSE;
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );

    // start idle timer
    hidDevStartIdleTimer();
  }
  // if disconnected
  else if ( hidDevGapState == GAPROLE_CONNECTED &&
            newState != GAPROLE_CONNECTED )
  {
    hidDevDisconnected();
    updateConnParams = TRUE;

    if ( pairingStatus == SMP_PAIRING_FAILED_CONFIRM_VALUE )
    {
      // bonding failed due to mismatched confirm values
      hidDevInitialAdvertising();

      pairingStatus = SUCCESS;
    }
  }
  // if started
  else if ( newState == GAPROLE_STARTED )
  {
    // nothing to do for now!
  }

  hidDevGapState = newState;
}

/*********************************************************************
 * @fn      hidDevPairStateCB
 *
 * @brief   Pairing state callback.
 *
 * @return  none
 */
void hidDevPairStateCB( uint16 connHandle, uint8 state, uint8 status )
{
  if ( state == GAPBOND_PAIRING_STATE_STARTED )
  {
    hidDevPairingStarted = TRUE;
  }
  else if ( state == GAPBOND_PAIRING_STATE_COMPLETE )
  {
    hidDevPairingStarted = FALSE;

    if ( status == SUCCESS )
    {
      hidDevConnSecure = TRUE;
    }

    pairingStatus = status;
  }
  else if ( state == GAPBOND_PAIRING_STATE_BONDED )
  {
    if ( status == SUCCESS )
    {
      hidDevConnSecure = TRUE;
    }
  }
  //if(hidDevConnSecure){
  //  osal_start_reload_timer(hidDevTaskId, HID_TEST_EVT, 1000);
  //}
  if ( !reportQEmpty() && hidDevConnSecure )
  {
    // Notify our task to send out pending reports
    osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT );
  }
}

/*********************************************************************
 * @fn      hidDevPasscodeCB
 *
 * @brief   Passcode callback.
 *
 * @param   deviceAddr - address of device to pair with, and could be either public or random.
 * @param   connectionHandle - connection handle
 * @param   uiInputs - pairing User Interface Inputs - Ask user to input passcode
 * @param   uiOutputs - pairing User Interface Outputs - Display passcode
 *
 * @return  none
 */
void hidDevPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
                                        uint8 uiInputs, uint8 uiOutputs )
{
  if ( pHidDevCB && pHidDevCB->passcodeCB )
  {
    // execute HID app passcode callback
    (*pHidDevCB->passcodeCB)( deviceAddr, connectionHandle, uiInputs, uiOutputs );
  }
  else
  {
    // Send passcode response
    GAPBondMgr_PasscodeRsp( connectionHandle, SUCCESS, 0 );
  }
}


/*********************************************************************
 * @fn      hidDevBattPeriodicTask
 *
 * @brief   Perform a periodic task for battery measurement.
 *
 * @param   none
 *
 * @return  none
 */
static void hidDevBattPeriodicTask( void )
{
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    // perform battery level check
    Batt_MeasLevel( );

    // Restart timer
    osal_start_timerEx( hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
  }
}

/*********************************************************************
 * @fn      hidDevRptByHandle
 *
 * @brief   Find the HID report structure for the given handle.
 *
 * @param   handle - ATT handle
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptByHandle( uint16 handle )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->handle == handle && p->mode == hidProtocolMode)
    {
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevRptByCccdHandle
 *
 * @brief   Find the HID report structure for the given CCC handle.
 *
 * @param   handle - ATT handle
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->cccdHandle == handle)
    {
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevRptById
 *
 * @brief   Find the HID report structure for the Report ID and type.
 *
 * @param   id - HID report ID
 * @param   type - HID report type
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptById( uint8 id, uint8 type )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->id == id && p->type == type && p->mode == hidProtocolMode )
    {
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevSendReport
 *
 * @brief   Send a HID report.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData )
{
  hidRptMap_t           *pRpt;
  gattAttribute_t       *pAttr;
  uint16                retHandle;

  LOG("%s\n",__FUNCTION__);
  // Get ATT handle for report
  if ( (pRpt = hidDevRptById(id, type)) != NULL )
  {
    // if notifications are enabled
    if ( (pAttr = GATT_FindHandle(pRpt->cccdHandle, &retHandle)) != NULL )
    {
      uint16 value;

      value  = GATTServApp_ReadCharCfg( gapConnHandle, (gattCharCfg_t *) pAttr->pValue );
      if ( value & GATT_CLIENT_CFG_NOTIFY )
      {
        // After service discovery and encryption, the HID Device should request to
        // change to the preferred connection parameters that best suit its use case.
        if ( updateConnParams )
        {
          GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_REQ, sizeof( uint8 ), &updateConnParams );
          updateConnParams = FALSE;
        }

        // send notification
        lastNoti.handle = pRpt->handle;
        lastNoti.len = len;
        osal_memcpy(lastNoti.value, pData, len);

        GATT_Notification( gapConnHandle, &lastNoti, FALSE );

        // start idle timer
        hidDevStartIdleTimer();
      }
    }
  }
}

/*********************************************************************
 * @fn      hidDevEnqueueReport
 *
 * @brief   Enqueue a HID report to be sent later.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData )
{
  // Enqueue only if bonded
  if ( hidDevBondCount() > 0 )
  {
    // Update last index
    lastQIdx = ( lastQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;

    if ( lastQIdx == firstQIdx )
    {
      // Queue overflow; discard oldest report
      firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;
    }

    // Save report
    hidDevReportQ[lastQIdx].id = id;
    hidDevReportQ[lastQIdx].type = type;
    hidDevReportQ[lastQIdx].len = len;
    osal_memcpy( hidDevReportQ[lastQIdx].data, pData, len );

    if ( hidDevConnSecure )
    {
      // Notify our task to send out pending reports
      osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT );
    }
  }
}

/*********************************************************************
 * @fn      hidDevDequeueReport
 *
 * @brief   Dequeue a HID report to be sent out.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static hidDevReport_t *hidDevDequeueReport( void )
{
  if ( reportQEmpty() )
  {
    return NULL;
  }

  // Update first index
  firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;

  return ( &(hidDevReportQ[firstQIdx]) );
}

/*********************************************************************
 * @fn      hidDevHighAdvertising
 *
 * @brief   Start advertising at a high duty cycle.

 * @param   None.
 *
 * @return  None.
 */
static void hidDevHighAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_HIGH_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_HIGH_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_HIGH_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_WHITE;
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevLowAdvertising
 *
 * @brief   Start advertising at a low duty cycle.
 *
 * @param   None.
 *
 * @return  None.
 */
static void hidDevLowAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_LOW_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_LOW_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_LOW_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_WHITE;
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevInitialAdvertising
 *
 * @brief   Start advertising for initial connection
 *
 * @return  None.
 */
static void hidDevInitialAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_INITIAL_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_INITIAL_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_INITIAL_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_ALL;
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevBondCount
 *
 * @brief   Gets the total number of bonded devices.
 *
 * @param   None.
 *
 * @return  number of bonded devices.
 */
static uint8 hidDevBondCount( void )
{
  uint8 bondCnt = 0;

  VOID GAPBondMgr_GetParameter( GAPBOND_BOND_COUNT, &bondCnt );

  return ( bondCnt );
}

/*********************************************************************
 * @fn      hidDevStartIdleTimer
 *
 * @brief   Start the idle timer.
 *
 * @return  None.
 */
static void hidDevStartIdleTimer( void )
{
  if ( pHidDevCfg->idleTimeout > 0 )
  {
    osal_start_timerEx( hidDevTaskId, HID_IDLE_EVT, pHidDevCfg->idleTimeout );
  }
}

/*********************************************************************
 * @fn      hidDevStopIdleTimer
 *
 * @brief   Stop the idle timer.
 *
 * @return  None.
 */
static void hidDevStopIdleTimer( void )
{
  osal_stop_timerEx( hidDevTaskId, HID_IDLE_EVT );
}

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