/***************************************************************************
 *
 * 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_OTA)

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

#include "app.h"      // Application Definitions
#include "app_ota.h"  // OTA Application Definitions
#include "app_task.h" // application task definitions
#include "arch.h"     // Platform Definitions
#include "co_bt.h"
#include "ota_bes.h"
#include "ota_control.h"
#include "ota_task.h"
#include "prf.h"
#include "prf_types.h"
#include "prf_utils.h"
#include "string.h"

#include "app_ble_mode_switch.h"
#include "app_ble_rx_handler.h"
#if defined(IBRT)
#include "app_tws_ibrt.h"
#endif

/*
 * GLOBAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */
struct app_ota_env_tag app_ota_env;

static app_ota_tx_done_t ota_data_tx_done_callback = NULL;

/*
 * GLOBAL FUNCTION DEFINITIONS
 ****************************************************************************************
 */
void app_ota_connected_evt_handler(uint8_t conidx) {
  app_ota_env.connectionIndex = conidx;
  app_ota_connected(APP_OTA_CONNECTED);
  ota_control_set_datapath_type(DATA_PATH_BLE);
  ota_control_register_transmitter(app_ota_send_notification);
  l2cap_update_param(conidx, 10, 15, 20000, 0);
}

void app_ota_disconnected_evt_handler(uint8_t conidx) {
  if (conidx == app_ota_env.connectionIndex) {
    app_ota_env.connectionIndex = BLE_INVALID_CONNECTION_INDEX;
    app_ota_env.isNotificationEnabled = false;
    app_ota_env.mtu[conidx] = 0;
    app_ota_disconnected(APP_OTA_DISCONNECTED);
  }
}

static void app_ota_ble_data_fill_handler(void *param) {
  // normally we won't allow OTA owned adv when there is already
  // an existing BLE connection. For special requirement, you can
  // disable this limitation
  if (app_ble_connection_count() > 0) {
    app_ble_data_fill_enable(USER_OTA, false);
    return;
  }

  bool adv_enable = false;

#if defined(IBRT)
  if (app_tws_ibrt_get_bt_ctrl_ctx()->init_done) {
    if (app_tws_ibrt_role_get_callback(NULL) != IBRT_MASTER) {
      TRACE(2, "%s role %d isn't MASTER", __func__,
            app_tws_ibrt_role_get_callback(NULL));
    } else if (!app_tws_ibrt_mobile_link_connected()) {
      TRACE(1, "%s don't connect mobile", __func__);
    } else {
      adv_enable = true;
    }
  }
#else
  adv_enable = true;
#endif

  app_ble_data_fill_enable(USER_OTA, adv_enable);
}

void app_ota_init(void) {
  // Reset the environment
  app_ota_env.connectionIndex = BLE_INVALID_CONNECTION_INDEX;
  app_ota_env.isNotificationEnabled = false;
  memset((uint8_t *)&(app_ota_env.mtu), 0, sizeof(app_ota_env.mtu));

  app_ble_register_data_fill_handle(
      USER_OTA, (BLE_DATA_FILL_FUNC_T)app_ota_ble_data_fill_handler, false);
}

void app_ota_add_ota(void) {
  BLE_APP_DBG("app_ota_add_ota");
  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, 0);

  // 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_OTA;
  req->app_task = TASK_APP;
  req->start_hdl = 0;

  // Send the message
  ke_msg_send(req);
}

void app_ota_send_notification(uint8_t *ptrData, uint32_t length) {
  TRACE(0, "Send ota rsp:");
  DUMP8("%02x ", ptrData, length);

  struct ble_ota_send_data_req_t *req =
      KE_MSG_ALLOC_DYN(OTA_SEND_NOTIFICATION,
                       KE_BUILD_ID(prf_get_task_from_id(TASK_ID_OTA),
                                   app_ota_env.connectionIndex),
                       TASK_APP, ble_ota_send_data_req_t, length);
  req->connecionIndex = app_ota_env.connectionIndex;
  req->length = length;
  memcpy(req->value, ptrData, length);

  ke_msg_send(req);
}

void app_ota_send_indication(uint8_t *ptrData, uint32_t length) {
  struct ble_ota_send_data_req_t *req =
      KE_MSG_ALLOC_DYN(OTA_SEND_INDICATION,
                       KE_BUILD_ID(prf_get_task_from_id(TASK_ID_OTA),
                                   app_ota_env.connectionIndex),
                       TASK_APP, ble_ota_send_data_req_t, length);
  req->connecionIndex = app_ota_env.connectionIndex;
  req->length = length;
  memcpy(req->value, ptrData, length);

  ke_msg_send(req);
}

static int app_ota_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);
}

static int app_ota_ccc_changed_handler(ke_msg_id_t const msgid,
                                       struct ble_ota_tx_notif_config_t *param,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id) {
  TRACE(1, "ota data ccc changed to %d", param->isNotificationEnabled);
  app_ota_env.isNotificationEnabled = param->isNotificationEnabled;

  if (app_ota_env.isNotificationEnabled) {
    if (BLE_INVALID_CONNECTION_INDEX == app_ota_env.connectionIndex) {
      uint8_t conidx = KE_IDX_GET(src_id);
      app_ota_connected_evt_handler(conidx);

      if (app_ota_env.mtu[conidx]) {
        ota_control_update_MTU(app_ota_env.mtu[conidx]);
      }
    }
  }

  return (KE_MSG_CONSUMED);
}

static int app_ota_tx_data_sent_handler(ke_msg_id_t const msgid,
                                        struct ble_ota_tx_sent_ind_t *param,
                                        ke_task_id_t const dest_id,
                                        ke_task_id_t const src_id) {
  if (NULL != ota_data_tx_done_callback) {
    ota_data_tx_done_callback();
  }

  // ota_data_transmission_done_callback();

  return (KE_MSG_CONSUMED);
}

static int app_ota_data_received_handler(ke_msg_id_t const msgid,
                                         struct ble_ota_rx_data_ind_t *param,
                                         ke_task_id_t const dest_id,
                                         ke_task_id_t const src_id) {
  app_ble_push_rx_data(BLE_RX_DATA_SELF_OTA, app_ota_env.connectionIndex,
                       param->data, param->length);
  return (KE_MSG_CONSUMED);
}

void app_ota_register_tx_done(app_ota_tx_done_t callback) {
  ota_data_tx_done_callback = callback;
}

void app_ota_mtu_exchanged_handler(uint8_t conidx, uint16_t MTU) {
  if (conidx == app_ota_env.connectionIndex) {
    ota_control_update_MTU(MTU);
  } else {
    app_ota_env.mtu[conidx] = MTU;
  }
}

uint8_t app_ota_get_conidx(void) { return app_ota_env.connectionIndex; }
/*
 * LOCAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// Default State handlers definition
const struct ke_msg_handler app_ota_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_ota_msg_handler},

    {OTA_CCC_CHANGED, (ke_msg_func_t)app_ota_ccc_changed_handler},
    {OTA_TX_DATA_SENT, (ke_msg_func_t)app_ota_tx_data_sent_handler},
    {OTA_DATA_RECEIVED, (ke_msg_func_t)app_ota_data_received_handler},
};

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

#endif // BLE_APP_OTA

/// @} APP