/******************************************************************************
 *
 *  Copyright (C) 2015, The linux Foundation. All rights reserved.
 *
 *  Not a Contribution.
 *
 *  Copyright (C) 2009-2012 Broadcom Corporation
 *
 *  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.
 *
 ******************************************************************************/

/************************************************************************************
 *
 *  Filename:      mcap_tool.cc
 *
 *  Description:   Fluoride MCAP Test Tool application
 *
 ***********************************************************************************/
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef OS_GENERIC
#include <sys/capability.h>
#endif
#include <sys/prctl.h>
#include <time.h>
#include <unistd.h>

#include <hardware/bluetooth.h>
#include <hardware/hardware.h>
#ifndef OS_GENERIC
#include <private/android_filesystem_config.h>
#endif
#include <base/logging.h>

#include "bt_types.h"
#include "l2c_api.h"
#include "mca_api.h"
#include "mca_defs.h"
#include "osi/include/compat.h"
#if defined(OS_GENERIC)
#include "hal_util.h"
#endif
#include "mcap_test_app.h"
#include "mcap_test_mcl.h"
#include "mcap_test_mdep.h"
#include "mcap_test_mdl.h"

using SYSTEM_BT_TOOLS_MCAP_TOOL::McapTestApp;
using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMcl;
using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdep;
using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdl;

/******************************************************************************
 *  Constants & Macros
 *****************************************************************************/
#define PID_FILE "/data/.bdt_pid"

#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif

#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif

#define CASE_RETURN_STR(const) \
  case const:                  \
    return #const;

#ifndef OS_GENERIC
/* Permission Groups */
static gid_t groups[] = {AID_NET_BT,    AID_INET, AID_NET_BT_ADMIN,
                         AID_SYSTEM,    AID_MISC, AID_SDCARD_RW,
                         AID_NET_ADMIN, AID_VPN};
#endif
/******************************************************************************
 *  Static variables
 *****************************************************************************/
/* Console loop states */
static bool global_main_done = false;
static bt_status_t global_status;
static bool global_strict_mode = false;

/* Device and Profile Interfaces */
static bluetooth_device_t* sBtDevice = nullptr;
const bt_interface_t* sBtInterface = nullptr;
static btmcap_test_interface_t* sMcapTestInterface = nullptr;
static McapTestApp* sMcapTestApp = nullptr;

/* Bluetooth stack states */
static bool global_bt_enabled = false;
static int global_adapter_state = BT_STATE_OFF;
static int global_pair_state = BT_BOND_STATE_NONE;
/************************************************************************************
**  Static functions
************************************************************************************/
static void process_cmd(char* p, bool is_job);

/*******************************************************************************
 ** Misc helper functions
 *******************************************************************************/
static const char* dump_bt_status(const bt_status_t status) {
  switch (status) {
    CASE_RETURN_STR(BT_STATUS_SUCCESS)
    CASE_RETURN_STR(BT_STATUS_FAIL)
    CASE_RETURN_STR(BT_STATUS_NOT_READY)
    CASE_RETURN_STR(BT_STATUS_NOMEM)
    CASE_RETURN_STR(BT_STATUS_BUSY)
    CASE_RETURN_STR(BT_STATUS_UNSUPPORTED)
    default:
      return "unknown status code";
  }
}

/************************************************************************************
**  MCAP Callbacks
************************************************************************************/
static void mcap_ctrl_callback(tMCA_HANDLE handle, tMCA_CL mcl, uint8_t event,
                               tMCA_CTRL* p_data) {
  sMcapTestApp->ControlCallback(handle, mcl, event, p_data);
}

static void mcap_data_cb(tMCA_DL mdl, BT_HDR* p_pkt) {
  printf("%s: mdl=%d, event=%d, len=%d, offset=%d, layer_specific=%d\n",
         __func__, mdl, p_pkt->event, p_pkt->len, p_pkt->offset,
         p_pkt->layer_specific);
  printf("%s: HEXDUMP OF DATA LENGTH %u:\n", __func__, p_pkt->len);
  printf("=========================Begin=========================\n");
  bool newline = false;
  for (int i = 0; i < p_pkt->len; ++i) {
    printf("%02x", p_pkt->data[i]);
    if (i > 0 && (i % 25) == 0) {
      printf("\n");
      newline = true;
    } else {
      printf(" ");
      newline = false;
    }
  }
  if (!newline) printf("\n");
  printf("=========================End===========================\n");
}

/************************************************************************************
**  Shutdown helper functions
************************************************************************************/

static void console_shutdown(void) {
  LOG(INFO) << __func__ << ": Shutdown Fluoride MCAP test app";
  global_main_done = true;
}

/*****************************************************************************
** Android's init.rc does not yet support applying linux capabilities
*****************************************************************************/

#ifndef OS_GENERIC
static void config_permissions(void) {
  struct __user_cap_header_struct header;
  struct __user_cap_data_struct cap[2];

  printf("set_aid_and_cap : pid %d, uid %d gid %d", getpid(), getuid(),
         getgid());

  header.pid = 0;

  prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);

  setuid(AID_BLUETOOTH);
  setgid(AID_BLUETOOTH);

  header.version = _LINUX_CAPABILITY_VERSION_3;

  cap[CAP_TO_INDEX(CAP_NET_RAW)].permitted |= CAP_TO_MASK(CAP_NET_RAW);
  cap[CAP_TO_INDEX(CAP_NET_ADMIN)].permitted |= CAP_TO_MASK(CAP_NET_ADMIN);
  cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].permitted |=
      CAP_TO_MASK(CAP_NET_BIND_SERVICE);
  cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted |= CAP_TO_MASK(CAP_SYS_RAWIO);
  cap[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
  cap[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
  cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].permitted |= CAP_TO_MASK(CAP_WAKE_ALARM);

  cap[CAP_TO_INDEX(CAP_NET_RAW)].effective |= CAP_TO_MASK(CAP_NET_RAW);
  cap[CAP_TO_INDEX(CAP_NET_ADMIN)].effective |= CAP_TO_MASK(CAP_NET_ADMIN);
  cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].effective |=
      CAP_TO_MASK(CAP_NET_BIND_SERVICE);
  cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective |= CAP_TO_MASK(CAP_SYS_RAWIO);
  cap[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
  cap[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
  cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].effective |= CAP_TO_MASK(CAP_WAKE_ALARM);

  capset(&header, &cap[0]);
  setgroups(sizeof(groups) / sizeof(groups[0]), groups);
}
#endif

/*******************************************************************************
 ** Console helper functions
 *******************************************************************************/

void skip_blanks(char** p) {
  while (**p == ' ') (*p)++;
}

uint32_t get_int(char** p, int DefaultValue) {
  uint32_t Value = 0;
  unsigned char UseDefault;

  UseDefault = 1;
  skip_blanks(p);

  while (((**p) <= '9' && (**p) >= '0')) {
    Value = Value * 10 + (**p) - '0';
    UseDefault = 0;
    (*p)++;
  }

  if (UseDefault)
    return DefaultValue;
  else
    return Value;
}

int get_signed_int(char** p, int DefaultValue) {
  int Value = 0;
  unsigned char UseDefault;
  unsigned char NegativeNum = 0;

  UseDefault = 1;
  skip_blanks(p);

  if ((**p) == '-') {
    NegativeNum = 1;
    (*p)++;
  }
  while (((**p) <= '9' && (**p) >= '0')) {
    Value = Value * 10 + (**p) - '0';
    UseDefault = 0;
    (*p)++;
  }

  if (UseDefault)
    return DefaultValue;
  else
    return ((NegativeNum == 0) ? Value : -Value);
}

void get_str(char** p, char* Buffer) {
  skip_blanks(p);

  while (**p != 0 && **p != ' ') {
    *Buffer = **p;
    (*p)++;
    Buffer++;
  }

  *Buffer = 0;
}

uint32_t get_hex_any(char** p, int DefaultValue, unsigned int NumOfNibble) {
  uint32_t Value = 0;
  unsigned char UseDefault;
  // unsigned char   NumOfNibble = 8;  //Since we are returning uint32, max
  // allowed is 4 bytes(8 nibbles).

  UseDefault = 1;
  skip_blanks(p);

  while ((NumOfNibble) &&
         (((**p) <= '9' && (**p) >= '0') || ((**p) <= 'f' && (**p) >= 'a') ||
          ((**p) <= 'F' && (**p) >= 'A'))) {
    if (**p >= 'a')
      Value = Value * 16 + (**p) - 'a' + 10;
    else if (**p >= 'A')
      Value = Value * 16 + (**p) - 'A' + 10;
    else
      Value = Value * 16 + (**p) - '0';
    UseDefault = 0;
    (*p)++;
    NumOfNibble--;
  }

  if (UseDefault)
    return DefaultValue;
  else
    return Value;
}
uint32_t get_hex(char** p, int DefaultValue) {
  return get_hex_any(p, DefaultValue, 8);
}
uint8_t get_hex_byte(char** p, int DefaultValue) {
  return get_hex_any(p, DefaultValue, 2);
}

bool is_cmd(const char* cmd, const char* str) {
  return (strlen(str) == strlen(cmd)) && (strncmp(cmd, str, strlen(str)) == 0);
}

typedef void(console_cmd_handler_t)(char* p);

typedef struct {
  const char* name;
  console_cmd_handler_t* handler;
  const char* help;
  bool is_job;
} cmd_t;

extern const cmd_t console_cmd_list[];
static int console_cmd_maxlen = 0;

static void* cmdjob_handler(void* param) {
  char* job_cmd = (char*)param;
  LOG(INFO) << "cmdjob starting: " << job_cmd;
  process_cmd(job_cmd, true);
  LOG(INFO) << "cmdjob terminating";
  free(job_cmd);
  return nullptr;
}

static int create_cmdjob(char* cmd) {
  CHECK(cmd);
  char* job_cmd = (char*)calloc(1, strlen(cmd) + 1); /* freed in job handler */
  if (job_cmd) {
    strlcpy(job_cmd, cmd, strlen(job_cmd) + 1);
    pthread_t thread_id;
    int ret =
        pthread_create(&thread_id, nullptr, cmdjob_handler, (void*)job_cmd);
    LOG_IF(ERROR, ret != 0) << "Error during pthread_create";
  } else {
    LOG(INFO) << "Cannot Allocate memory for cmdjob: " << cmd;
  }
  return 0;
}

/*******************************************************************************
 ** Load stack lib
 *******************************************************************************/

int HAL_load(void) {
  int err = 0;
  hw_module_t* module;
  hw_device_t* device;
  LOG(INFO) << "Loading HAL library and extensions";
#if defined(OS_GENERIC)
  err = hal_util_load_bt_library((hw_module_t const**)&module);
#else
  err = hw_get_module(BT_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
#endif
  if (!err) {
    err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
    if (!err) {
      sBtDevice = (bluetooth_device_t*)device;
      sBtInterface = sBtDevice->get_bluetooth_interface();
    }
  }
  LOG(INFO) << "HAL library loaded, status: " << strerror(err);
  return err;
}

int HAL_unload(void) {
  int err = 0;
  LOG(INFO) << "Unloading HAL lib";
  sBtInterface = nullptr;
  LOG(INFO) << "HAL library unloaded, status: " << strerror(err);
  return err;
}

/*******************************************************************************
 ** HAL test functions & callbacks
 *******************************************************************************/

void setup_test_env(void) {
  int i = 0;
  while (console_cmd_list[i].name) {
    console_cmd_maxlen =
        MAX(console_cmd_maxlen, (int)strlen(console_cmd_list[i].name));
    i++;
  }
}

void check_return_status(bt_status_t status) {
  if (status != BT_STATUS_SUCCESS) {
    LOG(INFO) << "HAL REQUEST FAILED status : " << status << " ("
              << dump_bt_status(status) << ")";
  } else {
    LOG(INFO) << "HAL REQUEST SUCCESS";
  }
}

static void adapter_state_changed(bt_state_t state) {
  int V1 = 1000, V2 = 2;
  bt_property_t property = {BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, 4, &V1};
  bt_property_t property1 = {BT_PROPERTY_ADAPTER_SCAN_MODE, 2, &V2};
  bt_property_t property2 = {BT_PROPERTY_BDNAME, 6, (void*)"Fluoride_Test"};

  global_adapter_state = state;

  if (state == BT_STATE_ON) {
    global_bt_enabled = true;
    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property1);
    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property);
    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property2);
  } else {
    global_bt_enabled = false;
  }
}

static void adapter_properties_changed(bt_status_t status, int num_properties,
                                       bt_property_t* properties) {
  RawAddress bd_addr;
  if (!properties) {
    printf("properties is null\n");
    return;
  }
  switch (properties->type) {
    case BT_PROPERTY_BDADDR:
      memcpy(bd_addr.address, properties->val,
             MIN((size_t)properties->len, sizeof(bd_addr)));
      LOG(INFO) << "Local Bd Addr = " << bd_addr;
      break;
    default:
      break;
  }
  return;
}

static void discovery_state_changed(bt_discovery_state_t state) {
  LOG(INFO) << "Discovery State Updated: "
            << (state == BT_DISCOVERY_STOPPED ? "STOPPED" : "STARTED");
}

static void pin_request_cb(RawAddress* remote_bd_addr, bt_bdname_t* bd_name,
                           uint32_t cod, bool min_16_digit) {
  bt_pin_code_t pincode = {{0x31, 0x32, 0x33, 0x34}};

  if (BT_STATUS_SUCCESS !=
      sBtInterface->pin_reply(remote_bd_addr, true, 4, &pincode)) {
    LOG(INFO) << "Pin Reply failed";
  }
}

static void ssp_request_cb(RawAddress* remote_bd_addr, bt_bdname_t* bd_name,
                           uint32_t cod, bt_ssp_variant_t pairing_variant,
                           uint32_t pass_key) {
  LOG(INFO) << __func__ << ": device_name:" << bd_name->name
            << ", pairing_variant: " << (int)pairing_variant
            << ", passkey: " << unsigned(pass_key);
  if (BT_STATUS_SUCCESS !=
      sBtInterface->ssp_reply(remote_bd_addr, pairing_variant, true,
                              pass_key)) {
    LOG(ERROR) << "SSP Reply failed";
  }
}

static void bond_state_changed_cb(bt_status_t status,
                                  RawAddress* remote_bd_addr,
                                  bt_bond_state_t state) {
  LOG(INFO) << "Bond State Changed = " << state;
  global_pair_state = state;
}

static void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
                              bt_acl_state_t state) {
  LOG(INFO) << __func__ << ": remote_bd_addr=" << *remote_bd_addr
            << ", acl status=" << (state == BT_ACL_STATE_CONNECTED
                                       ? "ACL Connected"
                                       : "ACL Disconnected");
}

static void dut_mode_recv(uint16_t opcode, uint8_t* buf, uint8_t len) {
  LOG(INFO) << "DUT MODE RECV : NOT IMPLEMENTED";
}

static bt_callbacks_t bt_callbacks = {
    sizeof(bt_callbacks_t),
    adapter_state_changed,
    adapter_properties_changed, /*adapter_properties_cb */
    nullptr,                    /* remote_device_properties_cb */
    nullptr,                    /* device_found_cb */
    discovery_state_changed,    /* discovery_state_changed_cb */
    pin_request_cb,             /* pin_request_cb  */
    ssp_request_cb,             /* ssp_request_cb  */
    bond_state_changed_cb,      /*bond_state_changed_cb */
    acl_state_changed,          /* acl_state_changed_cb */
    nullptr,                    /* thread_evt_cb */
    dut_mode_recv,              /*dut_mode_recv_cb */
    nullptr,                    /* le_test_mode_cb */
    nullptr                     /* energy_info_cb */
};

static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
                           void* data) {
  static timer_t timer;
  static bool timer_created;

  if (!timer_created) {
    struct sigevent sigevent;
    memset(&sigevent, 0, sizeof(sigevent));
    sigevent.sigev_notify = SIGEV_THREAD;
    sigevent.sigev_notify_function = (void (*)(union sigval))cb;
    sigevent.sigev_value.sival_ptr = data;
    timer_create(CLOCK_MONOTONIC, &sigevent, &timer);
    timer_created = true;
  }

  struct itimerspec new_value;
  new_value.it_value.tv_sec = delay_millis / 1000;
  new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000;
  new_value.it_interval.tv_sec = 0;
  new_value.it_interval.tv_nsec = 0;
  timer_settime(timer, 0, &new_value, nullptr);

  return true;
}

static int acquire_wake_lock(const char* lock_name) {
  return BT_STATUS_SUCCESS;
}

static int release_wake_lock(const char* lock_name) {
  return BT_STATUS_SUCCESS;
}

static bt_os_callouts_t callouts = {
    sizeof(bt_os_callouts_t), set_wake_alarm, acquire_wake_lock,
    release_wake_lock,
};

void adapter_init(void) {
  LOG(INFO) << __func__;
  global_status = (bt_status_t)sBtInterface->init(&bt_callbacks);
  if (global_status == BT_STATUS_SUCCESS) {
    global_status = (bt_status_t)sBtInterface->set_os_callouts(&callouts);
  }
  check_return_status(global_status);
}

void adapter_enable(void) {
  LOG(INFO) << __func__;
  if (global_bt_enabled) {
    LOG(INFO) << __func__ << ": Bluetooth is already enabled";
    return;
  }
  global_status = (bt_status_t)sBtInterface->enable(global_strict_mode);
  check_return_status(global_status);
}

void adapter_disable(void) {
  LOG(INFO) << __func__;
  if (!global_bt_enabled) {
    LOG(INFO) << __func__ << ": Bluetooth is already disabled";
    return;
  }
  global_status = (bt_status_t)sBtInterface->disable();
  check_return_status(global_status);
}
void adapter_dut_mode_configure(char* p) {
  LOG(INFO) << __func__;
  if (!global_bt_enabled) {
    LOG(INFO) << __func__
              << ": Bluetooth must be enabled for test_mode to work.";
    return;
  }
  int32_t mode = get_signed_int(&p, -1);  // arg1
  if ((mode != 0) && (mode != 1)) {
    LOG(INFO) << __func__ << "Please specify mode: 1 to enter, 0 to exit";
    return;
  }
  global_status = (bt_status_t)sBtInterface->dut_mode_configure(mode);
  check_return_status(global_status);
}

void adapter_cleanup(void) {
  LOG(INFO) << __func__;
  sBtInterface->cleanup();
}

/*******************************************************************************
 ** Console commands
 *******************************************************************************/

void do_help(char* p) {
  int i = 0;
  char line[128];
  int pos = 0;

  while (console_cmd_list[i].name != nullptr) {
    pos = snprintf(line, sizeof(line), "%s", (char*)console_cmd_list[i].name);
    printf("%s %s\n", (char*)line, (char*)console_cmd_list[i].help);
    i++;
  }
}

void do_quit(char* p) { console_shutdown(); }

/*******************************************************************
 *
 *  BT TEST  CONSOLE COMMANDS
 *
 *  Parses argument lists and passes to API test function
 *
 */

void do_init(char* p) { adapter_init(); }

void do_enable(char* p) { adapter_enable(); }

void do_disable(char* p) { adapter_disable(); }

void do_cleanup(char* p) { adapter_cleanup(); }

/**
 * MCAP API commands
 */
void do_mcap_register(char* p) {
  uint16_t ctrl_psm = get_hex(&p, 0);  // arg1
  uint16_t data_psm = get_hex(&p, 0);  // arg2
  uint16_t sec_mask = get_int(&p, 0);  // arg3
  printf("%s: ctrl_psm=0x%04x, data_psm=0x%04x, sec_mask=0x%04x\n", __func__,
         ctrl_psm, data_psm, sec_mask);
  if (!ctrl_psm || !data_psm) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  sMcapTestApp->Register(ctrl_psm, data_psm, sec_mask, mcap_ctrl_callback);
  printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
}

void do_mcap_deregister(char* p) {
  printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
  sMcapTestApp->Deregister();
  printf("%s: handle=%d\n", __func__, sMcapTestApp->GetHandle());
}

void do_mcap_create_mdep(char* p) {
  int type = get_int(&p, -1);  // arg1
  printf("%s: mcap_handle=%d, type=%d\n", __func__, sMcapTestApp->GetHandle(),
         type);
  bool ret = sMcapTestApp->CreateMdep(type, MCA_NUM_MDLS, mcap_data_cb);
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_delete_mdep(char* p) {
  uint8_t mdep_handle = get_int(&p, 0);
  printf("%s: mcap_handle=%d, mdep_handle=%d\n", __func__,
         sMcapTestApp->GetHandle(), mdep_handle);
  if (!mdep_handle) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMdep* mcap_mdep = sMcapTestApp->FindMdepByHandle(mdep_handle);
  if (!mcap_mdep) {
    LOG(ERROR) << "No MDEP for handle " << (int)mdep_handle;
    return;
  }
  bool ret = mcap_mdep->Delete();
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_connect_mcl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  uint16_t ctrl_psm = get_hex(&p, 0);  // arg2
  uint16_t sec_mask = get_int(&p, 0);  // arg3
  printf("%s: mcap_handle=%d, ctrl_psm=0x%04x, secMask=0x%04x, bd_addr=%s\n",
         __func__, sMcapTestApp->GetHandle(), ctrl_psm, sec_mask, buf);
  if (!ctrl_psm || !valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  bool ret = sMcapTestApp->ConnectMcl(bd_addr, ctrl_psm, sec_mask);
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_disconnect_mcl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  printf("%s: bd_addr=%s\n", __func__, buf);
  if (!valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  bool ret = mcap_mcl->Disconnect();
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_create_mdl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  uint16_t mdep_handle = get_int(&p, 0);  // arg2
  uint16_t data_psm = get_hex(&p, 0);     // arg3
  uint16_t mdl_id = get_int(&p, 0);       // arg4
  uint8_t peer_dep_id = get_int(&p, 0);   // arg5
  uint8_t cfg = get_hex(&p, 0);           // arg6
  int do_not_connect = get_int(&p, 0);    // arg7
  printf(
      "%s: bd_addr=%s, mdep_handle=%d, data_psm=0x%04x, mdl_id=%d,"
      " peer_dep_id=%d, cfg=0x%02x, do_not_connect=%d\n",
      __func__, buf, mdep_handle, data_psm, mdl_id, peer_dep_id, cfg,
      do_not_connect);
  if (!data_psm || !peer_dep_id || !valid_bd_addr || !mdep_handle) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  bool ret = mcap_mcl->CreateMdl(mdep_handle, data_psm, mdl_id, peer_dep_id,
                                 cfg, !do_not_connect);
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_data_channel_config(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  printf("%s: bd_addr=%s\n", __func__, buf);
  if (!valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  bool ret = mcap_mcl->DataChannelConfig();
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_abort_mdl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  printf("%s: bd_addr=%s\n", __func__, buf);
  if (!valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  bool ret = mcap_mcl->AbortMdl();
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_delete_mdl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  uint16_t mdl_id = get_int(&p, 0);  // arg2
  printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
  if (!valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  bool ret = mcap_mcl->DeleteMdl(mdl_id);
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_close_mdl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  uint16_t mdl_id = get_int(&p, 0);  // arg2
  printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
  if (!valid_bd_addr || !mdl_id) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
  if (!mcap_mdl) {
    LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
    return;
  }
  bool ret = mcap_mdl->Close();
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_mcap_reconnect_mdl(char* p) {
  char buf[64];
  get_str(&p, buf);  // arg1
  RawAddress bd_addr;
  bool valid_bd_addr = RawAddress::FromString(buf, bd_addr);
  uint16_t data_psm = get_hex(&p, 0);  // arg1
  uint16_t mdl_id = get_int(&p, 0);    // arg2
  printf("%s: data_psm=0x%04x, mdl_id=%d\n", __func__, data_psm, mdl_id);
  if (!valid_bd_addr) {
    printf("%s: Invalid Parameters\n", __func__);
    return;
  }
  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
  if (!mcap_mcl) {
    LOG(ERROR) << "No MCL for bd_addr " << buf;
    return;
  }
  McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
  if (!mcap_mdl) {
    LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
    return;
  }
  bool ret = mcap_mdl->Reconnect(data_psm);
  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
}

static void do_pairing(char* p) {
  RawAddress bd_addr;
  if (!RawAddress::FromString(p, bd_addr)) {
    LOG(ERROR) << "Invalid Bluetooth address " << p;
    return;
  }
  if (BT_STATUS_SUCCESS !=
      sBtInterface->create_bond(&bd_addr, BT_TRANSPORT_BR_EDR)) {
    LOG(ERROR) << "Failed to Initiate Pairing";
    return;
  }
}

/** CONSOLE COMMAND TABLE */

const cmd_t console_cmd_list[] = {
    /* INTERNAL */
    {"help", do_help, "", 0},
    {"quit", do_quit, "", 0},
    /* API CONSOLE COMMANDS */
    /* Init and Cleanup shall be called automatically */
    {"enable_bluetooth", do_enable, "", 0},
    {"disable_bluetooth", do_disable, "", 0},
    {"pair", do_pairing, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
    {"register", do_mcap_register,
     "ctrl_psm<hex> data_psm<hex> security_mask<0-10>", 0},
    {"deregister", do_mcap_deregister, "", 0},
    {"create_mdep", do_mcap_create_mdep, "type<0-Echo, 1-Normal>", 0},
    {"delete_mdep", do_mcap_delete_mdep, "mdep_handle<int>", 0},
    {"connect_mcl", do_mcap_connect_mcl,
     "BD_ADDR<xx:xx:xx:xx:xx:xx> ctrl_psm<hex> security_mask<0-10>", 0},
    {"disconnect_mcl", do_mcap_disconnect_mcl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
    {"create_mdl", do_mcap_create_mdl,
     "BD_ADDR<xx:xx:xx:xx:xx:xx> mdep_handle<int> data_psm<hex> mdl_id<int> "
     "peer_dep_id<int> cfg<hex> "
     "do_not_connect<0-connect,1-wait_for_data_channel_config>",
     0},
    {"data_channel_config", do_mcap_data_channel_config,
     "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
    {"abort_mdl", do_mcap_abort_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
    {"close_mdl", do_mcap_close_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
     0},
    {"delete_mdl", do_mcap_delete_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
     0},
    {"reconnect_mdl", do_mcap_reconnect_mdl,
     "BD_ADDR<xx:xx:xx:xx:xx:xx> data_psm<hex> mdl_id<int>", 0},
    /* last entry */
    {nullptr, nullptr, "", 0},
};

/** Main console command handler */

static void process_cmd(char* p, bool is_job) {
  char cmd[2048];
  int i = 0;
  char* p_saved = p;

  get_str(&p, cmd);  // arg1

  /* table commands */
  while (console_cmd_list[i].name != nullptr) {
    if (is_cmd(cmd, console_cmd_list[i].name)) {
      if (!is_job && console_cmd_list[i].is_job)
        create_cmdjob(p_saved);
      else {
        console_cmd_list[i].handler(p);
      }
      return;
    }
    i++;
  }
  LOG(ERROR) << "Unknown command: " << p_saved;
  do_help(nullptr);
}

int main(int argc, char* argv[]) {
  setbuf(stdout, NULL);
#if !defined(OS_GENERIC)
  config_permissions();
#endif
  LOG(INFO) << "Fluoride MCAP test app is starting";

  if (HAL_load() < 0) {
    fprintf(stderr, "%s: HAL failed to initialize, exit\n", __func__);
    unlink(PID_FILE);
    exit(0);
  }

  setup_test_env();

  /* Automatically perform the init */
  adapter_init();
  sleep(2);
  adapter_enable();
  sleep(2);
  sMcapTestInterface =
      (btmcap_test_interface_t*)sBtInterface->get_profile_interface(
          BT_TEST_INTERFACE_MCAP_ID);
  sMcapTestInterface->init();
  sMcapTestApp = new McapTestApp(sMcapTestInterface);

  /* Main loop */
  char line[2048];
  while (!global_main_done) {
    memset(line, '\0', sizeof(line));
    /* command prompt */
    printf(">");
    fflush(stdout);
    fgets(line, sizeof(line), stdin);
    if (line[0] != '\0') {
      /* Remove line feed */
      line[strlen(line) - 1] = 0;
      if (strlen(line) != 0) process_cmd(line, false);
    }
  }
  adapter_cleanup();
  HAL_unload();
  LOG(INFO) << "Fluoride MCAP test app is terminating";

  return 0;
}
