/*
 * Copyright 2025 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.
 */

#define LOG_TAG "bluetooth_hal.router_client_agent"

#include "bluetooth_hal/hci_router_client_agent.h"

#include <algorithm>
#include <mutex>
#include <unordered_set>

#include "android-base/logging.h"
#include "bluetooth_hal/hal_packet.h"
#include "bluetooth_hal/hal_types.h"

namespace bluetooth_hal {
namespace hci {

class HciRouterClientAgentImpl : public HciRouterClientAgent {
 public:
  HciRouterClientAgentImpl()
      : current_state_(HalState::kShutdown),
        is_bluetooth_chip_ready_(false),
        is_bluetooth_enabled_(false) {};
  bool RegisterClient(HciRouterClientCallback* callback) override;
  bool UnregisterClient(HciRouterClientCallback* callback) override;
  MonitorMode DispatchPacketToClients(const HalPacket& packet) override;
  void NotifyHalStateChange(HalState new_state, HalState old_state) override;
  bool IsBluetoothEnabled() override;
  bool IsBluetoothChipReady() override;

 private:
  void HandleBluetoothEnable(const HalPacket& packet);
  void ForEachClient(std::function<void(HciRouterClientCallback*)> action);

  std::recursive_mutex mutex_;
  HalState current_state_;
  bool is_bluetooth_chip_ready_;
  bool is_bluetooth_enabled_;
  std::unordered_set<HciRouterClientCallback*> router_clients_;
};

HciRouterClientAgent& HciRouterClientAgent::GetAgent() {
  static HciRouterClientAgentImpl agent;
  return agent;
}

bool HciRouterClientAgentImpl::RegisterClient(HciRouterClientCallback* client) {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);
  if (router_clients_.count(client) > 0) {
    LOG(WARNING) << "callback already registered!";
    return false;
  }
  router_clients_.insert(client);

  if (IsBluetoothChipReady()) {
    client->OnBluetoothChipReady();
  }
  if (IsBluetoothEnabled()) {
    client->OnBluetoothEnabled();
  }
  return true;
}

bool HciRouterClientAgentImpl::UnregisterClient(
    HciRouterClientCallback* callback) {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);
  if (router_clients_.erase(callback) == 0) {
    LOG(WARNING) << "callback was not registered!";
    return false;
  }
  return true;
}

MonitorMode HciRouterClientAgentImpl::DispatchPacketToClients(
    const HalPacket& packet) {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);
  if (!IsBluetoothEnabled()) {
    // Look for HCI_RESET complete event if Bluetooth is not enabled.
    HandleBluetoothEnable(packet);
  }

  MonitorMode result = MonitorMode::kNone;
  ForEachClient([&](HciRouterClientCallback* client) {
    MonitorMode mode = client->OnPacketCallback(packet);
    result = std::max(result, mode);
  });
  return result;
}

void HciRouterClientAgentImpl::NotifyHalStateChange(HalState new_state,
                                                    HalState old_state) {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);

#ifndef UNIT_TEST
  if (current_state_ > old_state) {
    LOG(FATAL) << __func__
               << " (old_state, current_state_in_client) is mismatched! "
                  "[ old_state("
               << HalStateToString(old_state) << ":"
               << static_cast<int>(old_state) << ") -> new_state("
               << HalStateToString(new_state) << ":"
               << static_cast<int>(new_state)
               << ") ], current_state_in_client: "
               << HalStateToString(current_state_) << ":"
               << static_cast<int>(current_state_);
    return;
  }
#endif

  current_state_ = new_state;

  switch (new_state) {
    case HalState::kBtChipReady:
      if (!is_bluetooth_chip_ready_) {
        ForEachClient([](HciRouterClientCallback* client) {
          client->OnBluetoothChipReady();
        });
      }
      if (is_bluetooth_enabled_) {
        ForEachClient([](HciRouterClientCallback* client) {
          client->OnBluetoothDisabled();
        });
      }
      is_bluetooth_chip_ready_ = true;
      is_bluetooth_enabled_ = false;
      break;
    case HalState::kRunning:
      if (!is_bluetooth_chip_ready_) {
        ForEachClient([](HciRouterClientCallback* client) {
          client->OnBluetoothChipReady();
        });
      }
      // We do not handle is_bluetooth_enabled_ here because the clients have to
      // wait for a HCI_RESET before they can send packets to the chip.
      is_bluetooth_chip_ready_ = true;
      break;
    default:
      if (is_bluetooth_chip_ready_) {
        ForEachClient([](HciRouterClientCallback* client) {
          client->OnBluetoothChipClosed();
        });
      }
      if (is_bluetooth_enabled_) {
        ForEachClient([](HciRouterClientCallback* client) {
          client->OnBluetoothDisabled();
        });
      }
      is_bluetooth_chip_ready_ = false;
      is_bluetooth_enabled_ = false;
      break;
  }

  ForEachClient([&](HciRouterClientCallback* client) {
    client->OnHalStateChanged(new_state, old_state);
  });
}

bool HciRouterClientAgentImpl::IsBluetoothEnabled() {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);
  return is_bluetooth_enabled_;
}

bool HciRouterClientAgentImpl::IsBluetoothChipReady() {
  std::scoped_lock<std::recursive_mutex> lock(mutex_);
  return is_bluetooth_chip_ready_;
}

void HciRouterClientAgentImpl::HandleBluetoothEnable(const HalPacket& packet) {
  if (current_state_ == HalState::kRunning &&
      packet.GetCommandOpcodeFromGeneratedEvent() ==
          static_cast<uint16_t>(CommandOpCode::kHciReset) &&
      packet.GetCommandCompleteEventResult() ==
          static_cast<uint8_t>(EventResultCode::kSuccess)) {
    // Inform the client that Bluetooth has enabled after a HCI_RESET command is
    // sent in kRunning state.
    is_bluetooth_enabled_ = true;
    ForEachClient(
        [](HciRouterClientCallback* client) { client->OnBluetoothEnabled(); });
  }
}

void HciRouterClientAgentImpl::ForEachClient(
    std::function<void(HciRouterClientCallback*)> action) {
  for (auto client : router_clients_) {
    if (client == nullptr) {
      LOG(WARNING) << __func__
                   << ": null router client callback in the registration list!";
      continue;
    }
    action(client);
  }
}

}  // namespace hci
}  // namespace bluetooth_hal
