/******************************************************************************
 *
 *  Copyright 2014 Google, Inc.
 *
 *  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.
 *
 ******************************************************************************/

#define LOG_TAG "bt_stack_manager"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
#include <hardware/bluetooth.h>

#include <cstdlib>
#include <cstring>

#include "bta/include/bta_ras_api.h"
#include "btcore/include/module.h"
#include "btcore/include/osi_module.h"
#include "btif/include/btif_api.h"
#include "btif/include/btif_common.h"
#include "btif/include/core_callbacks.h"
#include "btif/include/stack_manager_t.h"
#include "common/message_loop_thread.h"
#include "main/shim/shim.h"
#include "stack/include/acl_api.h"
#include "stack/include/btm_client_interface.h"
#include "stack/include/main_thread.h"

// Temp includes
#include "bta/sys/bta_sys.h"
#include "btif/include/btif_config.h"
#include "btif/include/btif_profile_queue.h"
#include "device/include/device_iot_config.h"
#include "internal_include/bt_target.h"
#include "stack/include/gatt_api.h"
#include "stack/include/l2cap_module.h"
#include "stack/include/port_api.h"
#include "stack/sdp/sdpint.h"
#if (BNEP_INCLUDED == TRUE)
#include "stack/include/bnep_api.h"
#endif
#include "stack/include/gap_api.h"
#if (PAN_INCLUDED == TRUE)
#include "stack/include/pan_api.h"
#endif
#if (HID_HOST_INCLUDED == TRUE)
#include "stack/include/hidh_api.h"
#endif
#include "bta/dm/bta_dm_int.h"
#include "device/include/interop.h"
#include "internal_include/stack_config.h"
#include "os/system_properties.h"
#include "rust/src/core/ffi/module.h"
#include "stack/btm/btm_ble_int.h"
#include "stack/include/ais_api.h"
#include "stack/include/smp_api.h"

// Validate or respond to various conditional compilation flags

// Once BTA_PAN_INCLUDED is no longer exposed via bt_target.h
// this check and error statement may be removed.
static_assert(BTA_PAN_INCLUDED,
              "#define BTA_PAN_INCLUDED preprocessor compilation flag is unsupported"
              "  Pan profile is always included in the bluetooth stack"
              "*** Conditional Compilation Directive error");

// Once PAN_SUPPORTS_ROLE_NAP is no longer exposed via bt_target.h
// this check and error statement may be removed.
static_assert(PAN_SUPPORTS_ROLE_NAP,
              "#define PAN_SUPPORTS_ROLE_NAP preprocessor compilation flag is unsupported"
              "  Pan profile always supports network access point in the bluetooth stack"
              "*** Conditional Compilation Directive error");

// Once PAN_SUPPORTS_ROLE_PANU is no longer exposed via bt_target.h
// this check and error statement may be removed.
static_assert(PAN_SUPPORTS_ROLE_PANU,
              "#define PAN_SUPPORTS_ROLE_PANU preprocessor compilation flag is "
              "unsupported"
              "  Pan profile always supports user as a client in the bluetooth stack"
              "*** Conditional Compilation Directive error");

// Once BTA_HH_INCLUDED is no longer exposed via bt_target.h
// this check and error statement may be removed.
static_assert(BTA_HH_INCLUDED,
              "#define BTA_HH_INCLUDED preprocessor compilation flag is "
              "unsupported"
              "  Host interface device profile is always enabled in the bluetooth stack"
              "*** Conditional Compilation Directive error");

// TODO(b/369381361) Enfore -Wmissing-prototypes
#pragma GCC diagnostic ignored "-Wmissing-prototypes"

void BTA_dm_on_hw_on();
void BTA_dm_on_hw_off();

using bluetooth::common::MessageLoopThread;
using bluetooth::log::error;
using bluetooth::log::fatal;
using bluetooth::log::info;
using bluetooth::log::warn;

static MessageLoopThread management_thread("bt_stack_manager_thread");

// If initialized, any of the bluetooth API functions can be called.
// (e.g. turning logging on and off, enabling/disabling the stack, etc)
static bool stack_is_initialized;
// If running, the stack is fully up and able to bluetooth.
static bool stack_is_running;

static void event_init_stack(std::promise<void> promise, bluetooth::core::CoreInterface* interface);
static void event_start_up_stack(bluetooth::core::CoreInterface* interface,
                                 ProfileStartCallback startProfiles,
                                 ProfileStopCallback stopProfiles);
static void event_shut_down_stack(ProfileStopCallback stopProfiles);
static void event_clean_up_stack(std::promise<void> promise, ProfileStopCallback stopProfiles);
static void event_start_up_rust_module(std::promise<void> promise);
static void event_shut_down_rust_module();

static void event_signal_stack_up(void* context);
static void event_signal_stack_down(void* context);

static bluetooth::core::CoreInterface* interfaceToProfiles;

bluetooth::core::CoreInterface* GetInterfaceToProfiles() { return interfaceToProfiles; }

// Unvetted includes/imports, etc which should be removed or vetted in the
// future
static future_t* hack_future;
// End unvetted section

// Interface functions

static void init_stack(bluetooth::core::CoreInterface* interface) {
  // This is a synchronous process. Post it to the thread though, so
  // state modification only happens there. Using the thread to perform
  // all stack operations ensures that the operations are done serially
  // and do not overlap.
  std::promise<void> promise;
  auto future = promise.get_future();
  management_thread.DoInThread(FROM_HERE, base::BindOnce(event_init_stack, std::move(promise),
                                                         base::Unretained(interface)));
  future.wait();
}

static void start_up_stack_async(bluetooth::core::CoreInterface* interface,
                                 ProfileStartCallback startProfiles,
                                 ProfileStopCallback stopProfiles) {
  management_thread.DoInThread(
          FROM_HERE, base::BindOnce(event_start_up_stack, interface, startProfiles, stopProfiles));
}

static void shut_down_stack_async(ProfileStopCallback stopProfiles) {
  management_thread.DoInThread(FROM_HERE, base::BindOnce(event_shut_down_stack, stopProfiles));
}

static void clean_up_stack(ProfileStopCallback stopProfiles) {
  // This is a synchronous process. Post it to the thread though, so
  // state modification only happens there.
  std::promise<void> promise;
  auto future = promise.get_future();
  management_thread.DoInThread(
          FROM_HERE, base::BindOnce(event_clean_up_stack, std::move(promise), stopProfiles));

  auto status = future.wait_for(std::chrono::milliseconds(
          bluetooth::os::GetSystemPropertyUint32("bluetooth.cleanup_timeout",
                                                 /* default_value = */ 1000)));
  if (status == std::future_status::ready) {
    management_thread.ShutDown();
  } else {
    error("cleanup could not be completed in time, abandon it");
  }
}

static void start_up_rust_module_async(std::promise<void> promise) {
  management_thread.DoInThread(FROM_HERE,
                               base::BindOnce(event_start_up_rust_module, std::move(promise)));
}

static void shut_down_rust_module_async() {
  management_thread.DoInThread(FROM_HERE, base::BindOnce(event_shut_down_rust_module));
}

static bool get_stack_is_running() { return stack_is_running; }

// Internal functions
extern const module_t bt_utils_module;
extern const module_t btif_config_module;
extern const module_t gd_shim_module;
extern const module_t interop_module;
extern const module_t osi_module;
extern const module_t rust_module;
extern const module_t stack_config_module;
extern const module_t device_iot_config_module;

struct module_lookup {
  const char* name;
  const module_t* module;
};

const struct module_lookup module_table[] = {
        {BTIF_CONFIG_MODULE, &btif_config_module},
        {GD_SHIM_MODULE, &gd_shim_module},
        {INTEROP_MODULE, &interop_module},
        {OSI_MODULE, &osi_module},
        {RUST_MODULE, &rust_module},
        {STACK_CONFIG_MODULE, &stack_config_module},
        {DEVICE_IOT_CONFIG_MODULE, &device_iot_config_module},
        {NULL, NULL},
};

inline const module_t* get_local_module(const char* name) {
  size_t len = strlen(name);

  for (const struct module_lookup* l = module_table; l->module; l++) {
    if (strncmp(l->name, name, len) == 0) {
      return l->module;
    }
  }

  fatal("Cannot find module {}, aborting", name);
  return nullptr;
}

static void init_stack_internal(bluetooth::core::CoreInterface* interface) {
  // all callbacks out of libbluetooth-core happen via this interface
  interfaceToProfiles = interface;

  module_management_start();

  main_thread_start_up();

  module_init(get_local_module(DEVICE_IOT_CONFIG_MODULE));
  module_init(get_local_module(OSI_MODULE));
  module_start_up(get_local_module(GD_SHIM_MODULE));
  module_init(get_local_module(BTIF_CONFIG_MODULE));
  btif_init_bluetooth();

  module_init(get_local_module(INTEROP_MODULE));
  module_init(get_local_module(STACK_CONFIG_MODULE));

  // stack init is synchronous, so no waiting necessary here
  stack_is_initialized = true;
}

// Synchronous function to initialize the stack
static void event_init_stack(std::promise<void> promise,
                             bluetooth::core::CoreInterface* interface) {
  info("is initializing the stack");

  if (stack_is_initialized) {
    info("found the stack already in initialized state");
  } else {
    init_stack_internal(interface);
  }

  info("finished");

  promise.set_value();
}

static void ensure_stack_is_initialized(bluetooth::core::CoreInterface* interface) {
  if (!stack_is_initialized) {
    warn("found the stack was uninitialized. Initializing now.");
    // No future needed since we are calling it directly
    init_stack_internal(interface);
  }
}

// Synchronous function to start up the stack
static void event_start_up_stack(bluetooth::core::CoreInterface* interface,
                                 ProfileStartCallback startProfiles,
                                 ProfileStopCallback stopProfiles) {
  if (stack_is_running) {
    info("stack already brought up");
    return;
  }

  ensure_stack_is_initialized(interface);

  info("is bringing up the stack");
  future_t* local_hack_future = future_new();
  hack_future = local_hack_future;

  info("Gd shim module enabled");
  get_btm_client_interface().lifecycle.btm_init();
  module_start_up(get_local_module(BTIF_CONFIG_MODULE));

  l2c_init();
  sdp_init();
  gatt_init();
  SMP_Init(get_btm_client_interface().security.BTM_GetSecurityMode());
  get_btm_client_interface().lifecycle.btm_ble_init();

  RFCOMM_Init();
  GAP_Init();
  AIS_Init();

  startProfiles();

  bta_sys_init();

  btif_init_ok();
  BTA_dm_init();
  bta_dm_enable(btif_dm_sec_evt, btif_dm_acl_evt);

  btm_acl_device_down();
  get_btm_client_interface().lifecycle.BTM_reset_complete();

  BTA_dm_on_hw_on();

  if (future_await(local_hack_future) != FUTURE_SUCCESS) {
    error("failed to start up the stack");
    stack_is_running = true;  // So stack shutdown actually happens
    event_shut_down_stack(stopProfiles);
    return;
  }

  if (!com::android::bluetooth::flags::scan_manager_refactor()) {
    info("Starting rust module");
    module_start_up(get_local_module(RUST_MODULE));
  }
  if (com::android::bluetooth::flags::channel_sounding_in_stack()) {
    bluetooth::ras::GetRasServer()->Initialize();
    bluetooth::ras::GetRasClient()->Initialize();
  }

  stack_is_running = true;
  info("finished");
  do_in_jni_thread(base::BindOnce(event_signal_stack_up, nullptr));
}

// Synchronous function to shut down the stack
static void event_shut_down_stack(ProfileStopCallback stopProfiles) {
  if (!stack_is_running) {
    info("stack is already brought down");
    return;
  }

  info("is bringing down the stack");
  future_t* local_hack_future = future_new();
  hack_future = local_hack_future;
  stack_is_running = false;

  if (!com::android::bluetooth::flags::scan_manager_refactor()) {
    info("Stopping rust module");
    module_shut_down(get_local_module(RUST_MODULE));
  }

  do_in_main_thread(base::BindOnce(&btm_ble_scanner_cleanup));

  btif_dm_on_disable();
  stopProfiles();

  do_in_main_thread(base::BindOnce(bta_dm_disable));

  btif_dm_cleanup();

  future_await(local_hack_future);
  local_hack_future = future_new();
  hack_future = local_hack_future;

  bta_sys_disable();
  BTA_dm_on_hw_off();

  module_shut_down(get_local_module(BTIF_CONFIG_MODULE));
  module_shut_down(get_local_module(DEVICE_IOT_CONFIG_MODULE));

  future_await(local_hack_future);

  gatt_free();
  sdp_free();
  l2c_free();
  get_btm_client_interface().lifecycle.btm_ble_free();

  get_btm_client_interface().lifecycle.btm_free();

  hack_future = future_new();
  do_in_jni_thread(base::BindOnce(event_signal_stack_down, nullptr));
  future_await(hack_future);
  info("finished");
}

static void event_start_up_rust_module(std::promise<void> promise) {
  info("is bringing up the Rust module");
  module_start_up(get_local_module(RUST_MODULE));
  promise.set_value();
  info("finished");
}

static void event_shut_down_rust_module() {
  info("is bringing down the Rust module");
  module_shut_down(get_local_module(RUST_MODULE));
  info("finished");
}

static void ensure_stack_is_not_running(ProfileStopCallback stopProfiles) {
  if (stack_is_running) {
    warn("found the stack was still running. Bringing it down now.");
    event_shut_down_stack(stopProfiles);
  }
}

// Synchronous function to clean up the stack
static void event_clean_up_stack(std::promise<void> promise, ProfileStopCallback stopProfiles) {
  if (!stack_is_initialized) {
    info("found the stack already in a clean state");
    goto cleanup;
  }

  ensure_stack_is_not_running(stopProfiles);

  info("is cleaning up the stack");
  stack_is_initialized = false;

  btif_cleanup_bluetooth();

  module_clean_up(get_local_module(STACK_CONFIG_MODULE));
  module_clean_up(get_local_module(INTEROP_MODULE));

  module_clean_up(get_local_module(BTIF_CONFIG_MODULE));
  module_clean_up(get_local_module(DEVICE_IOT_CONFIG_MODULE));

  info("Gd shim module disabled");
  module_shut_down(get_local_module(GD_SHIM_MODULE));

  module_clean_up(get_local_module(OSI_MODULE));

  main_thread_shut_down();

  module_management_stop();
  info("finished");

cleanup:
  promise.set_value();
}

static void event_signal_stack_up(void* /* context */) {
  // Notify BTIF connect queue that we've brought up the stack. It's
  // now time to dispatch all the pending profile connect requests.
  btif_queue_connect_next();
  GetInterfaceToProfiles()->events->invoke_adapter_state_changed_cb(BT_STATE_ON);
}

static void event_signal_stack_down(void* /* context */) {
  GetInterfaceToProfiles()->events->invoke_adapter_state_changed_cb(BT_STATE_OFF);
  future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);
}

static void ensure_manager_initialized() {
  if (management_thread.IsRunning()) {
    return;
  }

  management_thread.StartUp();
  if (!management_thread.IsRunning()) {
    error("unable to start stack management thread");
    return;
  }
}

static const stack_manager_t interface = {
        init_stack,          start_up_stack_async,       shut_down_stack_async,
        clean_up_stack,      start_up_rust_module_async, shut_down_rust_module_async,
        get_stack_is_running};

const stack_manager_t* stack_manager_get_interface() {
  ensure_manager_initialized();
  return &interface;
}

future_t* stack_manager_get_hack_future() { return hack_future; }

namespace bluetooth {
namespace legacy {
namespace testing {

void set_interface_to_profiles(bluetooth::core::CoreInterface* interfaceToProfiles) {
  ::interfaceToProfiles = interfaceToProfiles;
}

}  // namespace testing
}  // namespace legacy
}  // namespace bluetooth
