/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
 * UbsEngine is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "ubs_engine_npu.h"

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>

#include <securec.h>

#include "ubse_ipc_client.h"
#include "ubse_ipc_common.h"
#include "ubse_ipc_log.h"
#include "libubse_helper.h"
#include "libubse_npu_helper.h"
#include "ubs_error.h"

constexpr size_t GUID_SIZE = MACRO_UBSE_UB_DEVICE_GUID_SIZE;

int32_t ubs_npu_device_list_query(ubs_ub_devices_list_t* device_list)
{
    if (device_list == nullptr) {
        return UBS_ERR_NULL_POINTER;
    }
    ubse_api_buffer_t requestBuffer = {nullptr, 0};
    ubse_api_buffer_t responseBuffer = {nullptr, 0};

    const uint32_t ipcRet = ubse_invoke_call(UBSE_NPU, UBSE_NPU_GET_HOST_DEVICES, &requestBuffer, &responseBuffer);
    ubse_api_buffer_free(&requestBuffer);

    if (ipcRet != UBS_SUCCESS) {
        IPC_LOG_ERROR << "ubse_invoke_call failed with error code: " << ipcRet;
        ubse_api_buffer_free(&responseBuffer);
        return ubse_map_daemon_error(ipcRet);
    }

    const ubs_error_t ret = UbseUbDevListUnpack(responseBuffer.buffer, responseBuffer.length, device_list);
    ubse_api_buffer_free(&responseBuffer);
    if (ret != UBS_SUCCESS) {
        IPC_LOG_ERROR << "Failed to unpack ub device list, error: " << ret;
        return ret;
    }

    IPC_LOG_DEBUG << "Device List: UBCTRL=" << static_cast<uint32_t>(device_list->ubctrl_cnt)
                  << ", NIC_PFE=" << static_cast<uint32_t>(device_list->nic_pfe_cnt)
                  << ", NIC_VFE=" << static_cast<uint32_t>(device_list->nic_vfe_cnt)
                  << ", NPU=" << static_cast<uint32_t>(device_list->npu_cnt)
                  << ", BUSI=" << static_cast<uint32_t>(device_list->busi_cnt);

    return ret;
}

int32_t ubs_npu_device_alloc(ubs_ub_alloc_devices_info_t* alloc_info, uint8_t* new_bus_instance_guid,
                             ubs_ub_devices_list_t* device_list)
{
    if (new_bus_instance_guid == nullptr || device_list == nullptr) {
        return UBS_ERR_NULL_POINTER;
    }
    ubs_error_t ret = UbseNpuAllocInfoIsValid(alloc_info);
    if (ret != UBS_SUCCESS) {
        return static_cast<int32_t>(ret);
    }

    ubse_api_buffer_t requestBuffer;
    ret = UbseNpuAllocReqBuild(requestBuffer, *alloc_info);
    if (ret != UBS_SUCCESS) {
        return static_cast<int32_t>(ret);
    }

    ret = UbseNpuAllocReqPack(*alloc_info, requestBuffer.buffer);
    if (ret != UBS_SUCCESS) {
        ubse_api_buffer_free(&requestBuffer);
        return static_cast<int32_t>(ret);
    }

    ubse_api_buffer_t responseBuffer = {nullptr, 0};
    const uint32_t ipcRet = ubse_invoke_call(UBSE_NPU, UBSE_NPU_ALLOC_UB_DEVICES, &requestBuffer, &responseBuffer);
    ubse_api_buffer_free(&requestBuffer);

    if (ipcRet != UBS_SUCCESS) {
        ubse_api_buffer_free(&responseBuffer);
        return ubse_map_daemon_error(ipcRet);
    }

    ret = UbseNpuAllocUnpack(responseBuffer.buffer, responseBuffer.length, new_bus_instance_guid, device_list);
    ubse_api_buffer_free(&responseBuffer);
    return static_cast<int32_t>(ret);
}

int32_t ubs_npu_device_free(ubs_ub_alloc_devices_info_t* alloc_info)
{
    ubs_error_t ret = UbseNpuAllocInfoIsValid(alloc_info);
    if (ret != UBS_SUCCESS) {
        return static_cast<int32_t>(ret);
    }

    ubse_api_buffer_t requestBuffer;
    ret = UbseNpuAllocReqBuild(requestBuffer, *alloc_info);
    if (ret != UBS_SUCCESS) {
        return static_cast<int32_t>(ret);
    }

    ret = UbseNpuAllocReqPack(*alloc_info, requestBuffer.buffer);
    if (ret != UBS_SUCCESS) {
        ubse_api_buffer_free(&requestBuffer);
        return static_cast<int32_t>(ret);
    }

    ubse_api_buffer_t responseBuffer = {nullptr, 0};
    const uint32_t ipcRet = ubse_invoke_call(UBSE_NPU, UBSE_NPU_FREE_UB_DEVICES, &requestBuffer, &responseBuffer);
    ubse_api_buffer_free(&requestBuffer);

    if (ipcRet != UBS_SUCCESS) {
        ubse_api_buffer_free(&responseBuffer);
        return ubse_map_daemon_error(ipcRet);
    }

    ubse_api_buffer_free(&responseBuffer);
    return static_cast<int32_t>(UBS_SUCCESS);
}

void ubs_npu_device_list_free(ubs_ub_devices_list_t* device_list)
{
    FreeBusi(*device_list);
    FreeNpu(*device_list);
    FreeUbctrl(*device_list);
    FreeNicPfe(*device_list);
    FreeNicVfe(*device_list);
}

int32_t ubs_uba_tid_size_query(uint8_t* bus_instance_guid, uint32_t* tid, uint64_t* uba, uint64_t* size)
{
    if (bus_instance_guid == nullptr || tid == nullptr || uba == nullptr || size == nullptr) {
        return UBS_ERR_NULL_POINTER;
    }

    ubse_api_buffer_t requestBuffer;
    ubs_error_t ret = UbseNpuQueryTidUbaSizeReqBuild(requestBuffer, bus_instance_guid);
    if (ret != UBS_SUCCESS) {
        return ret;
    }

    ubse_api_buffer_t responseBuffer = {nullptr, 0};
    const uint32_t ipcRet = ubse_invoke_call(UBSE_NPU, UBSE_NPU_QUERY_UBA_TID_SIZE, &requestBuffer, &responseBuffer);
    ubse_api_buffer_free(&requestBuffer);

    if (ipcRet != UBS_SUCCESS) {
        ubse_api_buffer_free(&responseBuffer);
        return ubse_map_daemon_error(ipcRet);
    }

    ret = UbseNpuQueryTidUbaSizeUnpack(responseBuffer.buffer, responseBuffer.length, *tid, *uba, *size);
    ubse_api_buffer_free(&responseBuffer);
    return ret;
}