/***************************************************************************
 *
 * 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 APP
 * @{
 ****************************************************************************************
 */

#include "rwip_config.h" // SW configuration

#if (BLE_APP_HT)

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

#include "app.h"      // Application Definitions
#include "app_ht.h"   // Health Thermometer Application Definitions
#include "app_task.h" // application task definitions
#include "arch.h"     // Platform Definitions
#include "co_bt.h"
#include "htpt_task.h" // health thermometer functions
#include "prf_types.h"
#include "prf_utils.h"

#include "co_math.h"
#include "ke_timer.h"

#if (DISPLAY_SUPPORT)
#include "app_display.h"
#include "display.h"
#endif // DISPLAY_SUPPORT

/*
 * DEFINES
 ****************************************************************************************
 */

/// Initial Temperature Value : 37c
#define APP_HT_TEMP_VALUE_INIT (3700)
/// Temperature Step
#define APP_HT_TEMP_STEP_INIT (10)
/// Measurement Interval Value Min
#define APP_HT_MEAS_INTV_MIN (1)
/// Measurement Interval Value Max
#define APP_HT_MEAS_INTV_MAX (30)

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

/// health thermometer application environment structure
struct app_ht_env_tag app_ht_env;

/*
 * LOCAL FUNCTION DEFINITIONS
 ****************************************************************************************
 */

static void app_ht_temp_send(void) {
  // Temperature Value
  int32_t value = (int32_t)(app_ht_env.temp_value);

  // The value is a float value, set the exponent
  value |= 0xFE000000;

  // Allocate the HTPT_TEMP_SEND_REQ message
  struct htpt_temp_send_req *req =
      KE_MSG_ALLOC(HTPT_TEMP_SEND_REQ, prf_get_task_from_id(TASK_ID_HTPT),
                   TASK_APP, htpt_temp_send_req);

  // Stable => Temperature Measurement Char.
  req->stable_meas = 0x01;

  // Temperature Measurement Value
  req->temp_meas.temp = value;
  //    req->temp_meas.time_stamp = 0;
  req->temp_meas.flags = HTP_FLAG_CELSIUS | HTP_FLAG_TYPE;
  req->temp_meas.type = app_ht_env.temp_meas_type;

  ke_msg_send(req);
}

static void app_ht_update_type_string(uint8_t temp_type) {
  switch (temp_type) {
  case 0:
    strcpy(app_ht_env.temp_type_string, "NONE");
    break;
  case 1:
    strcpy(app_ht_env.temp_type_string, "ARMPIT");
    break;
  case 2:
    strcpy(app_ht_env.temp_type_string, "BODY");
    break;
  case 3:
    strcpy(app_ht_env.temp_type_string, "EAR");
    break;
  case 4:
    strcpy(app_ht_env.temp_type_string, "FINGER");
    break;
  case 5:
    strcpy(app_ht_env.temp_type_string, "GASTRO-INT");
    break;
  case 6:
    strcpy(app_ht_env.temp_type_string, "MOUTH");
    break;
  case 7:
    strcpy(app_ht_env.temp_type_string, "RECTUM");
    break;
  case 8:
    strcpy(app_ht_env.temp_type_string, "TOE");
    break;
  case 9:
    strcpy(app_ht_env.temp_type_string, "TYMPANUM");
    break;
  default:
    strcpy(app_ht_env.temp_type_string, "UNKNOWN");
    break;
  }
}

/*
 * GLOBAL FUNCTION DEFINITIONS
 ****************************************************************************************
 */

void app_ht_init(void) {
  // Reset the environment
  memset(&app_ht_env, 0, sizeof(app_ht_env));

  // Initial measurement interval : 0s
  app_ht_env.htpt_meas_intv = 5;
  // Initial temperature value : 37.00 C
  app_ht_env.temp_value = APP_HT_TEMP_VALUE_INIT;
  // Initial temperature step : 0.20 C
  app_ht_env.temp_step = APP_HT_TEMP_STEP_INIT;
  // Initial temperature type : ARMPIT
  app_ht_env.temp_meas_type = 1;

  // TODO: Add a state for the module
}

void app_stop_timer(void) {
  // Stop the timer used for the measurement interval if enabled
  if (app_ht_env.timer_enable) {
    ke_timer_clear(APP_HT_MEAS_INTV_TIMER, TASK_APP);
    app_ht_env.timer_enable = false;
  }
}

void app_ht_add_hts(void) {
  struct htpt_db_cfg *db_cfg;
  // Allocate the HTPT_CREATE_DB_REQ
  struct gapm_profile_task_add_cmd *req =
      KE_MSG_ALLOC_DYN(GAPM_PROFILE_TASK_ADD_CMD, TASK_GAPM, TASK_APP,
                       gapm_profile_task_add_cmd, sizeof(struct htpt_db_cfg));

  // Fill message
  req->operation = GAPM_PROFILE_TASK_ADD;
#if BLE_CONNECTION_MAX > 1
  req->sec_lvl = PERM(SVC_AUTH, ENABLE) | PERM(SVC_MI, ENABLE);
#else
  req->sec_lvl = PERM(SVC_AUTH, ENABLE);
#endif

  req->prf_task_id = TASK_ID_HTPT;
  req->app_task = TASK_APP;
  req->start_hdl = 0;

  // Set parameters
  db_cfg = (struct htpt_db_cfg *)req->param;
  // All features are supported
  db_cfg->features = HTPT_ALL_FEAT_SUP;

  // Measurement Interval range
  db_cfg->valid_range_min = APP_HT_MEAS_INTV_MIN;
  db_cfg->valid_range_max = APP_HT_MEAS_INTV_MAX;

  // Measurement
  db_cfg->temp_type = app_ht_env.temp_meas_type;
  db_cfg->meas_intv = app_ht_env.htpt_meas_intv;

  // Send the message
  ke_msg_send(req);
}

void app_ht_enable_prf(uint8_t conidx) {
  // Allocate the message
  struct htpt_enable_req *req =
      KE_MSG_ALLOC(HTPT_ENABLE_REQ, prf_get_task_from_id(TASK_ID_HTPT),
                   TASK_APP, htpt_enable_req);

  // Fill in the parameter structure
  req->conidx = conidx;
  // NTF/IND initial status - Disabled
  req->ntf_ind_cfg = PRF_CLI_STOP_NTFIND;

  // Send the message
  ke_msg_send(req);
}

/**
 ****************************************************************************************
 * Health Thermometer Application Functions
 ****************************************************************************************
 */

void app_ht_temp_inc(void) {
  app_ht_env.temp_value += app_ht_env.temp_step;

#if (DISPLAY_SUPPORT)
  app_display_update_temp_val_screen(app_ht_env.temp_value);
#endif // DISPLAY_SUPPORT

  app_ht_temp_send();
}

void app_ht_temp_dec(void) {
  app_ht_env.temp_value -= app_ht_env.temp_step;

#if (DISPLAY_SUPPORT)
  app_display_update_temp_val_screen(app_ht_env.temp_value);
#endif // DISPLAY_SUPPORT

  app_ht_temp_send();
}

void app_ht_temp_type_inc(void) {
  app_ht_env.temp_meas_type =
      (uint8_t)(((int)app_ht_env.temp_meas_type + 1) % 10);

#if (DISPLAY_SUPPORT)
  app_ht_update_type_string(app_ht_env.temp_meas_type);
  app_display_update_temp_type_screen(app_ht_env.temp_type_string);
#endif // DISPLAY_SUPPORT
}

void app_ht_temp_type_dec(void) {
  if (((int)app_ht_env.temp_meas_type - 1) < 0) {
    app_ht_env.temp_meas_type = 0x09;
  } else {
    app_ht_env.temp_meas_type = app_ht_env.temp_meas_type - 1;
  }

#if DISPLAY_SUPPORT
  app_ht_update_type_string(app_ht_env.temp_meas_type);
  app_display_update_temp_type_screen(app_ht_env.temp_type_string);
#endif // DISPLAY_SUPPORT
}

/****************************************************************************************
 * MESSAGE HANDLERS
 ****************************************************************************************/

/**
 ****************************************************************************************
 * @brief Handles measurement interval start indication from the Health
 *Thermometer profile. Start or stop a timer following the value of the param
 *intv.
 *
 * @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 (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int htpt_meas_intv_chg_req_ind_handler(
    ke_msg_id_t const msgid, struct htpt_meas_intv_chg_req_ind const *param,
    ke_task_id_t const dest_id, ke_task_id_t const src_id) {
  // Store the received Measurement Interval value
  app_ht_env.htpt_meas_intv = param->intv;

  // Check the new Measurement Interval Value
  if (app_ht_env.htpt_meas_intv != 0) {
    // Check if a Timer already exists
    if (!app_ht_env.timer_enable) {
      // Set a Timer
      ke_timer_set(APP_HT_MEAS_INTV_TIMER, TASK_APP,
                   app_ht_env.htpt_meas_intv * 100);
      app_ht_env.timer_enable = true;
    } else {
      // Clear the previous timer
      ke_timer_clear(APP_HT_MEAS_INTV_TIMER, TASK_APP);
      // Create a new timer with the received measurement interval
      ke_timer_set(APP_HT_MEAS_INTV_TIMER, TASK_APP,
                   app_ht_env.htpt_meas_intv * 100);
    }
  } else {
    // Check if a Timer exists
    if (app_ht_env.timer_enable) {
      // Measurement Interval is 0, clear the timer
      ke_timer_clear(APP_HT_MEAS_INTV_TIMER, TASK_APP);
      app_ht_env.timer_enable = false;
    }
  }

  // Allocate the message
  struct htpt_meas_intv_chg_cfm *cfm =
      KE_MSG_ALLOC(HTPT_MEAS_INTV_CHG_CFM, prf_get_task_from_id(TASK_ID_HTPT),
                   TASK_APP, htpt_meas_intv_chg_cfm);

  // Set data
  cfm->conidx = KE_IDX_GET(dest_id);
  cfm->status = 0;

  // Send the message
  ke_msg_send(cfm);

  return (KE_MSG_CONSUMED);
}

static int htpt_temp_send_rsp_handler(ke_msg_id_t const msgid,
                                      struct htpt_temp_send_rsp const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id) {
  // Do nothing
  return (KE_MSG_CONSUMED);
}

static int htpt_cfg_indntf_ind_handler(ke_msg_id_t const msgid,
                                       struct htpt_cfg_indntf_ind const *param,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id) {
  // Do nothing

  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles health thermometer timer
 *
 * @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 (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int app_ht_meas_intv_timer_handler(ke_msg_id_t const msgid,
                                          void const *param,
                                          ke_task_id_t const dest_id,
                                          ke_task_id_t const src_id) {
  // Random generation of a temperature value
  uint32_t rand_temp_step;
  // Sign used to know if the temperature will be increased or decreased
  int8_t sign;

  // Generate temperature step
  rand_temp_step = (uint32_t)(co_rand_word() % 20);
  // Increase or decrease the temperature value
  sign = (int8_t)(rand_temp_step & 0x00000001);

  if (!sign) {
    sign = -1;
  }

  app_ht_env.temp_value += sign * rand_temp_step;

  // Send the new temperature
  app_ht_temp_send();

#if (DISPLAY_SUPPORT)
  app_display_update_temp_val_screen(app_ht_env.temp_value);
#endif // DISPLAY_SUPPORT

  // Reset the Timer (Measurement Interval is not 0 if we are here)
  ke_timer_set(APP_HT_MEAS_INTV_TIMER, TASK_APP,
               app_ht_env.htpt_meas_intv * 100);

  return (KE_MSG_CONSUMED);
}

/**
 ****************************************************************************************
 * @brief Handles health thermometer timer
 *
 * @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 (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int app_ht_msg_handler(ke_msg_id_t const msgid, void const *param,
                              ke_task_id_t const dest_id,
                              ke_task_id_t const src_id) {
  // Do nothing

  return (KE_MSG_CONSUMED);
}

/*
 * LOCAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// Default State handlers definition
const struct ke_msg_handler app_ht_msg_handler_list[] = {
    // Note: first message is latest message checked by kernel so default is put
    // on top.
    {KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)app_ht_msg_handler},

    //  {HTPT_ENABLE_RSP,               (ke_msg_func_t)htpt_enable_rsp_handler},
    {HTPT_TEMP_SEND_RSP, (ke_msg_func_t)htpt_temp_send_rsp_handler},
    {HTPT_MEAS_INTV_CHG_REQ_IND,
     (ke_msg_func_t)htpt_meas_intv_chg_req_ind_handler},
    {HTPT_CFG_INDNTF_IND, (ke_msg_func_t)htpt_cfg_indntf_ind_handler},

    {APP_HT_MEAS_INTV_TIMER, (ke_msg_func_t)app_ht_meas_intv_timer_handler},
};

const struct ke_state_handler app_ht_table_handler = {
    &app_ht_msg_handler_list[0],
    (sizeof(app_ht_msg_handler_list) / sizeof(struct ke_msg_handler))};

#endif // BLE_APP_HT

/// @} APP
