/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/

/**
 ****************************************************************************************
 * @addtogroup ANCCTASK
 * @{
 ****************************************************************************************
 */

/*
 * INCLUDE FILES
 ****************************************************************************************
 */

#include "rwip_config.h"

#if (BLE_ANC_CLIENT)

#include "anc_common.h"
#include "ancc.h"
#include "ancc_task.h"

#include "attm.h"
#include "gap.h"
#include "gattc_task.h"
#include "prf_utils.h"
#include "prf_utils_128.h"

#include "compiler.h"
#include "ke_timer.h"

#include "co_utils.h"
#include "ke_mem.h"

#if (ANCS_PROXY_ENABLE)
#include "ancs_gatt_server.h"
#endif
/*
 * STRUCTURES
 ****************************************************************************************
 */

/// ANCS UUID
const struct att_uuid_128 ancc_anc_svc = {{0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B,
                                           0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5,
                                           0x31, 0xF4, 0x05, 0x79}};

/// State machine used to retrieve ANCS characteristics information
const struct prf_char_def_128 ancc_anc_char[ANCC_CHAR_MAX] = {
    /// Notification Source
    [ANCC_CHAR_NTF_SRC] = {{0xBD, 0x1D, 0xA2, 0x99, 0xE6, 0x25, 0x58, 0x8C,
                            0xD9, 0x42, 0x01, 0x63, 0x0D, 0x12, 0xBF, 0x9F},
                           ATT_MANDATORY,
                           ATT_CHAR_PROP_NTF},

    /// Control Point
    [ANCC_CHAR_CTRL_PT] = {{0xD9, 0xD9, 0xAA, 0xFD, 0xBD, 0x9B, 0x21, 0x98,
                            0xA8, 0x49, 0xE1, 0x45, 0xF3, 0xD8, 0xD1, 0x69},
                           ATT_OPTIONAL,
                           ATT_CHAR_PROP_WR},

    /// Data Source
    [ANCC_CHAR_DATA_SRC] = {{0xFB, 0x7B, 0x7C, 0xCE, 0x6A, 0xB3, 0x44, 0xBE,
                             0xB5, 0x4B, 0xD6, 0x24, 0xE9, 0xC6, 0xEA, 0x22},
                            ATT_OPTIONAL,
                            ATT_CHAR_PROP_NTF},

};

/// State machine used to retrieve ANCS characteristic descriptor information
const struct prf_char_desc_def ancc_anc_char_desc[ANCC_DESC_MAX] = {
    /// Notification Source Char. - Client Characteristic Configuration
    [ANCC_DESC_NTF_SRC_CL_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_MANDATORY,
                                  ANCC_CHAR_NTF_SRC},
    /// Data Source Char. - Client Characteristic Configuration
    [ANCC_DESC_DATA_SRC_CL_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_OPTIONAL,
                                   ANCC_CHAR_DATA_SRC},
};

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

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref ANCC_ENABLE_REQ message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int ancc_enable_req_handler(ke_msg_id_t const msgid,
                                   struct ancc_enable_req *param,
                                   ke_task_id_t const dest_id,
                                   ke_task_id_t const src_id) {
  // Status
  uint8_t status = GAP_ERR_NO_ERROR;
  struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);
  // Get connection index
  uint8_t conidx = param->conidx;
  uint8_t state = ke_state_get(dest_id);
  TRACE(3, "ANCSC %s Entry. state=%d, conidx=%d", __func__, state, conidx);

  if ((state == ANCC_IDLE) && (ancc_env->env[conidx] == NULL)) {
    TRACE(1, "ANCSC %s passed state check", __func__);
    // allocate environment variable for task instance
    ancc_env->env[conidx] = (struct ancc_cnx_env *)ke_malloc(
        sizeof(struct ancc_cnx_env), KE_MEM_ATT_DB);
    ASSERT(ancc_env->env[conidx], "%s alloc error", __func__);
    memset(ancc_env->env[conidx], 0, sizeof(struct ancc_cnx_env));

    ancc_env->env[conidx]->last_char_code = ANCC_ENABLE_OP_CODE;

    // Start discovering
    // Discover ANCS service by 128-bit UUID
    prf_disc_svc_send_128(&(ancc_env->prf_env), conidx,
                          (uint8_t *)&ancc_anc_svc.uuid);

    // Go to DISCOVERING state
    ke_state_set(dest_id, ANCC_DISCOVERING);

    // Configure the environment for a discovery procedure
    ancc_env->env[conidx]->last_req = ATT_DECL_PRIMARY_SERVICE;
  }

  else if (state != ANCC_FREE) {
    status = PRF_ERR_REQ_DISALLOWED;
  }

  // send an error if request fails
  if (status != GAP_ERR_NO_ERROR) {
    ancc_enable_rsp_send(ancc_env, conidx, status);
  }

  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref ANCC_READ_CMD message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int ancc_read_cmd_handler(ke_msg_id_t const msgid,
                                 struct gattc_read_cmd *param,
                                 ke_task_id_t const dest_id,
                                 ke_task_id_t const src_id) {

  uint8_t conidx = KE_IDX_GET(dest_id);
  TRACE(6, "ANCSC %s Entry. conidex %d hdl=0x%4.4x, op=%d, len=%d, off=%d",
        __func__, conidx, param->req.simple.handle, param->operation,
        param->req.simple.length, param->req.simple.offset);

  // Get the address of the environment
  struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);

  if (ancc_env != NULL) {
    // Send the read request
    prf_read_char_send(
        &(ancc_env->prf_env), conidx, ancc_env->env[conidx]->anc.svc.shdl,
        ancc_env->env[conidx]->anc.svc.ehdl, param->req.simple.handle);
  } else {
    //        amsc_send_no_conn_cmp_evt(dest_id, src_id, param->handle,
    //        ANCC_WRITE_CL_CFG_OP_CODE);
    ASSERT(0, "%s implement me", __func__);
  }

  return KE_MSG_CONSUMED;
}

static int gattc_read_ind_handler(ke_msg_id_t const msgid,
                                  struct gattc_read_ind const *param,
                                  ke_task_id_t const dest_id,
                                  ke_task_id_t const src_id) {
  // Get the address of the environment
  struct amsc_env_tag *amsc_env = PRF_ENV_GET(ANCC, amsc);
  TRACE(3, "ANCSC %s param->handle=0x%x param->length=%d", __func__,
        param->handle, param->length);

  if (amsc_env != NULL) {
    uint8_t conidx = KE_IDX_GET(src_id);
    struct gattc_read_cfm *cfm = KE_MSG_ALLOC_DYN(
        GATTC_READ_CFM,
        KE_BUILD_ID(prf_get_task_from_id(TASK_ID_ANCSP), conidx), dest_id,
        gattc_read_cfm, param->length);
    cfm->status = 0; // read_ind has no status???
    cfm->handle = param->handle;
    cfm->length = param->length;
    memcpy(cfm->value, param->value, param->length);
    ke_msg_send(cfm);
  }
  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref ANCC_WRITE_CMD message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int ancc_write_cmd_handler(ke_msg_id_t const msgid,
                                  struct gattc_write_cmd *param,
                                  ke_task_id_t const dest_id,
                                  ke_task_id_t const src_id) {
  TRACE(4, "ANCSC %s Entry. hdl=0x%4.4x, op=%d, len=%d", __func__,
        param->handle, param->operation, param->length);

  uint8_t conidx = KE_IDX_GET(dest_id);

  // Get the address of the environment
  struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);

  if (ancc_env != NULL) {
    ancc_env->last_write_handle[conidx] = param->handle;
    // TODO(jkessinger): Use ke_msg_forward.
    struct gattc_write_cmd *wr_char =
        KE_MSG_ALLOC_DYN(GATTC_WRITE_CMD, KE_BUILD_ID(TASK_GATTC, conidx),
                         dest_id, gattc_write_cmd, param->length);
    memcpy(wr_char, param, sizeof(struct gattc_write_cmd) + param->length);
    // Send the message
    ke_msg_send(wr_char);
  } else {
    //        ancc_send_no_conn_cmp_evt(dest_id, src_id, param->handle,
    //        ANCC_WRITE_CL_CFG_OP_CODE);
    ASSERT(0, "%s implement me", __func__);
  }

  return KE_MSG_CONSUMED;
}

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATTC_SDP_SVC_IND_HANDLER message.
 * The handler stores the found service details for service discovery.
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
__STATIC int gattc_sdp_svc_ind_handler(ke_msg_id_t const msgid,
                                       struct gattc_sdp_svc_ind const *ind,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id) {
  uint8_t state = ke_state_get(dest_id);

  TRACE(4,
        "ANCSC %s Entry. end_hdl=0x%4.4x, start_hdl=0x%4.4x, att.att_type=%d",
        __func__, ind->end_hdl, ind->start_hdl, ind->info[0].att.att_type);
  TRACE(3,
        "ANCSC att_char.prop=%d, att_char.handle=0x%4.4x, att_char.att_type=%d",
        ind->info[0].att_char.prop, ind->info[0].att_char.handle,
        ind->info[0].att_char.att_type);
  TRACE(4,
        "ANCSC inc_svc.att_type=%d, inc_svc.end_hdl=0x%4.4x, "
        "inc_svc.start_hdl=0x%4.4x, state=%d",
        ind->info[0].att_type, ind->info[0].inc_svc.att_type,
        ind->info[0].inc_svc.start_hdl, state);

  if (state == ANCC_DISCOVERING) {
    uint8_t conidx = KE_IDX_GET(src_id);

    struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);

    ASSERT_INFO(ancc_env != NULL, dest_id, src_id);
    ASSERT_INFO(ancc_env->env[conidx] != NULL, dest_id, src_id);

    if (ancc_env->env[conidx]->nb_svc == 0) {
      TRACE(0, "ANCSC retrieving characteristics and descriptors.");
      // Retrieve ANC characteristics and descriptors
      prf_extract_svc_info_128(ind, ANCC_CHAR_MAX, &ancc_anc_char[0],
                               &ancc_env->env[conidx]->anc.chars[0],
                               ANCC_DESC_MAX, &ancc_anc_char_desc[0],
                               &ancc_env->env[conidx]->anc.descs[0]);

      // Even if we get multiple responses we only store 1 range
      ancc_env->env[conidx]->anc.svc.shdl = ind->start_hdl;
      ancc_env->env[conidx]->anc.svc.ehdl = ind->end_hdl;
    }

    ancc_env->env[conidx]->nb_svc++;
  }

  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATTC_CMP_EVT message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gattc_cmp_evt_handler(ke_msg_id_t const msgid,
                                 struct gattc_cmp_evt const *param,
                                 ke_task_id_t const dest_id,
                                 ke_task_id_t const src_id) {
  // Get the address of the environment
  struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);
  uint8_t conidx = KE_IDX_GET(dest_id);
  TRACE(5, "ANCSC %s entry. op=%d, seq=%d, status=%d, conidx=%d", __func__,
        param->operation, param->seq_num, param->status, conidx);
  // Status
  uint8_t status;

  if (ancc_env->env[conidx] != NULL) {
    uint8_t state = ke_state_get(dest_id);

    TRACE(2, "ANCSC %s state=%d", __func__, state);
    if (state == ANCC_DISCOVERING) {
      status = param->status;

      if ((status == ATT_ERR_ATTRIBUTE_NOT_FOUND) ||
          (status == ATT_ERR_NO_ERROR)) {
        // Discovery
        // check characteristic validity
        if (ancc_env->env[conidx]->nb_svc == 1) {
          status = prf_check_svc_char_validity_128(
              ANCC_CHAR_MAX, ancc_env->env[conidx]->anc.chars, ancc_anc_char);
        }
        // too much services
        else if (ancc_env->env[conidx]->nb_svc > 1) {
          status = PRF_ERR_MULTIPLE_SVC;
        }
        // no services found
        else {
          status = PRF_ERR_STOP_DISC_CHAR_MISSING;
        }

        // check descriptor validity
        if (status == GAP_ERR_NO_ERROR) {
          status = prf_check_svc_char_desc_validity(
              ANCC_DESC_MAX, ancc_env->env[conidx]->anc.descs,
              ancc_anc_char_desc, ancc_env->env[conidx]->anc.chars);
        }
      }

      ancc_enable_rsp_send(ancc_env, conidx, status);
#if (ANCS_PROXY_ENABLE)
      TRACE(
          4, "ANCSC %s NSChar=0x%4.4x, NSVal=0x%4.4x, NSCfg=0x%4.4x", __func__,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_NTF_SRC].char_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_NTF_SRC].val_hdl,
          ancc_env->env[conidx]->anc.descs[ANCC_DESC_NTF_SRC_CL_CFG].desc_hdl);
      TRACE(
          4, "ANCSC %s DSChar=0x%4.4x DSVal=0x%4.4x, DSCfg=0x%4.4x", __func__,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_DATA_SRC].char_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_DATA_SRC].val_hdl,
          ancc_env->env[conidx]->anc.descs[ANCC_DESC_DATA_SRC_CL_CFG].desc_hdl);
      TRACE(3, "ANCSC %s CPChar=0x%4.4x, CPVal=0x%4.4x", __func__,
            ancc_env->env[conidx]->anc.chars[ANCC_CHAR_CTRL_PT].char_hdl,
            ancc_env->env[conidx]->anc.chars[ANCC_CHAR_CTRL_PT].val_hdl);
      ancs_proxy_set_ready_flag(
          conidx, ancc_env->env[conidx]->anc.chars[ANCC_CHAR_NTF_SRC].char_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_NTF_SRC].val_hdl,
          ancc_env->env[conidx]->anc.descs[ANCC_DESC_NTF_SRC_CL_CFG].desc_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_DATA_SRC].char_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_DATA_SRC].val_hdl,
          ancc_env->env[conidx]->anc.descs[ANCC_DESC_DATA_SRC_CL_CFG].desc_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_CTRL_PT].char_hdl,
          ancc_env->env[conidx]->anc.chars[ANCC_CHAR_CTRL_PT].val_hdl);
      ke_state_set(dest_id, ANCC_IDLE);
#endif
    } else {
      switch (param->operation) {
      case GATTC_READ: {
        TRACE(2, "ANCSC %s read complete status=%d", __func__, param->status);
        break;
      }
      case GATTC_WRITE: {
        struct gattc_write_cfm *cfm = KE_MSG_ALLOC(
            GATTC_WRITE_CFM,
            KE_BUILD_ID(prf_get_task_from_id(TASK_ID_ANCSP), conidx), dest_id,
            gattc_write_cfm);
        cfm->handle = ancc_env->last_write_handle[conidx];
        ancc_env->last_write_handle[conidx] = ATT_INVALID_HANDLE;
        cfm->status = param->status;
        ke_msg_send(cfm);
        break;
      }
      case GATTC_WRITE_NO_RESPONSE:
        // There's currently no need to notify the proxy task that this
        // completed.
        break;
      case GATTC_NOTIFY:
      case GATTC_INDICATE:
        // Nothing to do. Notification sent.
      case GATTC_REGISTER:
      case GATTC_UNREGISTER:
      case GATTC_SDP_DISC_SVC:
        // Do nothing
        break;

      default:
        ASSERT_ERR(0);
        break;
      }
    }
  }
  // else ignore the message
  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATTC_EVENT_IND message.
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gattc_event_ind_handler(ke_msg_id_t const msgid,
                                   struct gattc_event_ind const *param,
                                   ke_task_id_t const dest_id,
                                   ke_task_id_t const src_id) {
  BLE_FUNC_ENTER();
  TRACE(5, "ANCSC %s Entry. handle=0x%x, len=%d, type=%d, val[0]=0x%x",
        __func__, param->handle, param->length, param->type, param->value[0]);
  uint8_t conidx = KE_IDX_GET(src_id);
  struct gattc_send_evt_cmd *cmd;
  cmd =
      KE_MSG_ALLOC_DYN(ANCS_PROXY_IND_EVT,
                       KE_BUILD_ID(prf_get_task_from_id(TASK_ID_ANCSP), conidx),
                       dest_id, gattc_send_evt_cmd, param->length);
  cmd->handle = param->handle;
  cmd->operation = GATTC_NOTIFY;
  cmd->seq_num = 0;
  cmd->length = param->length;
  memcpy(cmd->value, param->value, param->length);
  ke_msg_send(cmd);

  return (KE_MSG_CONSUMED);
}

/*
 * GLOBAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// Specifies the default message handlers
KE_MSG_HANDLER_TAB(ancc){
    {ANCC_ENABLE_REQ, (ke_msg_func_t)ancc_enable_req_handler},
    {ANCC_WRITE_CMD, (ke_msg_func_t)ancc_write_cmd_handler},
    {ANCC_READ_CMD, (ke_msg_func_t)ancc_read_cmd_handler},
    {GATTC_READ_IND, (ke_msg_func_t)gattc_read_ind_handler},
    {GATTC_SDP_SVC_IND, (ke_msg_func_t)gattc_sdp_svc_ind_handler},
    {GATTC_EVENT_IND, (ke_msg_func_t)gattc_event_ind_handler},
    {GATTC_CMP_EVT, (ke_msg_func_t)gattc_cmp_evt_handler},
};

void ancc_task_init(struct ke_task_desc *task_desc) {
  TRACE(1, "ANCSC %s Entry.", __func__);
  // Get the address of the environment
  struct ancc_env_tag *ancc_env = PRF_ENV_GET(ANCC, ancc);

  task_desc->msg_handler_tab = ancc_msg_handler_tab;
  task_desc->msg_cnt = ARRAY_LEN(ancc_msg_handler_tab);
  task_desc->state = ancc_env->state;
  task_desc->idx_max = ANCC_IDX_MAX;
}

#endif //(BLE_ANC_CLIENT)

/// @} ANCCTASK
