// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/bluetooth/gatt_service.h"

#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/notimplemented.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_local_gatt_characteristic.h"
#include "device/bluetooth/device.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"

namespace {

std::string_view GattErrorCodeToString(
    device::BluetoothGattService::GattErrorCode error_code) {
  switch (error_code) {
    case device::BluetoothGattService::GattErrorCode::kUnknown:
      return "Unknown";
    case device::BluetoothGattService::GattErrorCode::kFailed:
      return "Failed";
    case device::BluetoothGattService::GattErrorCode::kInProgress:
      return "In Progress";
    case device::BluetoothGattService::GattErrorCode::kInvalidLength:
      return "Invalid Length";
    case device::BluetoothGattService::GattErrorCode::kNotPermitted:
      return "Not Permitted";
    case device::BluetoothGattService::GattErrorCode::kNotAuthorized:
      return "Not Authorized";
    case device::BluetoothGattService::GattErrorCode::kNotPaired:
      return "Not Paired";
    case device::BluetoothGattService::GattErrorCode::kNotSupported:
      return "Not Supported";
  };
}

}  // namespace

namespace bluetooth {

GattService::GattService(
    mojo::PendingReceiver<mojom::GattService> pending_gatt_service_receiver,
    mojo::PendingRemote<mojom::GattServiceObserver> pending_observer_remote,
    const device::BluetoothUUID& service_id,
    scoped_refptr<device::BluetoothAdapter> adapter,
    base::OnceCallback<void(device::BluetoothUUID)> on_gatt_service_invalidated)
    : on_gatt_service_invalidated_(std::move(on_gatt_service_invalidated)),
      service_id_(service_id),
      observer_remote_(std::move(pending_observer_remote)),
      adapter_(std::move(adapter)) {
  receiver_.Bind(std::move(pending_gatt_service_receiver));
  receiver_.set_disconnect_handler(
      base::BindOnce(&GattService::OnMojoDisconnect, base::Unretained(this)));
  observer_remote_.set_disconnect_handler(
      base::BindOnce(&GattService::OnMojoDisconnect, base::Unretained(this)));

  gatt_service_identifier_ = adapter_
                                 ->CreateLocalGattService(
                                     /*uuid=*/service_id,
                                     /*is_primary=*/true,
                                     /*delegate=*/this)
                                 ->GetIdentifier();
}

GattService::~GattService() = default;

void GattService::CreateCharacteristic(
    const device::BluetoothUUID& characteristic_uuid,
    const device::BluetoothGattCharacteristic::Permissions& permissions,
    const device::BluetoothGattCharacteristic::Properties& properties,
    CreateCharacteristicCallback callback) {
  device::BluetoothLocalGattService* service =
      adapter_->GetGattService(gatt_service_identifier_);
  if (!service) {
    LOG(WARNING) << __func__ << ": expected local GATT service at service id = "
                 << service_id_.canonical_value() << " does not exist.";
    std::move(callback).Run(/*success=*/false);
    return;
  }

  // Otherwise, attempt  to create the characteristic and add it to this GATT
  // server.
  auto created_characteristic = service->CreateCharacteristic(
      /*uuid=*/characteristic_uuid,
      /*properties=*/properties,
      /*permissions=*/permissions);
  if (!created_characteristic) {
    LOG(WARNING) << __func__
                 << ": failure to create a characteristic at uuid = "
                 << characteristic_uuid.canonical_value();
    std::move(callback).Run(/*success=*/false);
    return;
  }

  characteristic_uuids_.insert(characteristic_uuid);
  std::move(callback).Run(/*success=*/true);
}

void GattService::Register(RegisterCallback callback) {
  device::BluetoothLocalGattService* service =
      adapter_->GetGattService(gatt_service_identifier_);
  if (!service) {
    LOG(WARNING) << __func__ << ": local GATT service destroyed.";
    std::move(callback).Run(
        device::BluetoothGattService::GattErrorCode::kFailed);
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  service->Register(/*callback=*/base::BindOnce(
                        &GattService::OnRegisterSuccess, base::Unretained(this),
                        std::move(split_callback.first)),
                    /*error_callback=*/base::BindOnce(
                        &GattService::OnRegisterFailure, base::Unretained(this),
                        std::move(split_callback.second)));
}

void GattService::OnCharacteristicReadRequest(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattCharacteristic* characteristic,
    int offset,
    ValueCallback callback) {
  CHECK(characteristic);
  CHECK(characteristic_uuids_.contains(characteristic->GetUUID()));

  observer_remote_->OnLocalCharacteristicRead(
      /*remote_device=*/Device::ConstructDeviceInfoStruct(device),
      /*characteristic_uuid=*/characteristic->GetUUID(),
      /*service_uuid=*/service_id_,
      /*offset=*/offset,
      /*callback=*/
      base::BindOnce(&GattService::OnLocalCharacteristicReadResponse,
                     base::Unretained(this), std::move(callback)));
}

void GattService::OnCharacteristicWriteRequest(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value,
    int offset,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  NOTIMPLEMENTED();
}

void GattService::OnCharacteristicPrepareWriteRequest(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value,
    int offset,
    bool has_subsequent_request,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  NOTIMPLEMENTED();
}

void GattService::OnDescriptorReadRequest(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattDescriptor* descriptor,
    int offset,
    ValueCallback callback) {
  NOTIMPLEMENTED();
}

void GattService::OnDescriptorWriteRequest(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattDescriptor* descriptor,
    const std::vector<uint8_t>& value,
    int offset,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  NOTIMPLEMENTED();
}

void GattService::OnNotificationsStart(
    const device::BluetoothDevice* device,
    device::BluetoothGattCharacteristic::NotificationType notification_type,
    const device::BluetoothLocalGattCharacteristic* characteristic) {
  NOTIMPLEMENTED();
}

void GattService::OnNotificationsStop(
    const device::BluetoothDevice* device,
    const device::BluetoothLocalGattCharacteristic* characteristic) {
  NOTIMPLEMENTED();
}

void GattService::OnLocalCharacteristicReadResponse(
    ValueCallback callback,
    mojom::LocalCharacteristicReadResultPtr read_result) {
  if (read_result->is_error_code()) {
    LOG(WARNING) << __func__ << ": failed due to error: "
                 << GattErrorCodeToString(read_result->get_error_code());
    std::move(callback).Run(
        /*error_code=*/read_result->get_error_code(),
        /*data=*/std::vector<uint8_t>());
    return;
  }

  CHECK(read_result->is_data());
  std::move(callback).Run(
      /*error_code=*/std::nullopt,
      /*data=*/read_result->get_data());
}

void GattService::OnMojoDisconnect() {
  device::BluetoothLocalGattService* service =
      adapter_->GetGattService(gatt_service_identifier_);
  if (!service) {
    LOG(WARNING) << __func__ << ": local GATT service does not exist.";
  } else {
    service->Delete();
  }

  receiver_.reset();

  // This call needs to be the last in the `OnMojoDisconnect()` logic because
  // this `GattService` is expected to be destroyed after calling
  // `on_gatt_service_invalidated_`.
  std::move(on_gatt_service_invalidated_).Run(service_id_);
}

void GattService::OnRegisterSuccess(RegisterCallback callback) {
  VLOG(1) << __func__;
  std::move(callback).Run(/*error_code=*/std::nullopt);
}

void GattService::OnRegisterFailure(
    RegisterCallback callback,
    device::BluetoothGattService::GattErrorCode error_code) {
  LOG(WARNING) << __func__ << ": failed due to error: "
               << GattErrorCodeToString(error_code);
  std::move(callback).Run(error_code);
}

}  // namespace bluetooth
