/* Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC 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 "transfer/mctp_transfer.h"

// =================================================== send/receive ===================================================
uint32_t MctpTransfer::DeviceSendMessage(void* spdm_context, size_t message_size, const void* message, uint64_t timeout)
{
    endRecvTempBufferLen = sizeof(sendRecvTempBuffer);
    int32_t res = LCallbacks::send_recv(this->device_name, reinterpret_cast<const uint8_t*>(message), message_size,
                                        sendRecvTempBuffer, &endRecvTempBufferLen);
    return res == RET_OK ? LIBSPDM_STATUS_SUCCESS : LIBSPDM_STATUS_SESSION_MSG_ERROR;
}

uint32_t MctpTransfer::DeviceReceiveMessage(void* spdm_context, size_t* message_size, void** message, uint64_t timeout)
{
    if (*message == nullptr) {
        debug_log(DLOG_ERROR, "invalid parameter");
        return LIBSPDM_STATUS_SESSION_MSG_ERROR;
    }

    memcpy_s(reinterpret_cast<uint8_t*>(*message), *message_size, sendRecvTempBuffer, endRecvTempBufferLen);

    *message_size = endRecvTempBufferLen;
    memset_s(sendRecvTempBuffer, SENDER_RECEIVER_MAX_BUFFER_SIZE, 0, SENDER_RECEIVER_MAX_BUFFER_SIZE);
    endRecvTempBufferLen = SENDER_RECEIVER_MAX_BUFFER_SIZE;

    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpSendCallback(void* spdm_context, size_t message_size, const void* message, uint64_t timeout)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->DeviceSendMessage(spdm_context, message_size, message, timeout);
}

uint32_t MctpReceiveCallback(void* spdm_context, size_t* message_size, void** message, uint64_t timeout)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->DeviceReceiveMessage(spdm_context, message_size, message, timeout);
}

// =================================================== encode/decode ===================================================
uint32_t MctpTransfer::EncodeMessage(void* spdm_context, const uint32_t* session_id, bool is_app_message,
                                     bool is_request_message, size_t message_size, void* message,
                                     size_t* transport_message_size, void** transport_message)
{
    // TODO 数据编码动作
    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpTransfer::DecodeMessage(void* spdm_context, uint32_t** session_id, bool* is_app_message,
                                     bool is_request_message, size_t transport_message_size, void* transport_message,
                                     size_t* message_size, void** message)
{
    // TODO 数据解码动作
    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpEncodeCallback(void* spdm_context, const uint32_t* session_id, bool is_app_message,
                            bool is_request_message, size_t message_size, void* message, size_t* transport_message_size,
                            void** transport_message)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->EncodeMessage(spdm_context, session_id, is_app_message, is_request_message, message_size, message,
                                   transport_message_size, transport_message);
}

uint32_t MctpDecodeCallback(void* spdm_context, uint32_t** session_id, bool* is_app_message, bool is_request_message,
                            size_t transport_message_size, void* transport_message, size_t* message_size,
                            void** message)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->DecodeMessage(spdm_context, session_id, is_app_message, is_request_message, transport_message_size,
                                   transport_message, message_size, message);
}

// =================================================== buffer management
// ===================================================
uint32_t MctpTransfer::AcquireSenderBuffer(void* spdm_context, void** msg_buf_ptr)
{
    if (sendReceiveBufferAcquired) {
        return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
    }

    *msg_buf_ptr = sendReceiveBuffer;
    ZeroMem(sendReceiveBuffer, sizeof(sendReceiveBuffer));
    sendReceiveBufferAcquired = true;

    return LIBSPDM_STATUS_SUCCESS;
}

void MctpTransfer::ReleaseSenderBuffer(void* spdm_context, const void* msg_buf_ptr)
{
    sendReceiveBufferAcquired = false;
}

uint32_t MctpTransfer::AcquireReceiverBuffer(void* spdm_context, void** msg_buf_ptr)
{
    if (sendReceiveBufferAcquired) {
        return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
    }

    *msg_buf_ptr = sendReceiveBuffer;
    ZeroMem(sendReceiveBuffer, sizeof(sendReceiveBuffer));
    sendReceiveBufferAcquired = true;

    return LIBSPDM_STATUS_SUCCESS;
}

void MctpTransfer::ReleaseReceiverBuffer(void* spdm_context, const void* msg_buf_ptr)
{
    sendReceiveBufferAcquired = false;
}

uint32_t MctpAcquireSenderBufferCallback(void* spdm_context, void** msg_buf_ptr)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->AcquireSenderBuffer(spdm_context, msg_buf_ptr);
}

void MctpReleaseSenderBufferCallback(void* spdm_context, const void* msg_buf_ptr)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->ReleaseSenderBuffer(spdm_context, msg_buf_ptr);
}

uint32_t MctpAcquireReceiverBufferCallback(void* spdm_context, void** msg_buf_ptr)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->AcquireReceiverBuffer(spdm_context, msg_buf_ptr);
}

void MctpReleaseReceiverBufferCallback(void* spdm_context, const void* msg_buf_ptr)
{
    libspdm_context_t* ctx      = static_cast<libspdm_context_t*>(spdm_context);
    MctpTransfer*      transfer = static_cast<MctpTransfer*>(ctx->app_context_data_ptr);

    return transfer->ReleaseReceiverBuffer(spdm_context, msg_buf_ptr);
}

// =================================================== register ===================================================
int32_t MctpTransfer::RegisterFunctions(void* spdm_context)
{
    RegisterDeviceIoFunc(spdm_context, MctpSendCallback, MctpReceiveCallback);
    RegisterTransportLayerFunc(spdm_context, msgMaxSize, msgHeaderSize, msgTailSize, MctpEncodeCallback,
                               MctpDecodeCallback);
    RegisterDeviceBufferFunc(spdm_context, SENDER_BUFFER_SIZE, RECEIVER_BUFFER_SIZE, MctpAcquireSenderBufferCallback,
                             MctpReleaseSenderBufferCallback, MctpAcquireReceiverBufferCallback,
                             MctpReleaseReceiverBufferCallback);

    size_t scratch_buffer_size = GetRequiredScratchBufferSize(spdm_context);
    debug_log(DLOG_INFO, "scratch_buffer_size = %lu", scratch_buffer_size);
    if (scratch_buffer_size != 0) {
        scratchBuffer = (void*)malloc(scratch_buffer_size);
        if (scratchBuffer == nullptr) {
            debug_log(DLOG_ERROR, "malloc scratch buffer failed");
            return RET_ERR;
        }
        SetScratchBuffer(spdm_context, scratchBuffer, scratch_buffer_size);
    }

    return RET_OK;
}