/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief  Watch sample application for the following profiles:
 *            Time profile client
 *            Alert Notification profile client
 *            Phone Alert Status profile client
 *
 *  Copyright (c) 2011-2020 Arm Ltd. All Rights Reserved.
 *
 *  Copyright (c) 2019-2020 Packetcraft, Inc.
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/*************************************************************************************************/

#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_main.h"
#include "svc_core.h"
#include "svc_ch.h"
#include "gatt/gatt_api.h"
#include "tipc/tipc_api.h"
#include "anpc/anpc_api.h"
#include "paspc/paspc_api.h"
#include "hrpc/hrpc_api.h"
#include "dis/dis_api.h"
#include "atts_main.h"

#ifdef AM_BLE_EATT
#include "att_eatt.h"
#include "eatt_api.h"
#endif

/**************************************************************************************************
  Local Variables
**************************************************************************************************/
typedef enum
{
    WATCH_SCAN_NONE,
    WATCH_SCAN_START,
    WATCH_SCANNING
}eWatchScanState;
/*! application control block */
static struct
{
  uint16_t          hdlMasterList[APP_DB_HDL_LIST_LEN];   /*! Cached handle list in master role */
  uint16_t          hdlSlaveList[APP_DB_HDL_LIST_LEN];    /*! Cached handle list in slave role */
  wsfHandlerId_t    handlerId;                            /*! WSF hander ID */
  uint8_t           discState;                            /*! Service discovery state */
  eWatchScanState   scan_state;
  bool_t            autoConnect;                          /*! TRUE if auto-connecting */
} watchCb;

/*! \brief application connection information */
typedef struct {
  appDbHdl_t        dbHdl;                                /*! Device database record handle type */
  uint8_t           addrType;                             /*! Type of address of device to connect to */
  bdAddr_t          addr;                                 /*! Address of device to connect to */
  bool_t            doConnect;                            /*! TRUE to issue connect on scan complete */
  int8_t            rssi;                                 /*! RSSI of discovered remote device  */
} watchConnInfo_t;

watchConnInfo_t watchConnInfo;

/**************************************************************************************************
  Configurable Parameters
**************************************************************************************************/

/*! configurable parameters for advertising */
static const appAdvCfg_t watchAdvCfg =
{
  {    0,     0,     0},                  /*! Advertising durations in ms */
  {   160,   800,     0}                   /*! Advertising intervals in 0.625 ms units */
};

/*! configurable parameters for slave */
static const appSlaveCfg_t watchSlaveCfg =
{
  1,                                      /*! Maximum connections */
};

/*! configurable parameters for security */
static const appSecCfg_t watchSecCfg =
{
  DM_AUTH_BOND_FLAG,                      /*! Authentication and bonding flags */
  0,                                      /*! Initiator key distribution flags */
  DM_KEY_DIST_LTK,                        /*! Responder key distribution flags */
  FALSE,                                  /*! TRUE if Out-of-band pairing data is present */
  TRUE                                    /*! TRUE to initiate security upon connection */
};

/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t watchUpdateCfg =
{
  0,                                      /*! Connection idle period in ms before attempting
                                              connection parameter update; set to zero to disable */
  600,                                    /*! Minimum connection interval in 1.25ms units */
  800,                                    /*! Maximum connection interval in 1.25ms units */
  3,                                      /*! Connection latency */
  600,                                    /*! Supervision timeout in 10ms units */
  5                                       /*! Number of update attempts before giving up */
};

/*! Connection parameters */
static const hciConnSpec_t watchConnCfg =
{
  160,                                     /*! Minimum connection interval in 1.25ms units */
  160,                                     /*! Maximum connection interval in 1.25ms units */
  0,                                      /*! Connection latency */
  600,                                    /*! Supervision timeout in 10ms units */
  0,                                      /*! Unused */
  0                                       /*! Unused */
};

/*! SMP security parameter configuration */
/* Configuration structure */
static const smpCfg_t watchSmpCfg =
{
  3000,                                   /*! 'Repeated attempts' timeout in msec */
  SMP_IO_NO_IN_NO_OUT,                    /*! I/O Capability */
  7,                                      /*! Minimum encryption key length */
  16,                                     /*! Maximum encryption key length */
  3,                                      /*! Attempts to trigger 'repeated attempts' timeout */
  0,                                      /*! Device authentication requirements */
  64000,                                  /*! Maximum repeated attempts timeout in msec */
  64000,                                  /*! Time msec before attemptExp decreases */
  2                                       /*! Repeated attempts multiplier exponent */
};

#ifdef AM_BLE_EATT
/* EATT Configuration structure */
static const eattCfg_t watcheattCfg =
{
  247,                              /* MTU */
  247,                              /* MPS */
  TRUE,                             /* Open EATT channels automatically on connect */
  FALSE,                            /* Authorization required */
  DM_SEC_LEVEL_NONE,                /* Security level required */
  2,                                /* Number of enhanced l2cap channels per connection */
  NULL                              /* Channel priority table */
};
#endif

/*! Configurable parameters for service and characteristic discovery */
static const appDiscCfg_t watchDiscCfg =
{
  FALSE,                                  /*! TRUE to wait for a secure connection before initiating discovery */
  FALSE                                   /*! TRUE to fall back on database hash to verify handles when no bond exists. */
};

static const appCfg_t watchAppCfg =
{
  TRUE,                                   /*! TRUE to abort service discovery if service not found */
  TRUE                                    /*! TRUE to disconnect if ATT transaction times out */
};

/*! configurable parameters for master */
static const appMasterCfg_t watchMasterCfg =
{
  96,                                      /*! The scan interval, in 0.625 ms units */
  48,                                      /*! The scan window, in 0.625 ms units  */
  4000,                                    /*! The scan duration in ms */
  DM_DISC_MODE_NONE,                       /*! The GAP discovery mode */
  DM_SCAN_TYPE_ACTIVE                      /*! The scan type (active or passive) */
};

/**************************************************************************************************
  Advertising Data
**************************************************************************************************/

/*! advertising data, discoverable mode */
static const uint8_t watchAdvDataDisc[] =
{
  /*! flags */
  2,                                      /*! length */
  DM_ADV_TYPE_FLAGS,                      /*! AD type */
  DM_FLAG_LE_LIMITED_DISC |               /*! flags */
  DM_FLAG_LE_BREDR_NOT_SUP,

  /*! tx power */
  2,                                      /*! length */
  DM_ADV_TYPE_TX_POWER,                   /*! AD type */
  0,                                      /*! tx power */

  /*! manufacturer specific data */
  3,                                      /*! length */
  DM_ADV_TYPE_MANUFACTURER,               /*! AD type */
  UINT16_TO_BYTES(HCI_ID_PACKETCRAFT),    /*! company ID */

  /*! service solicitation UUID list */
  7,                                      /*! length */
  DM_ADV_TYPE_16_SOLICIT,                 /*! AD type */
  UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME_SERVICE),
  UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_SERVICE),
  UINT16_TO_BYTES(ATT_UUID_PHONE_ALERT_SERVICE)
};

/*! scan data, discoverable mode */
static const uint8_t watchScanDataDisc[] =
{
  /*! device name */
  6,                                      /*! length */
  DM_ADV_TYPE_LOCAL_NAME,                 /*! AD type */
  'W',
  'a',
  't',
  'c',
  'h'
};

/**************************************************************************************************
  ATT Client Discovery Data
**************************************************************************************************/

/*! Discovery states:  enumeration of services to be discovered */
enum
{
  WATCH_DISC_SLAVE_GATT_SVC,      /* GATT service */
  WATCH_DISC_SLAVE_CTS_SVC,       /* Current Time service */
  WATCH_DISC_SLAVE_ANS_SVC,       /* Alert Notification service */
  WATCH_DISC_SLAVE_PASS_SVC,      /* Phone Alert Status service */
  WATCH_DISC_SLAVE_SVC_MAX        /* Discovery complete */
};

/*! Discovery states:  enumeration of services to be discovered in the master role */
enum
{
  WATCH_DISC_MASTER_GATT_SVC,      /* GATT service */
  WATCH_DISC_MASTER_DIS_SVC,       /* Device Information service */
  WATCH_DISC_MASTER_HRS_SVC,       /* Heart Rate service */
};

/*! the Client handle list, watchCb.hdlList[], is set as follows:
 *
 *  ------------------------------- <- WATCH_DISC_GATT_START
 *  | GATT handles                |
 *  |...                          |
 *  ------------------------------- <- WATCH_DISC_CTS_START
 *  | CTS handles                 |
 *  | ...                         |
 *  ------------------------------- <- WATCH_DISC_ANS_START
 *  | ANS handles                 |
 *  | ...                         |
 *  ------------------------------- <- WATCH_DISC_PASS_START
 *  | PASS handles                |
 *  | ...                         |
 *  -------------------------------
 */

/*! Start of each service's handles in the the handle list */
#define WATCH_DISC_GATT_START           0
#define WATCH_DISC_CTS_START            (WATCH_DISC_GATT_START + GATT_HDL_LIST_LEN)
#define WATCH_DISC_ANS_START            (WATCH_DISC_CTS_START + TIPC_CTS_HDL_LIST_LEN)
#define WATCH_DISC_PASS_START           (WATCH_DISC_ANS_START + ANPC_ANS_HDL_LIST_LEN)
#define WATCH_DISC_SLAVE_HDL_LIST_LEN   (WATCH_DISC_PASS_START + PASPC_PASS_HDL_LIST_LEN)

/*! Pointers into handle list for each service's handles */
static uint16_t *pWatchSlvGattHdlList = &watchCb.hdlSlaveList[WATCH_DISC_GATT_START];
static uint16_t *pWatchCtsHdlList =     &watchCb.hdlSlaveList[WATCH_DISC_CTS_START];
static uint16_t *pWatchAnsHdlList =     &watchCb.hdlSlaveList[WATCH_DISC_ANS_START];
static uint16_t *pWatchPassHdlList =    &watchCb.hdlSlaveList[WATCH_DISC_PASS_START];

/* Start of cached heart rate service handles; begins after GATT - Master Role */
#define WATCH_DISC_DIS_START            (WATCH_DISC_GATT_START + GATT_HDL_LIST_LEN)
#define WATCH_DISC_HRS_START            (WATCH_DISC_DIS_START + DIS_HDL_LIST_LEN)
#define WATCH_DISC_MASTER_HDL_LIST_LEN  (WATCH_DISC_HRS_START + HRPC_HRS_HDL_LIST_LEN)

/*! Pointers into handle list heart rate service handles - Master Role */
static uint16_t *pWatchMstGattHdlList = &watchCb.hdlMasterList[WATCH_DISC_GATT_START];
static uint16_t *pWatchDisHdlList =     &watchCb.hdlMasterList[WATCH_DISC_DIS_START];
static uint16_t *pWatchHrsHdlList =     &watchCb.hdlMasterList[WATCH_DISC_HRS_START];

/* sanity check:  make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(WATCH_DISC_SLAVE_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
WSF_CT_ASSERT(WATCH_DISC_MASTER_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);

/**************************************************************************************************
  ATT Client Configuration Data
**************************************************************************************************/

/*
 * Data for configuration after service discovery
 */

/* Default value for CCC indications */
static const uint8_t watchCccIndVal[] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_INDICATE)};

/* Default value for CCC notifications */
static const uint8_t watchCccNtfVal[] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_NOTIFY)};

/* Default value for Client Supported Features (enable Robust Caching and Enhanced ATT Bearer) */
static const uint8_t watchCsfVal[1] = { ATTS_CSF_ROBUST_CACHING | ATTS_CSF_EATT_BEARER };

/* ANS Control point value for "Enable New Alert Notification" */
static const uint8_t watchAncpEnNewVal[] = {CH_ANCP_ENABLE_NEW, CH_ALERT_CAT_ID_ALL};

/* ANS Control point value for "Notify New Alert Immediately" */
static const uint8_t watchAncpNotNewVal[] = {CH_ANCP_NOTIFY_NEW, CH_ALERT_CAT_ID_ALL};

/* ANS Control point value for "Enable Unread Alert Status Notification" */
static const uint8_t watchAncpEnUnrVal[] = {CH_ANCP_ENABLE_UNREAD, CH_ALERT_CAT_ID_ALL};

/* ANS Control point value for "Notify Unread Alert Status Immediately" */
static const uint8_t watchAncpNotUnrVal[] = {CH_ANCP_NOTIFY_UNREAD, CH_ALERT_CAT_ID_ALL};

/* HRS Control point "Reset Energy Expended" */
static const uint8_t watchHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP};

/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t watchDiscSlaveCfgList[] =
{
  /* Read:  CTS Current time */
  {NULL, 0, (TIPC_CTS_CT_HDL_IDX + WATCH_DISC_CTS_START)},

  /* Read:  CTS Local time information */
  {NULL, 0, (TIPC_CTS_LTI_HDL_IDX + WATCH_DISC_CTS_START)},

  /* Read:  CTS Reference time information */
  {NULL, 0, (TIPC_CTS_RTI_HDL_IDX + WATCH_DISC_CTS_START)},

  /* Read:  ANS Supported new alert category */
  {NULL, 0, (ANPC_ANS_SNAC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Read:  ANS Supported unread alert category */
  {NULL, 0, (ANPC_ANS_SUAC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Read:  PASS Alert status */
  {NULL, 0, (PASPC_PASS_AS_HDL_IDX + WATCH_DISC_PASS_START)},

  /* Read:  PASS Ringer setting */
  {NULL, 0, (PASPC_PASS_RS_HDL_IDX + WATCH_DISC_PASS_START)},

  /* Write:  GATT service changed ccc descriptor */
  {watchCccIndVal, sizeof(watchCccIndVal), (GATT_SC_CCC_HDL_IDX + WATCH_DISC_GATT_START)},

  /* Write:  GATT client supported features */
  {watchCsfVal, sizeof(watchCsfVal), (GATT_CSF_HDL_IDX + WATCH_DISC_GATT_START) },

  /* Write:  CTS Current time ccc descriptor */
  {watchCccNtfVal, sizeof(watchCccNtfVal), (TIPC_CTS_CT_CCC_HDL_IDX + WATCH_DISC_CTS_START)},

  /* Write:  ANS New alert ccc descriptor */
  {watchCccNtfVal, sizeof(watchCccNtfVal), (ANPC_ANS_NA_CCC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Unread alert status ccc descriptor */
  {watchCccNtfVal, sizeof(watchCccNtfVal), (ANPC_ANS_UAS_CCC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  PASS Alert status ccc descriptor */
  {watchCccNtfVal, sizeof(watchCccNtfVal), (PASPC_PASS_AS_CCC_HDL_IDX + WATCH_DISC_PASS_START)},

  /* Write:  PASS Ringer setting ccc descriptor */
  {watchCccNtfVal, sizeof(watchCccNtfVal), (PASPC_PASS_RS_CCC_HDL_IDX + WATCH_DISC_PASS_START)},

  /* Write:  ANS Control point "Enable New Alert Notification" */
  {watchAncpEnNewVal, sizeof(watchAncpEnNewVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Notify New Alert Immediately" */
  {watchAncpNotNewVal, sizeof(watchAncpNotNewVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Enable Unread Alert Status Notification" */
  {watchAncpEnUnrVal, sizeof(watchAncpEnUnrVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Notify Unread Alert Status Immediately" */
  {watchAncpNotUnrVal, sizeof(watchAncpNotUnrVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)}
};

/* Characteristic configuration list length */
#define WATCH_DISC_SLAVE_CFG_LIST_LEN   (sizeof(watchDiscSlaveCfgList) / sizeof(attcDiscCfg_t))

/* sanity check:  make sure configuration list length is <= handle list length */
WSF_CT_ASSERT(WATCH_DISC_SLAVE_CFG_LIST_LEN <= WATCH_DISC_SLAVE_HDL_LIST_LEN);

/* List of characteristics to configure after service discovery - Master Role */
static const attcDiscCfg_t watchDiscMasterCfgList[] =
{
  /* Write:  GATT client supported features */
  { watchCsfVal, sizeof(watchCsfVal), (GATT_CSF_HDL_IDX + WATCH_DISC_GATT_START) },

  /* Read:  DIS Manufacturer name string */
  {NULL, 0, DIS_MFNS_HDL_IDX},

  /* Read:  DIS Model number string */
  {NULL, 0, DIS_MNS_HDL_IDX},

  /* Read:  DIS Serial number string */
  {NULL, 0, DIS_SNS_HDL_IDX},

  /* Read:  DIS Hardware revision string */
  {NULL, 0, DIS_HRS_HDL_IDX},

  /* Read:  DIS Firmware revision string */
  {NULL, 0, DIS_FRS_HDL_IDX},

  /* Read:  DIS Software revision string */
  {NULL, 0, DIS_SRS_HDL_IDX},

  /* Read:  DIS System ID */
  {NULL, 0, DIS_SID_HDL_IDX},

  /* Read:  DIS Registration certificate data */
  {NULL, 0, DIS_RCD_HDL_IDX},

  /* Read:  DIS PnP ID */
  {NULL, 0, DIS_PNP_ID_HDL_IDX},

  /* Read:  HRS Body sensor location */
  {NULL, 0, HRPC_HRS_BSL_HDL_IDX},

  /* Write:  HRS Control point "Reset Energy Expended"  */
  {watchHrsRstEnExp, sizeof(watchHrsRstEnExp), HRPC_HRS_HRCP_HDL_IDX + WATCH_DISC_HRS_START },

  /* Write:  HRS Heart rate measurement CCC descriptor  */
  {watchCccNtfVal, sizeof(watchCccNtfVal), HRPC_HRS_HRM_CCC_HDL_IDX + WATCH_DISC_HRS_START },
};

/* Characteristic configuration list length */
#define WATCH_DISC_MASTER_CFG_LIST_LEN   (sizeof(watchDiscMasterCfgList) / sizeof(attcDiscCfg_t))

/* sanity check:  make sure configuration list length is <= handle list length */
WSF_CT_ASSERT(WATCH_DISC_MASTER_CFG_LIST_LEN <= WATCH_DISC_MASTER_CFG_LIST_LEN);

/*
 * Data for configuration on connection setup
 */

/* List of characteristics to configure on connection setup */
static const attcDiscCfg_t watchDiscConnCfgList[] =
{
  /* Read:  ANS Supported new alert category */
  {NULL, 0, (ANPC_ANS_SNAC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Read:  ANS Supported unread alert category */
  {NULL, 0, (ANPC_ANS_SUAC_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Read:  PASS Alert status */
  {NULL, 0, (PASPC_PASS_AS_HDL_IDX + WATCH_DISC_PASS_START)},

  /* Write:  ANS Control point "Enable New Alert Notification" */
  {watchAncpEnNewVal, sizeof(watchAncpEnNewVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Notify New Alert Immediately" */
  {watchAncpNotNewVal, sizeof(watchAncpNotNewVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Enable Unread Alert Status Notification" */
  {watchAncpEnUnrVal, sizeof(watchAncpEnUnrVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)},

  /* Write:  ANS Control point "Notify Unread Alert Status Immediately" */
  {watchAncpNotUnrVal, sizeof(watchAncpNotUnrVal), (ANPC_ANS_ANCP_HDL_IDX + WATCH_DISC_ANS_START)}
};

/* Characteristic configuration list length */
#define WATCH_DISC_CONN_CFG_LIST_LEN   (sizeof(watchDiscConnCfgList) / sizeof(attcDiscCfg_t))

/* sanity check:  make sure configuration list length is <= handle list length */
WSF_CT_ASSERT(WATCH_DISC_CONN_CFG_LIST_LEN <= WATCH_DISC_SLAVE_HDL_LIST_LEN);

/**************************************************************************************************
  ATT Server Data
**************************************************************************************************/

/*! enumeration of client characteristic configuration descriptors used in local ATT server */
enum
{
  WATCH_GATT_SC_CCC_IDX,        /*! GATT service, service changed characteristic */
  WATCH_NUM_CCC_IDX             /*! Number of ccc's */
};

/*! client characteristic configuration descriptors settings, indexed by ccc enumeration */
static const attsCccSet_t watchCccSet[WATCH_NUM_CCC_IDX] =
{
  /* cccd handle         value range                 security level */
  {GATT_SC_CH_CCC_HDL,   ATT_CLIENT_CFG_INDICATE,    DM_SEC_LEVEL_ENC}    /* WATCH_GATT_SC_CCC_IDX */
};

/*************************************************************************************************/
/*!
 *  \brief  Application DM callback.
 *
 *  \param  pDmEvt  DM callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchDmCback(dmEvt_t *pDmEvt)
{
  dmEvt_t *pMsg;
  uint16_t  len;
  uint16_t  reportLen;

  len = DmSizeOfEvt(pDmEvt);

  if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
  {
    reportLen = pDmEvt->scanReport.len;
  }
  else
  {
    reportLen = 0;
  }

  if ((pMsg = WsfMsgAlloc(len + reportLen)) != NULL)
  {
    memcpy(pMsg, pDmEvt, len);

    if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
    {
      pMsg->scanReport.pData = (uint8_t *) ((uint8_t *) pMsg + len);
      memcpy(pMsg->scanReport.pData, pDmEvt->scanReport.pData, reportLen);
    }

    WsfMsgSend(watchCb.handlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Application  ATT callback.
 *
 *  \param  pEvt    ATT callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchAttCback(attEvt_t *pEvt)
{
  attEvt_t *pMsg;

  if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
  {
    memcpy(pMsg, pEvt, sizeof(attEvt_t));
    pMsg->pValue = (uint8_t *) (pMsg + 1);
    memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
    WsfMsgSend(watchCb.handlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Perform actions on scan start.
 *
 *  \param  pMsg    Pointer to DM callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchScanStart(dmEvt_t *pMsg)
{
  if (pMsg->hdr.status == HCI_SUCCESS)
  {
    watchCb.scan_state = WATCH_SCANNING;
    // set it to lowest possible value of RSSI
    watchConnInfo.rssi = -128;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Perform actions on scan stop.
 *
 *  \param  pMsg    Pointer to DM callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchScanStop(dmEvt_t *pMsg)
{
  if (pMsg->hdr.status == HCI_SUCCESS)
  {
    watchCb.scan_state = WATCH_SCAN_NONE;
    watchCb.autoConnect = FALSE;

    /* Open connection */
    if (watchConnInfo.doConnect)
    {
      AppConnOpen(watchConnInfo.addrType, watchConnInfo.addr, watchConnInfo.dbHdl);
      watchConnInfo.doConnect = FALSE;
    }
    else
    {
        AppAdvStart(APP_MODE_AUTO_INIT);
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Handle a scan report.
 *
 *  \param  pMsg    Pointer to DM callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchScanReport(dmEvt_t *pMsg)
{
  uint8_t  device_name[32] = {0};
  uint32_t name_length;
  uint8_t *pData;
  appDbHdl_t dbHdl;
  bool_t  connect = FALSE;

  /* disregard if not scanning or autoconnecting */
  if((watchCb.scan_state == WATCH_SCAN_NONE) || !watchCb.autoConnect)
  {
    return;
  }

#if 1   // Ambiq: Avoid "assignment in condition" warning
  pData  = DmFindAdType(DM_ADV_TYPE_LOCAL_NAME, pMsg->scanReport.len,
                        pMsg->scanReport.pData);
  if ( pData )
  {
    name_length = *pData;
    name_length--;

    memcpy(device_name, pData + 2, name_length);
  }
  else
  {
    pData = DmFindAdType(DM_ADV_TYPE_SHORT_NAME, pMsg->scanReport.len,
                          pMsg->scanReport.pData);
    if ( pData )
    {
      name_length = *pData;
      name_length--;

      memcpy(device_name, pData + 2, name_length);
    }
  }
#else
  if ((pData = DmFindAdType(DM_ADV_TYPE_LOCAL_NAME, pMsg->scanReport.len,
                   pMsg->scanReport.pData)))
  {
    name_length = *pData;
    name_length--;

    memcpy(device_name, pData + 2, name_length);
  }
  else if ((pData = DmFindAdType(DM_ADV_TYPE_SHORT_NAME, pMsg->scanReport.len,
                   pMsg->scanReport.pData)))
  {
    name_length = *pData;
    name_length--;

    memcpy(device_name, pData + 2, name_length);

  }
#endif

  WsfTrace("Found: 0x%02x:%02x:%02x:%02x:%02x:%02x rssi: %d %s",
    pMsg->scanReport.addr[5],
    pMsg->scanReport.addr[4],
    pMsg->scanReport.addr[3],
    pMsg->scanReport.addr[2],
    pMsg->scanReport.addr[1],
    pMsg->scanReport.addr[0],
    pMsg->scanReport.rssi,
    device_name
    );

  /* if we already have a bond with this device then connect to it */
  if ((dbHdl = AppDbFindByAddr(pMsg->scanReport.addrType, pMsg->scanReport.addr)) != APP_DB_HDL_NONE)
  {
    /* if this is a directed advertisement where the initiator address is an RPA */
    if (DM_RAND_ADDR_RPA(pMsg->scanReport.directAddr, pMsg->scanReport.directAddrType))
    {
      /* resolve direct address to see if it's addressed to us */
      AppMasterResolveAddr(pMsg, dbHdl, APP_RESOLVE_DIRECT_RPA);
    }
    else
    {
      connect = TRUE;
    }
  }
  /* if the peer device uses an RPA */
  else if (DM_RAND_ADDR_RPA(pMsg->scanReport.addr, pMsg->scanReport.addrType))
  {
    /* resolve advertiser's RPA to see if we already have a bond with this device */
    AppMasterResolveAddr(pMsg, APP_DB_HDL_NONE, APP_RESOLVE_ADV_RPA);
  }
  else
  {
    /* find Service UUID list; if full list not found search for partial */
    if ((pData = DmFindAdType(DM_ADV_TYPE_16_UUID, pMsg->scanReport.len,
                              pMsg->scanReport.pData)) == NULL)
    {
      pData = DmFindAdType(DM_ADV_TYPE_16_UUID_PART, pMsg->scanReport.len,
                           pMsg->scanReport.pData);
    }

    /* if found and length checks out ok */
    if (pData != NULL && pData[DM_AD_LEN_IDX] >= (ATT_16_UUID_LEN + 1))
    {
      uint8_t len = pData[DM_AD_LEN_IDX] - 1;
      pData += DM_AD_DATA_IDX;

      while ((!connect) && (len >= ATT_16_UUID_LEN))
      {
        /* Connect if heart rate service is included */
        if (BYTES_UINT16_CMP(pData, ATT_UUID_HEART_RATE_SERVICE))
        {
          connect = TRUE;
          break;
        }

        pData += ATT_16_UUID_LEN;
        len -= ATT_16_UUID_LEN;
      }
    }
  }

  if (connect)
  {

    if (pMsg->scanReport.rssi > watchConnInfo.rssi)
    {
      watchConnInfo.rssi = pMsg->scanReport.rssi;

      /* connect will start after scanning stops with expiration
       * of scan timer.
       */

      /* Store peer information for connect on scan stop */
      watchConnInfo.addrType = DmHostAddrType(pMsg->scanReport.addrType);
      memcpy(watchConnInfo.addr, pMsg->scanReport.addr, sizeof(bdAddr_t));
      watchConnInfo.dbHdl = dbHdl;
      watchConnInfo.doConnect = TRUE;
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Application ATTS client characteristic configuration callback.
 *
 *  \param  pDmEvt  DM callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchCccCback(attsCccEvt_t *pEvt)
{
  attsCccEvt_t  *pMsg;
  appDbHdl_t    dbHdl;

  /* If CCC not set from initialization and there's a device record and currently bonded */
  if ((pEvt->handle != ATT_HANDLE_NONE) &&
      ((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
      AppCheckBonded((dmConnId_t)pEvt->hdr.param))
  {
    /* Store value in device database. */
    AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
  }

  if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
  {
    memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
    WsfMsgSend(watchCb.handlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Perform UI actions on connection open.
 *
 *  \param  pMsg    Pointer to DM callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchOpen(dmEvt_t *pMsg)
{
  if (pMsg->connOpen.role == DM_ROLE_SLAVE)
  {
    /* if not bonded send a security request on connection open; devices must pair before
     * service discovery will be initiated
     */
    if (AppDbCheckBonded() == FALSE)
    {
      DmSecSlaveReq((dmConnId_t) pMsg->hdr.param, pAppSecCfg->auth);
    }
  }
  else {

    AppAdvStart(APP_MODE_AUTO_INIT);

  }
}

/*************************************************************************************************/
/*!
 *  \brief  Set up advertising and other procedures that need to be performed after
 *          device reset.
 *
 *  \param  pMsg    Pointer to DM callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchSetup(dmEvt_t *pMsg)
{
  /* set advertising and scan response data for discoverable mode */
  AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(watchAdvDataDisc), (uint8_t *) watchAdvDataDisc);
  AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(watchScanDataDisc), (uint8_t *) watchScanDataDisc);

  /* set advertising and scan response data for connectable mode */
  AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(watchAdvDataDisc), (uint8_t *) watchAdvDataDisc);
  AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(watchScanDataDisc), (uint8_t *) watchScanDataDisc);

  /* start advertising; automatically set connectable/discoverable mode and bondable mode */
  AppAdvStart(APP_MODE_AUTO_INIT);

  DmConnSetConnSpec((hciConnSpec_t *) &watchConnCfg);
}

/*************************************************************************************************/
/*!
 *  \brief  Process a received ATT read response, notification, or indication when connected in
 *          the slave role.
 *
 *  \param  pMsg    Pointer to ATT callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchSlaveValueUpdate(attEvt_t *pMsg)
{
  if (pMsg->hdr.status == ATT_SUCCESS)
  {
    /* determine which profile the handle belongs to; start with most likely */

    /* alert notification */
    if (AnpcAnsValueUpdate(pWatchAnsHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }
    /* phone alert status */
    if (PaspcPassValueUpdate(pWatchPassHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }

    /* current time */
    if (TipcCtsValueUpdate(pWatchCtsHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }

    /* GATT */
    if (GattValueUpdate(pWatchSlvGattHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Process a received ATT read response, notification, or indication when connected in
 *          the master role to a heart rate profile sensor.
 *
 *  \param  pMsg    Pointer to ATT callback event message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchMasterValueUpdate(attEvt_t *pMsg)
{
  if (pMsg->hdr.status == ATT_SUCCESS)
  {
    /* determine which profile the handle belongs to; start with most likely */

    /* heart rate */
    if (HrpcHrsValueUpdate(pWatchHrsHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }

    /* device information */
    if (DisValueUpdate(pWatchDisHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }

    /* GATT */
    if (GattValueUpdate(pWatchMstGattHdlList, pMsg) == ATT_SUCCESS)
    {
      return;
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Loop through the open connection ID list and find the first connection for the given role
 *
 *  \param  pConnIdList    Buffer with the connection IDs (must be DM_CONN_MAX bytes).
 *  \param  role           Role to look for.
 *
 *  \return None.
 */
/*************************************************************************************************/
static dmConnId_t watchGetConnIdByRole(dmConnId_t *pConnIdList, uint8_t role)
{
  uint8_t i;

  for (i = 0; i < DM_CONN_MAX; i++)
  {
    if (pConnIdList[i] != DM_CONN_ID_NONE)
    {
      if (DmConnRole(pConnIdList[i]) == role)
      {
        return pConnIdList[i];
      }
    }
  }

  return DM_CONN_ID_NONE;
}

/*************************************************************************************************/
/*!
 *  \brief  Button press callback.
 *
 *  \param  btn    Button press.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchBtnCback(uint8_t btn)
{
  dmConnId_t      slaveConnId, masterConnId;
  static uint8_t  ringer = CH_RCP_SILENT;
  dmConnId_t      connIdList[DM_CONN_MAX];

  AppConnOpenList(connIdList);

  slaveConnId = watchGetConnIdByRole(connIdList, DM_ROLE_SLAVE);
  masterConnId = watchGetConnIdByRole(connIdList, DM_ROLE_MASTER);

  APP_TRACE_INFO3("btn: %d - master conn id: %d - slave conn id: %d", btn, masterConnId, slaveConnId);

  // as long as button 1 is short pressed, we'll scan and connect to
  // a  new fit device.
  if (1)
  {
    /* No connection as a master */
    switch (btn)
    {
      case APP_UI_BTN_1_SHORT:
        /* if scanning cancel scanning */
        if(watchCb.scan_state!=WATCH_SCAN_NONE)
        {
          AppScanStop();
        }
        /* else auto connect */
        else if (!watchCb.autoConnect)
        {
          watchCb.autoConnect = TRUE;
          watchConnInfo.doConnect = FALSE;
          watchCb.scan_state = WATCH_SCAN_START;

          if (AppSlaveIsAdvertising())
          {
              AppAdvStop();
          }
          else
          {
              AppScanStart(watchMasterCfg.discMode, watchMasterCfg.scanType,
                        watchMasterCfg.scanDuration);
          }

        }
        return;

      case APP_UI_BTN_1_LONG:
        /* clear bonded device info */
        AppClearAllBondingInfo();
        return;

      default:
        break;
    }
  }

  if (slaveConnId == DM_CONN_ID_NONE)
  {
    /* No connection as a slave */
    switch (btn)
    {
      case APP_UI_BTN_2_SHORT:
        /* start or restart advertising */
        AppAdvStart(APP_MODE_AUTO_INIT);
        return;

      case APP_UI_BTN_2_MED:
        /* enter discoverable and bondable mode mode */
        AppSetBondable(TRUE);
        AppAdvStart(APP_MODE_DISCOVERABLE);
        return;

      case APP_UI_BTN_2_LONG:
        /* clear all bonding info */
        AppSlaveClearAllBondingInfo();

        /* restart advertising */
        AppAdvStart(APP_MODE_AUTO_INIT);
        return;

      default:
        break;
    }
  }
  else
  {
    /* Active connection to a slave */
    switch (btn)
    {
      case APP_UI_BTN_2_SHORT:
        /* mute ringer once */
        PaspcPassControl(slaveConnId, pWatchPassHdlList[PASPC_PASS_RCP_HDL_IDX], CH_RCP_MUTE_ONCE);
        return;

      case APP_UI_BTN_2_MED:
        /* toggle between silencing ringer and enabling ringer */
        PaspcPassControl(slaveConnId, pWatchPassHdlList[PASPC_PASS_RCP_HDL_IDX], ringer);
        ringer = (ringer == CH_RCP_SILENT) ? CH_RCP_CANCEL_SILENT : CH_RCP_SILENT;
        return;

      case APP_UI_BTN_2_LONG:
        /* disconnect slave connection */
        AppConnClose(slaveConnId);
        break;

      default:
        break;
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Discovery callback.
 *
 *  \param  connId    Connection identifier.
 *  \param  status    Service or configuration status.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchDiscCback(dmConnId_t connId, uint8_t status)
{
  switch(status)
  {
    case APP_DISC_INIT:
      /* set handle list when initialization requested */
      if (DmConnRole(connId) == DM_ROLE_MASTER)
      {
        AppDiscSetHdlList(connId, WATCH_DISC_MASTER_HDL_LIST_LEN, watchCb.hdlMasterList);
      }
      else
      {
        AppDiscSetHdlList(connId, WATCH_DISC_SLAVE_HDL_LIST_LEN, watchCb.hdlSlaveList);
      }
      break;

    case APP_DISC_READ_DATABASE_HASH:
      /* Read peer's database hash */
      AppDiscReadDatabaseHash(connId);
      break;

    case APP_DISC_SEC_REQUIRED:
      /* request security */
      if (DmConnRole(connId) == DM_ROLE_MASTER)
      {
        AppMasterSecurityReq(connId);
      }
      else
      {
        AppSlaveSecurityReq(connId);
      }
      break;

    case APP_DISC_START:
      /* initialize discovery state */
      if (DmConnRole(connId) == DM_ROLE_MASTER)
      {
        /* discover GATT service - master role */
        watchCb.discState = WATCH_DISC_MASTER_GATT_SVC;
        GattDiscover(connId, pWatchMstGattHdlList);
      }
      else
      {
        /* discover GATT service - slave role */
        watchCb.discState = WATCH_DISC_SLAVE_GATT_SVC;
        GattDiscover(connId, pWatchSlvGattHdlList);
      }

      break;

    case APP_DISC_FAILED:
    case APP_DISC_CMPL:
      /* next discovery state */
      watchCb.discState++;

      if (DmConnRole(connId) == DM_ROLE_MASTER)
      {
        if (watchCb.discState == WATCH_DISC_MASTER_DIS_SVC)
        {
          /* discover device information service */
          DisDiscover(connId, pWatchDisHdlList);
        }
        else if (watchCb.discState == WATCH_DISC_MASTER_HRS_SVC)
        {
          /* discover heart rate service */
          HrpcHrsDiscover(connId, pWatchHrsHdlList);
        }
        else
        {
          /* discovery complete */
          AppDiscComplete(connId, APP_DISC_CMPL);

          /* start configuration */
          AppDiscConfigure(connId, APP_DISC_CFG_START, WATCH_DISC_MASTER_CFG_LIST_LEN,
                           (attcDiscCfg_t *) watchDiscMasterCfgList,
                           WATCH_DISC_MASTER_HDL_LIST_LEN, watchCb.hdlMasterList);
        }
      }
      else
      {
        if (watchCb.discState == WATCH_DISC_SLAVE_CTS_SVC)
        {
          /* discover current time service */
          TipcCtsDiscover(connId, pWatchCtsHdlList);
        }
        else if (watchCb.discState == WATCH_DISC_SLAVE_ANS_SVC)
        {
          /* discover alert notification service */
          AnpcAnsDiscover(connId, pWatchAnsHdlList);
        }
        else if (watchCb.discState == WATCH_DISC_SLAVE_PASS_SVC)
        {
          /* discover phone alert status service */
          PaspcPassDiscover(connId, pWatchPassHdlList);
        }
        else
        {
          /* discovery complete */
          AppDiscComplete(connId, APP_DISC_CMPL);

          /* start configuration */
          AppDiscConfigure(connId, APP_DISC_CFG_START, WATCH_DISC_SLAVE_CFG_LIST_LEN,
                           (attcDiscCfg_t *) watchDiscSlaveCfgList,
                           WATCH_DISC_SLAVE_HDL_LIST_LEN, watchCb.hdlSlaveList);
        }
      }
      break;

    case APP_DISC_CFG_START:
      if (DmConnRole(connId) == DM_ROLE_MASTER)
      {
        /* start configuration */
        AppDiscConfigure(connId, APP_DISC_CFG_START, WATCH_DISC_MASTER_CFG_LIST_LEN,
                         (attcDiscCfg_t *) watchDiscMasterCfgList,
                         WATCH_DISC_MASTER_HDL_LIST_LEN, watchCb.hdlMasterList);
      }
      else
      {
        /* start configuration */
        AppDiscConfigure(connId, APP_DISC_CFG_START, WATCH_DISC_SLAVE_CFG_LIST_LEN,
                         (attcDiscCfg_t *) watchDiscSlaveCfgList,
                         WATCH_DISC_SLAVE_HDL_LIST_LEN, watchCb.hdlSlaveList);
      }
      break;

    case APP_DISC_CFG_CMPL:
      AppDiscComplete(connId, status);
      break;

    case APP_DISC_CFG_CONN_START:
      if (DmConnRole(connId) == DM_ROLE_SLAVE)
      {
        /* start connection setup configuration */
        AppDiscConfigure(connId, APP_DISC_CFG_CONN_START, WATCH_DISC_CONN_CFG_LIST_LEN,
                         (attcDiscCfg_t *) watchDiscConnCfgList,
                         WATCH_DISC_CONN_CFG_LIST_LEN, watchCb.hdlSlaveList);
      }
      break;

    default:
      break;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Process messages from the event handler.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void watchProcMsg(dmEvt_t *pMsg)
{
  uint8_t uiEvent = APP_UI_NONE;

  switch(pMsg->hdr.event)
  {
    case ATTC_READ_RSP:
    case ATTC_HANDLE_VALUE_NTF:
    case ATTC_HANDLE_VALUE_IND:
      if (DmConnRole((dmConnId_t) pMsg->hdr.param) == DM_ROLE_MASTER)
      {
        watchMasterValueUpdate((attEvt_t *) pMsg);
      }
      else
      {
        watchSlaveValueUpdate((attEvt_t *) pMsg);
      }
      break;

    case ATT_MTU_UPDATE_IND:
      APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
      break;

    case DM_RESET_CMPL_IND:
      // set database hash calculating status to true until a new hash is generated after reset
      attsCsfSetHashUpdateStatus(TRUE);

      // Generate ECC key if configured support secure connection,
      // else will calcualte ATT database hash
      if( watchSecCfg.auth & DM_AUTH_SC_FLAG )
      {
          DmSecGenerateEccKeyReq();
      }
      else
      {
          AttsCalculateDbHash();
      }
      uiEvent = APP_UI_RESET_CMPL;
      break;

    case ATTS_DB_HASH_CALC_CMPL_IND:
      watchSetup(pMsg);
      break;

    case DM_SEC_ECC_KEY_IND:
      DmSecSetEccKey(&pMsg->eccMsg.data.key);
      // Only calculate database hash if the calculating status is in progress
      if( attsCsfGetHashUpdateStatus() )
      {
        AttsCalculateDbHash();
      }
      break;

    case DM_ADV_START_IND:
      uiEvent = APP_UI_ADV_START;
      break;

    case DM_ADV_STOP_IND:
      if(watchCb.scan_state == WATCH_SCAN_START)
      {
          AppScanStart(watchMasterCfg.discMode, watchMasterCfg.scanType,
                      watchMasterCfg.scanDuration);
      }
      else
      {
          AppAdvStart(APP_MODE_AUTO_INIT);
          uiEvent = APP_UI_ADV_STOP;
      }
      break;

    case DM_SCAN_START_IND:
      watchScanStart(pMsg);
      uiEvent = APP_UI_SCAN_START;
      break;

    case DM_SCAN_STOP_IND:
      watchScanStop(pMsg);
      uiEvent = APP_UI_SCAN_STOP;
      break;

    case DM_SCAN_REPORT_IND:
      watchScanReport(pMsg);
      break;

    case DM_CONN_OPEN_IND:
      watchOpen(pMsg);
      uiEvent = APP_UI_CONN_OPEN;
      break;

    case DM_CONN_CLOSE_IND:
      // only when the connection we as slave is closed
      if (pMsg->connOpen.role == DM_ROLE_SLAVE)
      {
        AppAdvStart(APP_MODE_AUTO_INIT);
      }
      uiEvent = APP_UI_CONN_CLOSE;
      break;

    case DM_PHY_UPDATE_IND:
      APP_TRACE_INFO3("DM_PHY_UPDATE_IND status: %d, RX: %d, TX: %d", pMsg->phyUpdate.status,pMsg->phyUpdate.rxPhy, pMsg->phyUpdate.txPhy);
      break;

    case DM_SEC_PAIR_CMPL_IND:
      DmSecGenerateEccKeyReq();
      uiEvent = APP_UI_SEC_PAIR_CMPL;
      break;

    case DM_SEC_PAIR_FAIL_IND:
      DmSecGenerateEccKeyReq();
      uiEvent = APP_UI_SEC_PAIR_FAIL;
      break;

    case DM_SEC_ENCRYPT_IND:
      uiEvent = APP_UI_SEC_ENCRYPT;
      break;

    case DM_SEC_ENCRYPT_FAIL_IND:
      uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
      break;

    case DM_SEC_AUTH_REQ_IND:
      AppHandlePasskey(&pMsg->authReq);
      break;

    case DM_PRIV_CLEAR_RES_LIST_IND:
      APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
      break;

    case DM_VENDOR_SPEC_CMD_CMPL_IND:
      {
        #if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)

          uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];

          switch (pMsg->vendorSpecCmdCmpl.opcode)
          {
            case 0xFC20: //read at address
            {
              uint32_t read_value;

              BSTREAM_TO_UINT32(read_value, param_ptr);

              APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
                pMsg->vendorSpecCmdCmpl.opcode,
                pMsg->hdr.status,
                read_value);
            }

            break;
            default:
                APP_TRACE_INFO2("VSC 0x%0x complete status %x",
                    pMsg->vendorSpecCmdCmpl.opcode,
                    pMsg->hdr.status);
            break;
          }

        #endif
      }
      break;

    default:
      break;
  }

  if (uiEvent != APP_UI_NONE)
  {
    AppUiAction(uiEvent);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Application handler init function called during system initialization.
 *
 *  \param  handlerID  WSF handler ID.
 *
 *  \return None.
 */
/*************************************************************************************************/
void WatchHandlerInit(wsfHandlerId_t handlerId)
{
  APP_TRACE_INFO0("WatchHandlerInit");

  /* store handler ID */
  watchCb.handlerId = handlerId;

  /* Set configuration pointers */
  pAppMasterCfg = (appMasterCfg_t *) &watchMasterCfg;
  pAppSlaveCfg = (appSlaveCfg_t *) &watchSlaveCfg;
  pAppAdvCfg = (appAdvCfg_t *) &watchAdvCfg;
  pAppSecCfg = (appSecCfg_t *) &watchSecCfg;
  pAppUpdateCfg = (appUpdateCfg_t *) &watchUpdateCfg;
  pAppDiscCfg = (appDiscCfg_t *) &watchDiscCfg;
  pAppCfg = (appCfg_t *) &watchAppCfg;

  /* Set stack configuration pointers */
  pSmpCfg = (smpCfg_t *) &watchSmpCfg;
#ifdef AM_BLE_EATT
  pEattCfg = (eattCfg_t *) &watcheattCfg;
#endif

  /* Initialize application framework */
  AppMasterInit();
  AppSlaveInit();
  AppDiscInit();
}

/*************************************************************************************************/
/*!
 *  \brief  WSF event handler for application.
 *
 *  \param  event   WSF event mask.
 *  \param  pMsg    WSF message.
 *
 *  \return None.
 */
/*************************************************************************************************/
void WatchHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
  if (pMsg != NULL)
  {
    APP_TRACE_INFO1("Watch got evt %d", pMsg->event);

    /* process ATT messages */
    if (pMsg->event <= ATT_CBACK_END)
    {
      /* process discovery-related ATT messages */
      AppDiscProcAttMsg((attEvt_t *) pMsg);

      /* process server-related ATT messages */
      AppServerProcAttMsg(pMsg);
    }
    /* process DM messages */
    else if (pMsg->event <= DM_CBACK_END)
    {
      if (pMsg->param == DM_CONN_ID_NONE || DmConnRole((dmConnId_t) pMsg->param) == DM_ROLE_MASTER)
      {
        /* process advertising and connection-related messages */
        AppMasterProcDmMsg((dmEvt_t *) pMsg);

        /* process security-related messages */
        AppMasterSecProcDmMsg((dmEvt_t *) pMsg);
      }

      if (pMsg->param == DM_CONN_ID_NONE || DmConnRole((dmConnId_t) pMsg->param) == DM_ROLE_SLAVE)
      {
        /* process advertising and connection-related messages */
        AppSlaveProcDmMsg((dmEvt_t *) pMsg);

        /* process security-related messages */
        AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
      }

      /* process discovery-related messages */
      AppDiscProcDmMsg((dmEvt_t *) pMsg);
    }

    /* perform profile and user interface-related operations */
    watchProcMsg((dmEvt_t *) pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Start the application.
 *
 *  \return None.
 */
/*************************************************************************************************/
void WatchStart(void)
{
  /* EATT initialization */
#ifdef AM_BLE_EATT
  EattInit(EATT_ROLE_INITIATOR | EATT_ROLE_ACCEPTOR);
#endif

  /* Register for stack callbacks */
  DmRegister(watchDmCback);
  DmConnRegister(DM_CLIENT_ID_APP, watchDmCback);
  AttRegister(watchAttCback);
  AttConnRegister(AppServerConnCback);
  AttsCccRegister(WATCH_NUM_CCC_IDX, (attsCccSet_t *) watchCccSet, watchCccCback);

  /* Register for app framework button callbacks */
  AppUiBtnRegister(watchBtnCback);

  /* Register for app framework discovery callbacks */
  AppDiscRegister(watchDiscCback);

  /* Initialize attribute server database */
  SvcCoreAddGroup();

  /* Reset the device */
  DmDevReset();
}
