/*************************************************************************************************/
/*!
 *  \file   uricfg_main.c
 *
 *  \brief  UriBeacon configuration profile.
 *
 *          $Date: 2016-12-28 16:12:14 -0600 (Wed, 28 Dec 2016) $
 *          $Revision: 10805 $
 *
 *  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 "uricfg_api.h"
#include "app_api.h"
#include "att_api.h"
#include "wsf_trace.h"
#include "bstream.h"
#include "uricfg_defs.h"

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

/*! \brief      callback when attributes are written */
static uriCfgAttWriteCback_t uriCfgAttWriteCback;

/*! \brief      callback when lock status changes */
static uriCfgLockChangeCback_t uriCfgLockChangeCback;

/*! \brief      reset value of URI data */
static uint8_t uriCfgDataResetValue[URICFG_MAXSIZE_URIDATA_ATT];

/*************************************************************************************************/
/*!
 *  \fn     uriCfgBeaconWriteCback
 *
 *  \brief  ATTS write callback for UriBeacon service.
 *
 *  \return ATT status.
 */
/*************************************************************************************************/
static uint8_t uriCfgBeaconWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
  uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
  uint8_t  lockState = 0;
  uint8_t *pLockState = NULL;
  uint16_t attLen;

  if (AttsGetAttr(URICFG_HANDLE_LOCKSTATE, &attLen, &pLockState) == ATT_SUCCESS)
  {
    lockState = *pLockState;
  }

  switch (handle)
  {
    case URICFG_HANDLE_LOCK:
    {
      if (uriCfgLockChangeCback != NULL)
      {
        if (!lockState)
        {
          APP_TRACE_INFO0("URI profile locking");
          lockState = 1u;
          AttsSetAttr(URICFG_HANDLE_LOCK,      URICFG_SIZE_LOCK_ATT, pValue);
          AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState),    &lockState);
          uriCfgLockChangeCback(lockState, pValue);
        }
        else
        {
          APP_TRACE_INFO0("URI profile already locked");
          return ATT_ERR_AUTHOR;
        }
      }
      break;
    }

    case URICFG_HANDLE_UNLOCK:
    {
      if (uriCfgLockChangeCback != NULL)
      {
        uint8_t *pLock = NULL;

        if (lockState)
        {
          if (AttsGetAttr(URICFG_HANDLE_LOCK, &attLen, &pLock) != ATT_SUCCESS)
          {
            APP_TRACE_INFO0("URI profile failed to get lock value");
            return ATT_ERR_AUTHOR;
          }
          if (memcmp(pLock, pValue, len) != 0)
          {
            APP_TRACE_INFO0("URI profile lock mismatch");
            return ATT_ERR_AUTHOR;
          }
          APP_TRACE_INFO0("URI profile unlocking");
          lockState = 0u;
          AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState), &lockState);
          uriCfgLockChangeCback(lockState, pValue);
        }
        else
        {
          APP_TRACE_INFO0("URI profile already unlocked");
        }
      }
      break;
    }

    case URICFG_HANDLE_URIDATA:
    {
      /* check lock */
      if (lockState)
      {
        return ATT_ERR_AUTHOR;
      }

      /* save value */
      AttsSetAttr(handle, len, pValue);
      if (uriCfgAttWriteCback != NULL)
      {
        uriCfgAttWriteCback(handle, len, pValue);
      }
      break;
    }

    case URICFG_HANDLE_URIFLAGS:
    case URICFG_HANDLE_TXPWRLEVELS:
    {
      /* check lock */
      if (lockState)
      {
        return ATT_ERR_AUTHOR;
      }

      /* save value */
      AttsSetAttr(handle, len, pValue);
      if (uriCfgAttWriteCback != NULL)
      {
        uriCfgAttWriteCback(handle, len, pValue);
      }
      break;
    }

    case URICFG_HANDLE_TXPWRMODE:
    {
      uint8_t txPwrMode;

      /* check lock */
      if (lockState)
      {
        return ATT_ERR_AUTHOR;
      }

      /* check value */
      txPwrMode = (uint8_t)*pValue;
      if (txPwrMode > URICFG_ATT_TXPWRMODE_HIGH)
      {
        return ATT_ERR_WRITE;
      }

      /* save value */
      AttsSetAttr(handle, sizeof(txPwrMode), &txPwrMode);
      if (uriCfgAttWriteCback != NULL)
      {
        uriCfgAttWriteCback(handle, sizeof(txPwrMode), &txPwrMode);
      }
      break;
    }

    case URICFG_HANDLE_BEACONPERIOD:
    {
      uint16_t beaconPeriod;

      if (lockState)
      {
        return ATT_ERR_AUTHOR;
      }

      /* check value */
      BYTES_TO_UINT16(beaconPeriod, pValue);

      /* save value */
      if (beaconPeriod != URICFG_ATT_BEACONPERIOD_DISABLE)
      {
        if (beaconPeriod < URICFG_ATT_BEACONPERIOD_MIN)
        {
          beaconPeriod = URICFG_ATT_BEACONPERIOD_MIN;
        }
      }

      uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
      AttsSetAttr(handle, sizeof(beaconPeriod), beaconPeriodVal);
      if (uriCfgAttWriteCback != NULL)
      {
        uriCfgAttWriteCback(handle, sizeof(beaconPeriod), (uint8_t *)&beaconPeriod);
      }
      break;
    }

    case URICFG_HANDLE_RESET:
    {
      uint8_t resetValue;

      if (lockState)
      {
        return ATT_ERR_AUTHOR;
      }

      /* check value */
      resetValue = *pValue;

      if (resetValue != 0)
      {
        uint8_t  uriFlags;
        uint8_t  txPwrMode;
        uint16_t beaconPeriod;
        uint8_t  lock[] = {URICFG_ATT_LOCK_DEFAULT_BYTES};

        /* reset URI data */
        AttsSetAttr(URICFG_HANDLE_URIDATA, sizeof(uriCfgDataResetValue), uriCfgDataResetValue);
        if (uriCfgAttWriteCback != NULL)
        {
          uriCfgAttWriteCback(URICFG_HANDLE_URIDATA, sizeof(uriCfgDataResetValue), uriCfgDataResetValue);
        }

        /* reset URI flags */
        uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
        AttsSetAttr(URICFG_HANDLE_URIFLAGS, sizeof(uriFlags), &uriFlags);
        if (uriCfgAttWriteCback != NULL)
        {
          uriCfgAttWriteCback(URICFG_HANDLE_URIFLAGS, sizeof(uriFlags), &uriFlags);
        }

        /* reset tx power mode */
        txPwrMode = URICFG_ATT_TXPWRMODE_DEFAULT;
        AttsSetAttr(URICFG_HANDLE_TXPWRMODE, sizeof(txPwrMode), &txPwrMode);
        if (uriCfgAttWriteCback != NULL)
        {
          uriCfgAttWriteCback(URICFG_HANDLE_TXPWRMODE, sizeof(txPwrMode), &txPwrMode);
        }

        /* reset beacon period */
        beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
        uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
        AttsSetAttr(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod), beaconPeriodVal);
        if (uriCfgAttWriteCback != NULL)
        {
          uriCfgAttWriteCback(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod), (uint8_t *)&beaconPeriod);
        }

        /* reset lock */
        AttsSetAttr(URICFG_HANDLE_LOCK, URICFG_SIZE_LOCK_ATT, lock);
        if (uriCfgAttWriteCback != NULL)
        {
          uriCfgAttWriteCback(URICFG_HANDLE_LOCK, sizeof(lock), lock);
        }
      }
      break;
    }

    default:
      return ATT_ERR_NOT_SUP;
  }
  return ATT_SUCCESS;
}

/*************************************************************************************************/
/*!
 *  \fn     UriCfgStart
 *
 *  \brief  Start UriBeacon configuration service.
 *
 *  \param  pUriData          Initial URI data value
 *  \param  uriDataLen        Length of URI data value
 *  \param  uriFlags          Initial URI flags value
 *  \param  advTxPwrLevels    Initial advertised tx power levels value
 *  \param  txPwrMode         Initial tx power mode value
 *  \param  beaconPeriod      Initial beacon period value
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriCfgStart(const uint8_t *pUriData, uint8_t uriDataLen, uint8_t uriFlags,
    int8_t *pAdvTxPwrLevels, uint8_t txPwrMode, uint16_t beaconPeriod)
{
  SvcUriCfgAddGroup();
  SvcUriCfgCbackRegister(uriCfgBeaconWriteCback);

  uint8_t lockState = 0;
  AttsSetAttr(URICFG_HANDLE_LOCKSTATE,    sizeof(lockState),          &lockState);
  AttsSetAttr(URICFG_HANDLE_URIDATA,      uriDataLen,                 (uint8_t *)pUriData);
  AttsSetAttr(URICFG_HANDLE_URIFLAGS,     sizeof(uriFlags),           &uriFlags);
  AttsSetAttr(URICFG_HANDLE_TXPWRLEVELS,  URICFG_SIZE_TXPWRLEVELS_ATT,(uint8_t *)pAdvTxPwrLevels);
  AttsSetAttr(URICFG_HANDLE_TXPWRMODE,    sizeof(txPwrMode),          &txPwrMode);
  uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
  AttsSetAttr(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod),       beaconPeriodVal);

  uriCfgAttWriteCback = NULL;
  uriCfgLockChangeCback = NULL;
  memset(uriCfgDataResetValue, 0xFFu, URICFG_MAXSIZE_URIDATA_ATT);
}

/*************************************************************************************************/
/*!
 *  \fn     UriCfgStop
 *
 *  \brief  Stop UriBeacon service.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriCfgStop(void)
{
  SvcUriCfgRemoveGroup();
}

/*************************************************************************************************/
/*!
 *  \fn     UriCfgAttWriteCbackRegister
 *
 *  \brief  Register callback for written UriBeacon attributes.
 *
 *  \param  cback       Callback to invoke when an attribute changes.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriCfgAttWriteCbackRegister(uriCfgAttWriteCback_t cback)
{
  uriCfgAttWriteCback = cback;
}

/*************************************************************************************************/
/*!
 *  \fn     UriCfgMakeLockable
 *
 *  \brief  Make UriBeacon lockable.
 *
 *  \param  lockState   Initial lock state value.
 *  \param  lock        Initial lock value.
 *  \param  cback       Callback to invoke when lock changes.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriCfgMakeLockable(uint8_t lockState, uint8_t *pLock, uriCfgLockChangeCback_t cback)
{
  AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState),    &lockState);
  AttsSetAttr(URICFG_HANDLE_LOCK,      URICFG_SIZE_LOCK_ATT, pLock);

  uriCfgLockChangeCback = cback;
}

/*************************************************************************************************/
/*!
 *  \fn     UriCfgSetUriDataResetValue
 *
 *  \brief  Set reset value of URI data.
 *
 *  \param  pUriData    Reset value of URI data.
 *
 *  \return None.
 */
/*************************************************************************************************/
void UriCfgSetUriDataResetValue(const uint8_t *pUriData)
{
  memcpy(uriCfgDataResetValue, pUriData, URICFG_MAXSIZE_URIDATA_ATT);
}
