/*************************************************************************************************/
/*!
 *  \file   uribeacon_main.c
 *
 *  \brief  UriBeacon sample application.
 *
 *          $Date: 2017-02-09 11:58:21 -0600 (Thu, 09 Feb 2017) $
 *          $Revision: 11135 $
 *
 *  Copyright (c) 2011-2017 ARM Ltd., all rights reserved.
 *  ARM Ltd. confidential and proprietary.
 *
 *  IMPORTANT.  Your use of this file is governed by a Software License Agreement
 *  ("Agreement") that must be accepted in order to download or otherwise receive a
 *  copy of this file.  You may not use or copy this file for any purpose other than
 *  as described in the Agreement.  If you do not agree to all of the terms of the
 *  Agreement do not use this file and delete all copies in your possession or control;
 *  if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
 *  to any use, copying or further distribution of this software.
 */
/*************************************************************************************************/

#include <string.h>

#include "uribeacon_api.h"
#include "uricfg_api.h"
#include "uricfg_defs.h"
#include "svc_uricfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_param.h"
#include "app_ui.h"
#include "att_defs.h"
#include "bstream.h"
#include "dm_api.h"
#include "sec_api.h"
#include "svc_core.h"
#include "svc_dis.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_types.h"

/**************************************************************************************************
  Macros
**************************************************************************************************/

/*! default lock state  */
#define URIBEACON_LOCKSTATE_DEFAULT         0

/*! default URI data value ("https://www.arm.com") */
#define URIBEACON_URIDATA_DEFAULT           0x01, 'a', 'r', 'm', 0x07

/*! clamp beacon period to allowed range */
#define URIBEACON_BEACONPERIOD_CLAMP(bp)        (((bp) < URICFG_ATT_BEACONPERIOD_MIN) ? URICFG_ATT_BEACONPERIOD_MIN : \
                                                 ((bp) > URICFG_ATT_BEACONPERIOD_MAX) ? URICFG_ATT_BEACONPERIOD_MAX : \
                                                  (bp))
/*! convert beacon period from ms to 0.625-ms units */
#define URIBEACON_BEACONPERIOD_TO_INTERVAL(bp) (((URIBEACON_BEACONPERIOD_CLAMP(bp)) * 8u) / 5u)

/*! size of advertising information */
#define URIBEACON_INFO_SIZE                 24

/*! offsets of information within advertising data */
#define URIBEACON_INFO_OFFSET               3  /*! offset of advertising information within advertising data */
#define URIBEACON_INFO_SVCDATASIZE_OFFSET   7  /*! offset of service data size */
#define URIBEACON_INFO_URIFLAGS_OFFSET      11 /*! offset of URI flags. */
#define URIBEACON_INFO_TXPWRLEVEL_OFFSET    12 /*! offset of Tx power level */
#define URIBEACON_INFO_URIDATA_OFFSET       13 /*! offset of URI data */

/*! default adv Tx power levels */
static const int8_t uriAdvertisedTxPwrLevelsDefault[] = {-80, -60, -40, -20};

/*! WSF message event starting value */
#define URIBEACON_MSG_START                 0xA0

/*! WSF message event enumeration */
enum
{
  URIBEACON_ADV_TIMER_IND = URIBEACON_MSG_START   /*! Advertising timer expired */
};

/*! Advertising timer timeout */
#define URIBEACON_ADV_TIMEOUT_SEC           30

/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/

/*! enumeration of client characteristic configuration descriptors */
enum
{
  URIBEACON_GATT_SC_CCC_IDX,                    /*! GATT service, service changed characteristic */
  URIBEACON_NUM_CCC_IDX
};

/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t uriBeaconCccSet[URIBEACON_NUM_CCC_IDX] =
{
  /* cccd handle        value range              security level */
  { GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE } /* URIBEACON_GATT_SC_CCC_IDX */
};

/**************************************************************************************************
  Global Variables
**************************************************************************************************/

/*! WSF handler ID */
static wsfHandlerId_t uriBeaconHandlerId;

/*! Advertising mode timer */
static wsfTimer_t uriBeaconAdvTimer;
static uint8_t    uriBeaconAdvType;

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

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

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

/*! configurable parameters for security */
static const appSecCfg_t uriBeaconSecCfg =
{
  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 */
  FALSE                                   /*! TRUE to initiate security upon connection */
};

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

/**************************************************************************************************
  Advertising Data
**************************************************************************************************/
static int8_t uriBeaconAdvertisedTxPwrLevels[URICFG_SIZE_TXPWRLEVELS_ATT];
static uint8_t uriBeaconTxPwrMode;
static const uint8_t uriBeaconUriData[] = {URIBEACON_URIDATA_DEFAULT};

/* advertising data, discoverable mode */
static uint8_t uriBeaconAdvDataDisc[HCI_ADV_DATA_LEN] =
{
  /* advertising type flags */
  1u + sizeof(uint8_t),
  DM_ADV_TYPE_FLAGS,
  DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_BREDR_NOT_SUP,

  /* UriBeacon service UUID */
  3u,
  DM_ADV_TYPE_16_UUID,
  UINT16_TO_BYTES(URICFG_SERVICE_UUID),

  /* UriBeacon service data */
  5u + sizeof(uriBeaconUriData), /* length (5-23) */
  DM_ADV_TYPE_SERVICE_DATA,
  UINT16_TO_BYTES(URICFG_SERVICE_UUID),
  0x10,                               /* Frame types, fixed 0x10. */
  0,                                  /* tx power level */
  URIBEACON_URIDATA_DEFAULT           /* URI */
};

/* scan data, discoverable and connectable modes */
static uint8_t uriBeaconScanDataDisc[HCI_ADV_DATA_LEN];
static uint8_t uriBeaconScanDataConn[HCI_ADV_DATA_LEN];
static const uint8_t uriBeaconScanDataLocalName[] = { 'U', 'r', 'i', 'B', 'c', 'n' };
static const uint8_t uriBeaconScanData128Uuid[] = { URICFG_UUID_BYTES(URICFG_UUID_SVC) };

static const char * const uriBeaconPrefixes[] =
{
  "http://www.", "https://www.", "http://", "https://", "urn:uuid:"
};
static const char * const uriBeaconCodes[] =
{
  ".com/", ".org/", ".edu/", ".net/", ".info/", ".biz/", ".gov/",
  ".com",  ".org",  ".edu",  ".net",  ".info",  ".biz",  ".gov",
};

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconPrintUri
 *
 *  \brief  Print URI data as URI.
 *
 *  \param  pUriData      URI data.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconPrintUri(const uint8_t *pUriData)
{
  uint8_t c;
  uint8_t len = 0u;
  char    uri[2 * URICFG_MAXSIZE_URIDATA_ATT];
  uint8_t uriOffset;

  memset(uri, 0, sizeof(uri));
  uriOffset = 0;

  c = *pUriData++;
  len++;
  if (c < sizeof(uriBeaconPrefixes) / sizeof(uriBeaconPrefixes[0]))
  {
    memcpy(uri + uriOffset, uriBeaconPrefixes[c], strlen(uriBeaconPrefixes[c]));
    uriOffset += (uint8_t) strlen(uriBeaconPrefixes[c]);
  }
  else
  {
    uri[uriOffset] = '?';
    APP_TRACE_INFO1("URI = %s", uri);
    return;
  }

  while (len < URICFG_MAXSIZE_URIDATA_ATT)
  {
    c = *pUriData++;
    if ((c > 0x20u) && (c < 0x7F))
    {
      uri[uriOffset++] = (char)c;
    }
    else if (c < sizeof(uriBeaconCodes) / sizeof(uriBeaconCodes[0]))
    {
      memcpy(uri + uriOffset, uriBeaconCodes[c], strlen(uriBeaconCodes[c]));
      uriOffset += (uint8_t) strlen(uriBeaconCodes[c]);
    }
    else
    {
      break;
    }
  }
  APP_TRACE_INFO1("URI = %s", uri);
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconSetAdvType
 *
 *  \brief  Set advertising type.
 *
 *  \param  advType       Advertising type
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconSetAdvType(uint8_t advType)
{
  uriBeaconAdvType = advType;
  AppSetAdvType(advType);
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconUpdateAdvUriData
 *
 *  \brief  Update URI data in advertising data.
 *
 *  \param  pUriData      URI data.
 *  \param  uriDataLen    URI data length.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconUpdateAdvUriData(const uint8_t *pUriData, uint8_t uriDataLen)
{
  uint16_t len;

  for (len = 0u; len < uriDataLen; len++)
  {
    if (pUriData[len] == 0xFFu)
    {
      break;
    }
  }
  memcpy(&uriBeaconAdvDataDisc[URIBEACON_INFO_URIDATA_OFFSET], pUriData, len);
  uriBeaconAdvDataDisc[URIBEACON_INFO_SVCDATASIZE_OFFSET] = 5u + len;
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconUpdateAdvUriFlags
 *
 *  \brief  Update URI flags in advertising data.
 *
 *  \param  uriFlags      URI flags.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconUpdateAdvUriFlags(uint8_t uriFlags)
{
  uriBeaconAdvDataDisc[URIBEACON_INFO_URIFLAGS_OFFSET] = uriFlags;
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconUpdateAdvTxPwrLevel
 *
 *  \brief  Update Tx power level in advertising data.
 *
 *  \param  txPwrLevel    Tx power level.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconUpdateAdvTxPwrLevel(int8_t txPwrLevel)
{
  uriBeaconAdvDataDisc[URIBEACON_INFO_TXPWRLEVEL_OFFSET] = (uint8_t)txPwrLevel;

  /* TODO: SEND VS COMMAND TO ACTUALLY CHANGE POWER LEVEL */
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconUpdateBeaconPeriod
 *
 *  \brief  Update beacon period (advertising interval).
 *
 *  \param  beaconPeriod  Beacon period in milliseconds.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconUpdateBeaconPeriod(uint16_t beaconPeriod)
{
  /* this never occurs while we're advertising, so we just need to update the datum */
  if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
  {
    uriBeaconAdvCfg.advInterval[0] = 0;
  }
  else
  {
    uriBeaconAdvCfg.advInterval[0] = URIBEACON_BEACONPERIOD_TO_INTERVAL(beaconPeriod);
  }
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconSetSystemId
 *
 *  \brief  Set the system ID.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconSetSystemId(void)
{
  uint8_t *bdaddr = HciGetBdAddr();
  uint8_t  sysId[8];

  /* formatted according to GATT specification for System ID characteristic (0x2A23) */
  sysId[0] = bdaddr[0];
  sysId[1] = bdaddr[1];
  sysId[2] = bdaddr[2];
  sysId[3] = 0xFE;
  sysId[4] = 0xFF;
  sysId[5] = bdaddr[3];
  sysId[6] = bdaddr[4];
  sysId[7] = bdaddr[5];
  AttsSetAttr(DIS_SID_HDL, sizeof(sysId), sysId);
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconAssignRandomAddress
 *
 *  \brief  Assign a random address.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconAssignRandomAddress(void)
{
  uint8_t addr[BDA_ADDR_LEN];
  SecRand(addr, BDA_ADDR_LEN);
  DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC);
  DmDevSetRandAddr(addr);
  DmAdvSetAddrType(DM_ADDR_RANDOM);
}

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

  len = DmSizeOfEvt(pDmEvt);

  if ((pMsg = WsfMsgAlloc(len)) != NULL)
  {
    memcpy(pMsg, pDmEvt, len);
    WsfMsgSend(uriBeaconHandlerId, pMsg);
  }
}

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

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

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconAttWriteCback
 *
 *  \brief  Attribute write callback.
 *
 *  \param  handle      Attribute handle.
 *  \param  valueLen    Length of value data.
 *  \param  pValue      Pointer to value data.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconAttWriteCback(uint16_t handle, uint16_t valueLen, const uint8_t *pValue)
{
  uint8_t id;
  bool_t  updatedAdvData = FALSE;
  uint8_t uriData[URICFG_MAXSIZE_URIDATA_ATT];

  switch (handle)
  {
    case URICFG_HANDLE_URIDATA:
    {
      /* pad data to maximum length so setting does not need to resize */
      memset(uriData, 0xFF, sizeof(uriData));
      memcpy(uriData, pValue, valueLen);
      pValue   = uriData;
      valueLen = URICFG_MAXSIZE_URIDATA_ATT;

      APP_TRACE_INFO0("URI upd URI data");
      uriBeaconPrintUri(uriData);
      uriBeaconUpdateAdvUriData(uriData, (uint8_t) valueLen);
      updatedAdvData = TRUE;
      id = URI_BEACON_PARAM_URI_DATA;
      break;
    }
    case URICFG_HANDLE_URIFLAGS:
    {
      APP_TRACE_INFO1("URI upd URI flags %02X", *pValue);
      uriBeaconUpdateAdvUriFlags(*pValue);
      updatedAdvData = TRUE;
      id = URI_BEACON_PARAM_URI_FLAGS;
      break;
    }
    case URICFG_HANDLE_TXPWRLEVELS:
    {
      APP_TRACE_INFO2("URI upd adv tx pwr lvls {%d, %d,", pValue[0], pValue[1]);
      APP_TRACE_INFO2("                         %d, %d}", pValue[2], pValue[3]);

      memcpy(uriBeaconAdvertisedTxPwrLevels, pValue, valueLen);
      uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
      updatedAdvData = TRUE;
      id = URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS;
      break;
    }
    case URICFG_HANDLE_TXPWRMODE:
    {
      APP_TRACE_INFO1("URI upd tx pwr mode %u", *pValue);
      uriBeaconTxPwrMode = *pValue;
      uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
      updatedAdvData = TRUE;
      id = URI_BEACON_PARAM_TX_POWER_MODE;
      break;
    }
    case URICFG_HANDLE_BEACONPERIOD:
    {
      uint16_t beaconPeriod;

      BYTES_TO_UINT16(beaconPeriod, pValue);
      APP_TRACE_INFO1("URI upd beacon period %u", beaconPeriod);
      uriBeaconUpdateBeaconPeriod(beaconPeriod);
      if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
      {
        /* do not store 'disable' beacon period */
        return;
      }
      id = URI_BEACON_PARAM_BEACON_PERIOD;
      break;
    }
    case URICFG_HANDLE_LOCK:
    {
      /* only called during reset */
      APP_TRACE_INFO0("URI upd lock");
      id = URI_BEACON_PARAM_LOCK;
      break;
    }
    default:
    {
      APP_TRACE_INFO0("URI upd unknown attr");
      return;
    }
  }

  if (AppParamWrite(id, valueLen, pValue) != valueLen)
  {
    APP_TRACE_ERR0("URI failed to wr hostcfg");
  }

  if (updatedAdvData)
  {
    AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
  }
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconLockChangeCback
 *
 *  \brief  Lock change callback.
 *
 *  \param  lockState   New lock state.
 *  \param  lock        Lock value.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconLockChangeCback(uint8_t lockState, const uint8_t lock[URICFG_SIZE_LOCK_ATT])
{
  APP_TRACE_INFO1("URI upd lock state %u", lockState);

  /* only update lock if locking */
  if (lockState)
  {
    if (AppParamWrite(URI_BEACON_PARAM_LOCK, URICFG_SIZE_LOCK_ATT, lock) != URICFG_SIZE_LOCK_ATT)
    {
      APP_TRACE_ERR0("URI failed to wr lock state");
      return;
    }
  }
  /* always update lock state */
  if (AppParamWrite(URI_BEACON_PARAM_LOCK_STATE, sizeof(lockState), &lockState) != sizeof(lockState))
  {
    APP_TRACE_ERR0("URI failed to wr lcok state");
    return;
  }
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconSetup
 *
 *  \brief  Set up advertising and other procedures that need to be performed after
 *          device reset.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconSetup(wsfMsgHdr_t *pMsg)
{
  /* set advertising and scan response data for discoverable mode */
  AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);

  /* set scan response data for discoverable mode */
  memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
  AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, 0, (uint8_t *) uriBeaconScanDataDisc);
  AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
  AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);

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

  /* set scan response data for connectable mode */
  memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
  AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, (uint8_t *) uriBeaconScanDataConn);
  AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
  AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);

  /* set system ID according to BDADDR */
  uriBeaconSetSystemId();

  /* assign a random address before we start advertising */
  uriBeaconAssignRandomAddress();

  /* start timer for advertising mode change */
  memset(&uriBeaconAdvTimer, 0, sizeof(uriBeaconAdvTimer));
  uriBeaconAdvTimer.handlerId = uriBeaconHandlerId;
  uriBeaconAdvTimer.msg.event = URIBEACON_ADV_TIMER_IND;
  WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);

  /* start advertising */
  uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
  AppAdvStart(APP_MODE_CONNECTABLE);
}

/*************************************************************************************************/
/*!
 *  \fn     uriBeaconBtnCback
 *
 *  \brief  Button press callback.
 *
 *  \param  btn    Button press.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void uriBeaconBtnCback(uint8_t btn)
{
  /* button actions when connected */
  if (AppConnIsOpen() != DM_CONN_ID_NONE)
  {
    switch (btn)
    {
      /* ignore button when connected */
      case APP_UI_BTN_1_SHORT:
        break;

      default:
        break;
    }
  }
  /* button actions when not connected */
  else
  {
    switch (btn)
    {
      case APP_UI_BTN_1_SHORT:
        /* start connectable advertising and reset timer */
        uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
        WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
        break;

      default:
        break;
    }
  }
}

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

  switch (pMsg->event)
  {
    /* revert to non-connectable advertising */
    case URIBEACON_ADV_TIMER_IND:
      uriBeaconSetAdvType(DM_ADV_NONCONN_UNDIRECT);
      break;

    case DM_ADV_START_IND:
      if (pMsg->status == HCI_SUCCESS)
      {
        uiEvent = (uriBeaconAdvType == DM_ADV_NONCONN_UNDIRECT) ? APP_UI_DISCOVERABLE : APP_UI_ADV_START;
      }
      break;

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

    case DM_RESET_CMPL_IND:
      uriBeaconSetup(pMsg);
      uiEvent = APP_UI_RESET_CMPL;
      break;

    /* stop advertising timeout */
    case DM_CONN_OPEN_IND:
      WsfTimerStop(&uriBeaconAdvTimer);
      uiEvent = APP_UI_CONN_OPEN;
      break;

    /* re-start connectable advertising, with timeout */
    case DM_CONN_CLOSE_IND:
      uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
      WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
      uiEvent = APP_UI_ADV_START;
      break;

    default:
      break;
  }

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

/*************************************************************************************************/
/*!
 *  \fn     UriBeaconHandlerInit
 *
 *  \brief  Application handler init function called during system initialization.
 *
 *  \param  handlerID  WSF handler ID.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriBeaconHandlerInit(wsfHandlerId_t handlerId)
{
  /* store handler ID */
  uriBeaconHandlerId = handlerId;

  /* set configuration pointers */
  pAppAdvCfg    = (appAdvCfg_t *)&uriBeaconAdvCfg;
  pAppSlaveCfg  = (appSlaveCfg_t *)&uriBeaconSlaveCfg;
  pAppSecCfg    = (appSecCfg_t *)&uriBeaconSecCfg;
  pAppUpdateCfg = (appUpdateCfg_t *)&uriBeaconUpdateCfg;

  /* initialize application framework */
  AppSlaveInit();
}

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

    if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
    {
      /* process advertising and connection-related messages */
      AppSlaveProcDmMsg((dmEvt_t *) pMsg);

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

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

/*************************************************************************************************/
/*!
 *  \fn     UriBeaconStart
 *
 *  \brief  Start UriBeacon application.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriBeaconStart(void)
{
  uint8_t  lockState;
  uint8_t  uriData[URICFG_MAXSIZE_URIDATA_ATT];
  uint8_t  uriDataLen = 0;
  uint8_t  uriDataReset[URICFG_MAXSIZE_URIDATA_ATT];
  uint8_t  uriFlags;
  uint16_t beaconPeriod;
  uint8_t  lock[URICFG_SIZE_LOCK_ATT];

  APP_TRACE_INFO0("URI starting app");

  /* try to load settings from NV memory */
  if (AppParamRead(URI_BEACON_PARAM_LOCK_STATE, sizeof(lockState), (uint8_t *)&lockState) == sizeof(lockState))
  {
    APP_TRACE_INFO1("URI rd lock state from param DB %u", lockState);
  }
  else
  {
    lockState = URIBEACON_LOCKSTATE_DEFAULT;
  }
  if (AppParamRead(URI_BEACON_PARAM_URI_DATA, sizeof(uriData), &uriData[0]) == sizeof(uriData))
  {
    APP_TRACE_INFO0("URI rd URI data from param DB ");
    uriBeaconPrintUri(uriData);

    uint8_t *pUriData = uriData;
    while ((*pUriData != 0xFF) && (uriDataLen < URICFG_MAXSIZE_URIDATA_ATT))
    {
      pUriData++;
      uriDataLen++;
    }
  }
  else
  {
    memcpy(uriData, uriBeaconUriData, sizeof(uriBeaconUriData));
    uriDataLen = sizeof(uriBeaconUriData);
  }

  if (AppParamRead(URI_BEACON_PARAM_URI_FLAGS, sizeof(uriFlags), (uint8_t *)&uriFlags) == sizeof(uriFlags))
  {
    APP_TRACE_INFO1("URI rd URI flags from param DB %02X", uriFlags);
  }
  else
  {
    uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
  }

  if (AppParamRead(URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS, sizeof(uriBeaconAdvertisedTxPwrLevels), (uint8_t *)&uriBeaconAdvertisedTxPwrLevels) == sizeof(uriBeaconAdvertisedTxPwrLevels))
  {
    APP_TRACE_INFO2("URI rd adv tx pwr lvls from param DB {%d, %d,", uriBeaconAdvertisedTxPwrLevels[0], uriBeaconAdvertisedTxPwrLevels[1]);
    APP_TRACE_INFO2("                                      %d, %d}", uriBeaconAdvertisedTxPwrLevels[2], uriBeaconAdvertisedTxPwrLevels[3]);
  }
  else
  {
    memcpy(uriBeaconAdvertisedTxPwrLevels, uriAdvertisedTxPwrLevelsDefault, sizeof(uriBeaconAdvertisedTxPwrLevels));
  }

  if (AppParamRead(URI_BEACON_PARAM_TX_POWER_MODE, sizeof(uriBeaconTxPwrMode), (uint8_t *)&uriBeaconTxPwrMode) == sizeof(uriBeaconTxPwrMode))
  {
    APP_TRACE_INFO1("URI rd tx pwr mode from param DB %u", uriBeaconTxPwrMode);
  }
  else
  {
    uriBeaconTxPwrMode = URICFG_ATT_TXPWRMODE_DEFAULT;
  }

  if (AppParamRead(URI_BEACON_PARAM_BEACON_PERIOD, sizeof(beaconPeriod), (uint8_t *)&beaconPeriod) == sizeof(beaconPeriod))
  {
    APP_TRACE_INFO1("URI rd bcn per from param DB %u", beaconPeriod);
  }
  else
  {
    beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
  }

  if (AppParamRead(URI_BEACON_PARAM_LOCK, sizeof(lock), &lock[0]) == sizeof(lock))
  {
    APP_TRACE_INFO0("URI rd lock from param DB");
  }
  else
  {
    memset(lock, 0x00u, sizeof(lock));
  }

  /* register for stack callbacks */
  DmRegister(uriBeaconDmCback);
  DmConnRegister(DM_CLIENT_ID_APP, uriBeaconDmCback);
  AttConnRegister(AppServerConnCback);
  AttsCccRegister(URIBEACON_NUM_CCC_IDX, (attsCccSet_t *)uriBeaconCccSet, uriBeaconCccCback);

  /* set advertising and scan response data for discoverable mode */
  uriBeaconUpdateAdvUriData(uriData, uriDataLen);
  uriBeaconUpdateAdvUriFlags(uriFlags);
  uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);

  /* register for app framework callbacks */
  AppUiBtnRegister(uriBeaconBtnCback);

  /* initialize attribute server database */
  SvcCoreAddGroup();
  SvcDisAddGroup();
  UriCfgStart(uriData, uriDataLen, uriFlags, uriBeaconAdvertisedTxPwrLevels, uriBeaconTxPwrMode, beaconPeriod);
  UriCfgAttWriteCbackRegister(uriBeaconAttWriteCback);
  UriCfgMakeLockable(lockState, lock, uriBeaconLockChangeCback);
  memset(uriDataReset, 0xFFu,            sizeof(uriDataReset));
  memcpy(uriDataReset, uriBeaconUriData, sizeof(uriBeaconUriData));
  UriCfgSetUriDataResetValue(uriDataReset);

  /* update beacon period (advertising interval) */
  uriBeaconUpdateBeaconPeriod(beaconPeriod);

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