/*
 * Copyright (c) 2024-2026 Huawei Device Co., Ltd.
 * 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 "bluetooth_ble_clientDevice.h"

#include "bluetooth_ble_common.h"
#include "bluetooth_errorcode.h"
#include "bluetooth_host.h"
#include "bluetooth_log.h"
#include "cj_lambda.h"

namespace OHOS {
namespace CJSystemapi {
namespace CJBluetoothBle {
using Bluetooth::BluetoothHost;
using Bluetooth::BT_ERR_INTERNAL_ERROR;
using Bluetooth::BT_NO_ERROR;
using Bluetooth::BT_TRANSPORT_BLE;
using Bluetooth::GATT_TRANSPORT_TYPE_LE;
using Bluetooth::UUID;

int32_t FfiClientDevice::Connect()
{
    if (client_ == nullptr) {
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->Connect(callback_, false, GATT_TRANSPORT_TYPE_LE);
}

int32_t FfiClientDevice::Disconnect()
{
    if (client_ == nullptr) {
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->Disconnect();
}

int32_t FfiClientDevice::Close()
{
    if (client_ == nullptr) {
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->Close();
}

std::string FfiClientDevice::GetDeviceName(int32_t* errCode)
{
    std::string deviceName = "";
    std::string deviceAddr = GetGattClientDeviceId();
    *errCode = BluetoothHost::GetDefaultHost().GetRemoteDevice(deviceAddr, BT_TRANSPORT_BLE).GetDeviceName(deviceName);

    HILOGI("err: %{public}d, deviceName: %{private}s", *errCode, deviceName.c_str());
    return deviceName;
}

int32_t FfiClientDevice::GetServices(CArrGattService& service)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    int ret = client_->DiscoverServices();
    if (ret == BT_NO_ERROR) {
        HILOGI("start get services");
        std::vector<GattService> nativeServices = client_->GetService();
        service = Convert2CArrGattService(nativeServices);
    }
    return ret;
}

static GattCharacteristic* GetCharacteristic(
    const std::shared_ptr<GattClient>& client, const UUID& serviceUuid, const UUID& characterUuid)
{
    GattCharacteristic* character = nullptr;
    if (client) {
        auto service = client->GetService(serviceUuid);
        if (service.has_value()) {
            character = service->get().GetCharacteristic(characterUuid);
        }
    }
    return character;
}

static GattCharacteristic* GetGattcCharacteristic(
    const std::shared_ptr<GattClient>& client, NativeBLECharacteristic& nativeCharacteristic)
{
    GattCharacteristic* character = GetCharacteristic(client, UUID::FromString(nativeCharacteristic.serviceUuid),
        UUID::FromString(nativeCharacteristic.characteristicUuid));
    if (character) {
        character->SetValue(
            nativeCharacteristic.characteristicValue.head, nativeCharacteristic.characteristicValue.size);
    }
    return character;
}

int32_t FfiClientDevice::ReadCharacteristicValue(NativeBLECharacteristic& characteristic, void (*callback)())
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);

    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto readCharacteristicFunc = CJLambda::Create(reinterpret_cast<void (*)(RetNativeBLECharacteristic)>(callback));
    callback_->RegisterReadCharacteristicCallback(readCharacteristicFunc);
    return client_->ReadCharacteristic(*character);
}

static GattDescriptor* GetGattcDescriptor(
    const std::shared_ptr<GattClient>& client, const NativeBLEDescriptor& nativeDescriptor)
{
    GattDescriptor* descriptor = nullptr;
    if (client) {
        auto* character = GetCharacteristic(client, UUID::FromString(nativeDescriptor.serviceUuid),
            UUID::FromString(nativeDescriptor.characteristicUuid));
        if (character == nullptr) {
            HILOGE("character is nullptr");
            return nullptr;
        }
        descriptor = character->GetDescriptor(UUID::FromString(nativeDescriptor.descriptorUuid));
        if (descriptor) {
            descriptor->SetValue(nativeDescriptor.descriptorValue.head, nativeDescriptor.descriptorValue.size);
        }
    }
    return descriptor;
}

int32_t FfiClientDevice::ReadDescriptorValue(NativeBLEDescriptor& inputDescriptor, void (*callback)())
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattDescriptor* descriptor = GetGattcDescriptor(client_, inputDescriptor);

    if (descriptor == nullptr) {
        HILOGE("descriptor is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto readDescriptorFunc = CJLambda::Create(reinterpret_cast<void (*)(RetNativeBLEDescriptor)>(callback));
    callback_->RegisterReadDescriptorCallback(readDescriptorFunc);
    return client_->ReadDescriptor(*descriptor);
}

int32_t FfiClientDevice::WriteCharacteristicValue(
    NativeBLECharacteristic characteristic, int32_t writeType, void (*callback)())
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);
    std::vector<uint8_t> value {};

    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto writeCharacteristicFunc = CJLambda::Create(reinterpret_cast<void (*)(int32_t)>(callback));
    callback_->RegisterWriteCharacteristicCallback(writeCharacteristicFunc);
    character->SetWriteType(writeType);

    return client_->WriteCharacteristic(*character);
}

int32_t FfiClientDevice::WriteDescriptorValue(NativeBLEDescriptor inputDescriptor, void (*callback)())
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattDescriptor* descriptor = GetGattcDescriptor(client_, inputDescriptor);

    if (descriptor == nullptr) {
        HILOGE("descriptor is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto writeDescriptorValueFunc = CJLambda::Create(reinterpret_cast<void (*)(int32_t)>(callback));
    callback_->RegisterWriteDescriptorCallback(writeDescriptorValueFunc);
    return client_->WriteDescriptor(*descriptor);
}

int32_t FfiClientDevice::GetRssiValue(void (*callback)())
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto getRssiValueFunc = CJLambda::Create(reinterpret_cast<void (*)(RetDataI32)>(callback));
    callback_->RegisterGetRemoteRssicallback(getRssiValueFunc);
    return client_->ReadRemoteRssiValue();
}

int32_t FfiClientDevice::SetBLEMtuSize(int32_t mut)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->RequestBleMtuSize(mut);
}

int32_t FfiClientDevice::SetCharacteristicChangeNotification(NativeBLECharacteristic characteristic, bool enable)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);
    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->SetNotifyCharacteristic(*character, enable);
}

int32_t FfiClientDevice::SetCharacteristicChangeIndication(NativeBLECharacteristic characteristic, bool enable)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);
    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    return client_->SetIndicateCharacteristic(*character, enable);
}

int32_t FfiClientDevice::SetCharacteristicChangeNotificationCallback(
    NativeBLECharacteristic characteristic, bool enable, int64_t callback)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);
    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto characteristicChangeIndicationFunc = CJLambda::Create(reinterpret_cast<void (*)(int32_t)>(callback));
    callback_->RegisterCharacteristicChangeIndicationCallback(characteristicChangeIndicationFunc);
    return client_->SetNotifyCharacteristic(*character, enable);
}

int32_t FfiClientDevice::SetCharacteristicChangeIndicationCallback(
    NativeBLECharacteristic characteristic, bool enable, int64_t callback)
{
    if (client_ == nullptr) {
        HILOGE("gattClient is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    GattCharacteristic* character = GetGattcCharacteristic(client_, characteristic);
    if (character == nullptr) {
        HILOGE("character is nullptr");
        return BT_ERR_INTERNAL_ERROR;
    }
    auto characteristicChangeNotificationFunc = CJLambda::Create(reinterpret_cast<void (*)(int32_t)>(callback));
    callback_->RegisterCharacteristicChangeNotificationCallback(characteristicChangeNotificationFunc);
    return client_->SetIndicateCharacteristic(*character, enable);
}

int32_t FfiClientDevice::RegisterBleGattClientDeviceObserver(int32_t callbackType, void (*callback)())
{
    if (callbackType == BLE_CHARACTERISTIC_CHANGE) {
        auto bleCharacteristicChangeFunc =
            CJLambda::Create(reinterpret_cast<void (*)(NativeBLECharacteristic)>(callback));
        if (!bleCharacteristicChangeFunc) {
            HILOGD("Register bleCharacteristicChange event failed");
            return BT_ERR_INTERNAL_ERROR;
        }
        callback_->RegisterBLECharacteristicChangeFunc(bleCharacteristicChangeFunc);
        return BT_NO_ERROR;
    }

    if (callbackType == BLE_CONNECTION_STATE_CHANGE) {
        auto bleConnectionStateChangeFunc =
            CJLambda::Create(reinterpret_cast<void (*)(NativeBLEConnectionChangeState)>(callback));
        if (!bleConnectionStateChangeFunc) {
            HILOGD("Register bleConnectionStateChange event failed");
            return BT_ERR_INTERNAL_ERROR;
        }
        callback_->RegisterBLEConnectionStateChangeFunc(bleConnectionStateChangeFunc);
        return BT_NO_ERROR;
    }

    if (callbackType == CLIENT_BLE_MTU_CHANGE) {
        auto bleMtuChangeFunc = CJLambda::Create(reinterpret_cast<void (*)(int32_t)>(callback));
        if (!bleMtuChangeFunc) {
            HILOGD("Register bleMtuChange event failed");
            return BT_ERR_INTERNAL_ERROR;
        }
        callback_->RegisterBLEMtuChangeFunc(bleMtuChangeFunc);
        return BT_NO_ERROR;
    }

    return BT_NO_ERROR;
}
} // namespace CJBluetoothBle
} // namespace CJSystemapi
} // namespace OHOS