/******************************************************************************
 *
 *  Copyright 2019 The Android Open Source Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

#include <frameworks/base/core/proto/android/bluetooth/enums.pb.h>
#include <frameworks/base/core/proto/android/bluetooth/hci/enums.pb.h>

#include "bt_types.h"
#include "btm_int.h"
#include "common/metrics.h"
#include "device/include/controller.h"
#include "l2c_int.h"
#include "stack/gatt/connection_manager.h"
#include "stack/include/hcimsgs.h"

extern void btm_ble_advertiser_notify_terminated_legacy(
    uint8_t status, uint16_t connection_handle);

/** This function get BLE connection state */
tBTM_BLE_CONN_ST btm_ble_get_conn_st(void) {
  return btm_cb.ble_ctr_cb.conn_state;
}

/** This function set BLE connection state */
void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st) {
  btm_cb.ble_ctr_cb.conn_state = new_st;

  if (new_st == BLE_CONNECTING)
    btm_ble_set_topology_mask(BTM_BLE_STATE_INIT_BIT);
  else
    btm_ble_clear_topology_mask(BTM_BLE_STATE_INIT_BIT);
}

void btm_send_hci_create_connection(
    uint16_t scan_int, uint16_t scan_win, uint8_t init_filter_policy,
    uint8_t addr_type_peer, const RawAddress& bda_peer, uint8_t addr_type_own,
    uint16_t conn_int_min, uint16_t conn_int_max, uint16_t conn_latency,
    uint16_t conn_timeout, uint16_t min_ce_len, uint16_t max_ce_len,
    uint8_t initiating_phys) {
  if (controller_get_interface()->supports_ble_extended_advertising()) {
    EXT_CONN_PHY_CFG phy_cfg[3];  // maximum three phys

    int phy_cnt =
        std::bitset<std::numeric_limits<uint8_t>::digits>(initiating_phys)
            .count();

    LOG_ASSERT(phy_cnt <= 3) << "More than three phys provided";
    // TODO(jpawlowski): tune parameters for different transports
    for (int i = 0; i < phy_cnt; i++) {
      phy_cfg[i].scan_int = scan_int;
      phy_cfg[i].scan_win = scan_win;
      phy_cfg[i].conn_int_min = conn_int_min;
      phy_cfg[i].conn_int_max = conn_int_max;
      phy_cfg[i].conn_latency = conn_latency;
      phy_cfg[i].sup_timeout = conn_timeout;
      phy_cfg[i].min_ce_len = min_ce_len;
      phy_cfg[i].max_ce_len = max_ce_len;
    }

    addr_type_peer &= ~BLE_ADDR_TYPE_ID_BIT;
    btsnd_hcic_ble_ext_create_conn(init_filter_policy, addr_type_own,
                                   addr_type_peer, bda_peer, initiating_phys,
                                   phy_cfg);
  } else {
    btsnd_hcic_ble_create_ll_conn(scan_int, scan_win, init_filter_policy,
                                  addr_type_peer, bda_peer, addr_type_own,
                                  conn_int_min, conn_int_max, conn_latency,
                                  conn_timeout, min_ce_len, max_ce_len);
  }

  btm_ble_set_conn_st(BLE_CONNECTING);
}

/** LE connection complete. */
void btm_ble_create_ll_conn_complete(uint8_t status) {
  if (status == HCI_SUCCESS) return;

  LOG(WARNING) << "LE Create Connection attempt failed, status="
               << loghex(status);

  if (status == HCI_ERR_COMMAND_DISALLOWED) {
    btm_ble_set_conn_st(BLE_CONNECTING);
    LOG(ERROR) << "LE Create Connection - command disallowed";
  } else {
    btm_ble_set_conn_st(BLE_CONN_IDLE);
    btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, NULL, status);
  }
}

/** LE connection complete. */
void btm_ble_conn_complete(uint8_t* p, UNUSED_ATTR uint16_t evt_len,
                           bool enhanced) {
#if (BLE_PRIVACY_SPT == TRUE)
  uint8_t peer_addr_type;
#endif
  RawAddress local_rpa, peer_rpa;
  uint8_t role, status, bda_type;
  uint16_t handle;
  RawAddress bda;
  uint16_t conn_interval, conn_latency, conn_timeout;
  bool match = false;

  STREAM_TO_UINT8(status, p);
  STREAM_TO_UINT16(handle, p);
  STREAM_TO_UINT8(role, p);
  STREAM_TO_UINT8(bda_type, p);
  STREAM_TO_BDADDR(bda, p);
  if (enhanced) {
    STREAM_TO_BDADDR(local_rpa, p);
    STREAM_TO_BDADDR(peer_rpa, p);
  }
  STREAM_TO_UINT16(conn_interval, p);
  STREAM_TO_UINT16(conn_latency, p);
  STREAM_TO_UINT16(conn_timeout, p);
  handle = HCID_GET_HANDLE(handle);

  uint32_t hci_ble_event =
      enhanced ? android::bluetooth::hci::BLE_EVT_ENHANCED_CONN_COMPLETE_EVT
               : android::bluetooth::hci::BLE_EVT_CONN_COMPLETE_EVT;

  if (status == HCI_SUCCESS) {
#if (BLE_PRIVACY_SPT == TRUE)
    peer_addr_type = bda_type;
    bool addr_is_rpa =
        (peer_addr_type == BLE_ADDR_RANDOM && BTM_BLE_IS_RESOLVE_BDA(bda));

    /* We must translate whatever address we received into the "pseudo" address.
     * i.e. if we bonded with device that was using RPA for first connection,
     * "pseudo" address is equal to this RPA. If it later decides to use Public
     * address, or Random Static Address, we convert it into the "pseudo"
     * address here. */
    if (!addr_is_rpa || peer_addr_type & BLE_ADDR_TYPE_ID_BIT) {
      match = btm_identity_addr_to_random_pseudo(&bda, &bda_type, true);
    }

    /* possiblly receive connection complete with resolvable random while
       the device has been paired */
    if (!match && addr_is_rpa) {
      tBTM_SEC_DEV_REC* match_rec = btm_ble_resolve_random_addr(bda);
      if (match_rec) {
        LOG(INFO) << __func__ << ": matched and resolved random address";
        match = true;
        match_rec->ble.active_addr_type = BTM_BLE_ADDR_RRA;
        match_rec->ble.cur_rand_addr = bda;
        if (!btm_ble_init_pseudo_addr(match_rec, bda)) {
          /* assign the original address to be the current report address */
          bda = match_rec->ble.pseudo_addr;
        } else {
          bda = match_rec->bd_addr;
        }
      } else {
        LOG(INFO) << __func__ << ": unable to match and resolve random address";
      }
    }
#endif
    // Log for the HCI success case after resolving Bluetooth address
    bluetooth::common::LogLinkLayerConnectionEvent(
        &bda, handle, android::bluetooth::DIRECTION_UNKNOWN,
        android::bluetooth::LINK_TYPE_ACL, android::bluetooth::hci::CMD_UNKNOWN,
        android::bluetooth::hci::EVT_BLE_META, hci_ble_event, status,
        android::bluetooth::hci::STATUS_UNKNOWN);

    if (role == HCI_ROLE_MASTER) {
      btm_ble_set_conn_st(BLE_CONN_IDLE);
    }

    connection_manager::on_connection_complete(bda);
    btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type,
                      match);

    l2cble_conn_comp(handle, role, bda, bda_type, conn_interval, conn_latency,
                     conn_timeout);

#if (BLE_PRIVACY_SPT == TRUE)
    if (enhanced) {
      btm_ble_refresh_local_resolvable_private_addr(bda, local_rpa);

      if (peer_addr_type & BLE_ADDR_TYPE_ID_BIT)
        btm_ble_refresh_peer_resolvable_private_addr(bda, peer_rpa,
                                                     BLE_ADDR_RANDOM);
    }
#endif
  } else {
    // Log for non HCI success case
    bluetooth::common::LogLinkLayerConnectionEvent(
        &bda, handle, android::bluetooth::DIRECTION_UNKNOWN,
        android::bluetooth::LINK_TYPE_ACL, android::bluetooth::hci::CMD_UNKNOWN,
        android::bluetooth::hci::EVT_BLE_META, hci_ble_event, status,
        android::bluetooth::hci::STATUS_UNKNOWN);

    role = HCI_ROLE_UNKNOWN;
    if (status != HCI_ERR_ADVERTISING_TIMEOUT) {
      btm_ble_set_conn_st(BLE_CONN_IDLE);
#if (BLE_PRIVACY_SPT == TRUE)
      btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, true);
#endif
    } else {
#if (BLE_PRIVACY_SPT == TRUE)
      btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE;
      btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, true);
#endif
    }
  }

  btm_ble_update_mode_operation(role, &bda, status);

  if (role == HCI_ROLE_SLAVE)
    btm_ble_advertiser_notify_terminated_legacy(status, handle);
}

void btm_ble_create_conn_cancel() {
  btsnd_hcic_ble_create_conn_cancel();
  btm_ble_set_conn_st(BLE_CONN_CANCEL);
}

void btm_ble_create_conn_cancel_complete(uint8_t* p) {
  uint8_t status;
  STREAM_TO_UINT8(status, p);
  if (status != HCI_SUCCESS) {
    // Only log errors to prevent log spam due to whitelist connections
    bluetooth::common::LogLinkLayerConnectionEvent(
        nullptr, bluetooth::common::kUnknownConnectionHandle,
        android::bluetooth::DIRECTION_OUTGOING,
        android::bluetooth::LINK_TYPE_ACL,
        android::bluetooth::hci::CMD_BLE_CREATE_CONN_CANCEL,
        android::bluetooth::hci::EVT_COMMAND_COMPLETE,
        android::bluetooth::hci::BLE_EVT_UNKNOWN, status,
        android::bluetooth::hci::STATUS_UNKNOWN);
  }

  if (status == HCI_ERR_COMMAND_DISALLOWED) {
    /* This is a sign that logic around keeping connection state is broken */
    LOG(ERROR)
        << "Attempt to cancel LE connection, when no connection is pending.";
    if (btm_ble_get_conn_st() == BLE_CONN_CANCEL) {
      btm_ble_set_conn_st(BLE_CONN_IDLE);
      btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, nullptr, status);
    }
  }
}