/*
 * This RPC client stub was autogenerated by idlc (version 8a02fc5d). DO NOT EDIT!
 * Generated from DiskDriver.idl for interface DiskDriver at 2021-06-26T17:21:52-0500
 *
 * You may use these generated stubs directly as the RPC interface, or you can subclass it to
 * override the behavior of the function calls, or to perform some preprocessing to the data as
 * needed before sending it.
 *
 * See the full RPC documentation for more details.
 */
#include "Client_DiskDriver.hpp"
#include "RpcHelpers_DiskDriver.hpp"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <rpc/rt/RpcIoStream.hpp>

using namespace rpc;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
using Client = DiskDriverClient;

/**
 * Creates a new client instance, with the given IO stream.
 */
Client::DiskDriverClient(const std::shared_ptr<IoStream> &stream) : io(stream) {
}

/**
 * Shuts down the RPC client, releasing any allocated resources.
 */
Client::~DiskDriverClient() {
    free(this->txBuf);
}

/// Sends the message that's been built up in the transmit message buffer.
uint32_t Client::_sendRequest(const uint64_t type, const size_t payloadBytes) {
    const size_t len = sizeof(MessageHeader) + payloadBytes;

    const auto tag = __atomic_add_fetch(&this->nextTag, 1, __ATOMIC_RELAXED);
    auto hdr = reinterpret_cast<MessageHeader *>(this->txBuf);
    memset(hdr, 0, sizeof(*hdr));
    hdr->type = type;
    hdr->flags = MessageHeader::Flags::Request;
    hdr->tag = tag;

    const std::span<std::byte> txBufSpan(reinterpret_cast<std::byte *>(this->txBuf), len);
    if(!this->io->sendRequest(txBufSpan)) {
        this->_HandleError(true, "Failed to send RPC request");
        return 0;
    }

    return tag;
}

// Allocates an aligned transmit buffer of the given size
void Client::_ensureTxBuf(const size_t payloadBytes) {
    const size_t len = sizeof(MessageHeader) + payloadBytes + 16;
    if(len > this->txBufSize) {
        free(this->txBuf);
        int err = posix_memalign(&this->txBuf, 16, len);
        if(err) {
            return this->_HandleError(true, "Failed to allocate RPC send buffer");
        }
        this->txBufSize = len;
    }
}

/**
 * Handles an error that occurred on the client connection. Implementations may override this
 * method if they want to use exceptions, for example.
 *
 * @param fatal If set, the error precludes further operation on this RPC connection
 * @param what Descriptive string for the error
 */
void Client::_HandleError(const bool fatal, const std::string_view &what) {
    fprintf(stderr, "[RPCC] %s: Encountered %s RPC error: %s\n", kServiceName.data(),
        fatal ? "fatal" : "recoverable", what.data());
    if(fatal) exit(-1);
}
/*
 * Autogenerated call method for 'GetCapacity' (id $91df49e5f38b0cb5)
 * Have 1 parameter(s), 3 return(s); method is sync
 */
Client::GetCapacityReturn Client::GetCapacity(uint64_t diskId) {
    uint32_t sentTag;
    {
        internals::GetCapacityRequest request;
        request.diskId = diskId;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::GetCapacity), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::GetCapacity)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::GetCapacityResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        GetCapacityReturn r;
        r.status =  reply.status;
        r.sectorSize =  reply.sectorSize;
        r.numSectors =  reply.numSectors;
        return r;

    }
}
/*
 * Autogenerated call method for 'OpenSession' (id $f4e1aa89aee2c5f)
 * Have 0 parameter(s), 5 return(s); method is sync
 */
Client::OpenSessionReturn Client::OpenSession() {
    uint32_t sentTag;
    {
        internals::OpenSessionRequest request;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::OpenSession), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::OpenSession)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::OpenSessionResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        OpenSessionReturn r;
        r.status =  reply.status;
        r.sessionToken =  reply.sessionToken;
        r.regionHandle =  reply.regionHandle;
        r.regionSize =  reply.regionSize;
        r.numCommands =  reply.numCommands;
        return r;

    }
}
/*
 * Autogenerated call method for 'CloseSession' (id $bdac3777974760fb)
 * Have 1 parameter(s), 1 return(s); method is sync
 */
int32_t Client::CloseSession(uint64_t session) {
    uint32_t sentTag;
    {
        internals::CloseSessionRequest request;
        request.session = session;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::CloseSession), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::CloseSession)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::CloseSessionResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        return reply.status;
    }
}
/*
 * Autogenerated call method for 'CreateReadBuffer' (id $5c63169ecba56263)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
Client::CreateReadBufferReturn Client::CreateReadBuffer(uint64_t session, uint64_t requestedSize) {
    uint32_t sentTag;
    {
        internals::CreateReadBufferRequest request;
        request.session = session;
        request.requestedSize = requestedSize;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::CreateReadBuffer), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::CreateReadBuffer)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::CreateReadBufferResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        CreateReadBufferReturn r;
        r.status =  reply.status;
        r.readBufHandle =  reply.readBufHandle;
        r.readBufMaxSize =  reply.readBufMaxSize;
        return r;

    }
}
/*
 * Autogenerated call method for 'CreateWriteBuffer' (id $2647106809f005fc)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
Client::CreateWriteBufferReturn Client::CreateWriteBuffer(uint64_t session, uint64_t requestedSize) {
    uint32_t sentTag;
    {
        internals::CreateWriteBufferRequest request;
        request.session = session;
        request.requestedSize = requestedSize;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::CreateWriteBuffer), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::CreateWriteBuffer)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::CreateWriteBufferResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        CreateWriteBufferReturn r;
        r.status =  reply.status;
        r.writeBufHandle =  reply.writeBufHandle;
        r.writeBufMaxSize =  reply.writeBufMaxSize;
        return r;

    }
}
/*
 * Autogenerated call method for 'ExecuteCommand' (id $aae5bbef0049e019)
 * Have 2 parameter(s), 0 return(s); method is async
 */
void Client::ExecuteCommand(uint64_t session, uint32_t slot) {
    {
        internals::ExecuteCommandRequest request;
        request.session = session;
        request.slot = slot;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        this->_sendRequest(static_cast<uint64_t>(internals::Type::ExecuteCommand), numBytes);
    }
}
/*
 * Autogenerated call method for 'ReleaseReadCommand' (id $dcc360757768916a)
 * Have 2 parameter(s), 0 return(s); method is async
 */
void Client::ReleaseReadCommand(uint64_t session, uint32_t slot) {
    {
        internals::ReleaseReadCommandRequest request;
        request.session = session;
        request.slot = slot;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        this->_sendRequest(static_cast<uint64_t>(internals::Type::ReleaseReadCommand), numBytes);
    }
}
/*
 * Autogenerated call method for 'AllocWriteMemory' (id $3dc1fae0d30f6af6)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
Client::AllocWriteMemoryReturn Client::AllocWriteMemory(uint64_t session, uint64_t bytesRequested) {
    uint32_t sentTag;
    {
        internals::AllocWriteMemoryRequest request;
        request.session = session;
        request.bytesRequested = bytesRequested;

        const auto numBytes = bytesFor(request);
        this->_ensureTxBuf(numBytes);

        auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
        std::span<std::byte> data(packet->payload, numBytes);
        serialize(data, request);
        sentTag = this->_sendRequest(static_cast<uint64_t>(internals::Type::AllocWriteMemory), numBytes);
    }
    {
        std::span<std::byte> buf;
        if(!this->io->receiveReply(buf)) this->_HandleError(false, "Failed to receive RPC reply");
        if(buf.size() < sizeof(MessageHeader)) this->_HandleError(false, "Received message too small");
        const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());
        if(hdr->tag != sentTag) this->_HandleError(false, "Invalid tag in reply RPC packet");
        else if(hdr->type != static_cast<uint64_t>(internals::Type::AllocWriteMemory)) this->_HandleError(false, "Invalid type in reply RPC packet");
        const auto payload = buf.subspan(offsetof(MessageHeader, payload));

        internals::AllocWriteMemoryResponse reply;
        if(!deserialize(payload, reply)) this->_HandleError(false, "Failed to decode message");
        AllocWriteMemoryReturn r;
        r.status =  reply.status;
        r.offset =  reply.offset;
        r.bytesAllocated =  reply.bytesAllocated;
        return r;

    }
}
#pragma clang diagnostic pop
