/*
 * This RPC server 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 should subclass this implementation and define the required abstract methods to complete
 * implementing the interface. Note that there are several helper methods available to simplify
 * this task, or to retrieve more information about the caller.
 *
 * See the full RPC documentation for more details.
 */
#include "Server_DiskDriver.hpp"
#include "RpcHelpers_DiskDriver.hpp"

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

#include <rpc/rt/RpcIoStream.hpp>

using namespace rpc;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
using Server = DiskDriverServer;

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

/**
 * Releases any allocated resources.
 */
Server::~DiskDriverServer() {
    free(this->txBuf);
}

/**
 * Continuously processes messages until processing fails to receive another message.
 */
bool Server::run(const bool block) {
    bool cont;
    do {
        cont = this->runOne(block);
    } while(cont);
    return cont;
}

/**
 * Reads a single message from the RPC connection and attempts to process it.
 *
 * @return Whether a message was able to be received and processed.
 */
bool Server::runOne(const bool block) {
    // try to receive message
    std::span<std::byte> buf;
    if(!this->io->receive(buf, block)) return false;

    // get the message header and its payload
    if(buf.size() < sizeof(MessageHeader)) {
        this->_HandleError(false, "Received message too small");
        return false;
    }
    const auto hdr = reinterpret_cast<const MessageHeader *>(buf.data());

    const auto payload = buf.subspan(offsetof(MessageHeader, payload));

    // then invoke the appropriate marshalling function
    switch(hdr->type) {
        case static_cast<uint64_t>(internals::Type::GetCapacity):
            this->_marshallGetCapacity(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::OpenSession):
            this->_marshallOpenSession(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::CloseSession):
            this->_marshallCloseSession(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::CreateReadBuffer):
            this->_marshallCreateReadBuffer(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::CreateWriteBuffer):
            this->_marshallCreateWriteBuffer(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::ExecuteCommand):
            this->_marshallExecuteCommand(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::ReleaseReadCommand):
            this->_marshallReleaseReadCommand(*hdr, payload);
            break;
        case static_cast<uint64_t>(internals::Type::AllocWriteMemory):
            this->_marshallAllocWriteMemory(*hdr, payload);
            break;
    }
    return true;
}


// Helper method to build and send a reply message
void Server::_sendReply(const MessageHeader &inHdr, const size_t payloadBytes) {
    const size_t len = sizeof(MessageHeader) + payloadBytes;

    auto hdr = reinterpret_cast<MessageHeader *>(this->txBuf);
    memset(hdr, 0, sizeof(*hdr));
    hdr->type = inHdr.type;
    hdr->flags = MessageHeader::Flags::Response;
    hdr->tag = inHdr.tag;

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

// Allocates an aligned transmit buffer of the given size
void Server::_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 server 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 Server::_HandleError(const bool fatal, const std::string_view &what) {
    fprintf(stderr, "[RPCS] %s: Encountered %s RPC error: %s\n", kServiceName.data(),
        fatal ? "fatal" : "recoverable", what.data());
    if(fatal) exit(-1);
}
/*
 * Autogenerated marshalling method for 'GetCapacity' (id $91df49e5f38b0cb5)
 * Have 1 parameter(s), 3 return(s); method is sync
 */
void Server::_marshallGetCapacity(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::GetCapacityRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implGetCapacity(request.diskId);

    internals::GetCapacityResponse reply;
    reply.status = retVal.status;
    reply.sectorSize = retVal.sectorSize;
    reply.numSectors = retVal.numSectors;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
/*
 * Autogenerated marshalling method for 'OpenSession' (id $f4e1aa89aee2c5f)
 * Have 0 parameter(s), 5 return(s); method is sync
 */
void Server::_marshallOpenSession(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::OpenSessionRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implOpenSession();

    internals::OpenSessionResponse reply;
    reply.status = retVal.status;
    reply.sessionToken = retVal.sessionToken;
    reply.regionHandle = retVal.regionHandle;
    reply.regionSize = retVal.regionSize;
    reply.numCommands = retVal.numCommands;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
/*
 * Autogenerated marshalling method for 'CloseSession' (id $bdac3777974760fb)
 * Have 1 parameter(s), 1 return(s); method is sync
 */
void Server::_marshallCloseSession(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::CloseSessionRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implCloseSession(request.session);

    internals::CloseSessionResponse reply;
    reply.status = retVal;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
/*
 * Autogenerated marshalling method for 'CreateReadBuffer' (id $5c63169ecba56263)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
void Server::_marshallCreateReadBuffer(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::CreateReadBufferRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implCreateReadBuffer(request.session, request.requestedSize);

    internals::CreateReadBufferResponse reply;
    reply.status = retVal.status;
    reply.readBufHandle = retVal.readBufHandle;
    reply.readBufMaxSize = retVal.readBufMaxSize;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
/*
 * Autogenerated marshalling method for 'CreateWriteBuffer' (id $2647106809f005fc)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
void Server::_marshallCreateWriteBuffer(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::CreateWriteBufferRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implCreateWriteBuffer(request.session, request.requestedSize);

    internals::CreateWriteBufferResponse reply;
    reply.status = retVal.status;
    reply.writeBufHandle = retVal.writeBufHandle;
    reply.writeBufMaxSize = retVal.writeBufMaxSize;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
/*
 * Autogenerated marshalling method for 'ExecuteCommand' (id $aae5bbef0049e019)
 * Have 2 parameter(s), 0 return(s); method is async
 */
void Server::_marshallExecuteCommand(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::ExecuteCommandRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    this->implExecuteCommand(request.session, request.slot);
}
/*
 * Autogenerated marshalling method for 'ReleaseReadCommand' (id $dcc360757768916a)
 * Have 2 parameter(s), 0 return(s); method is async
 */
void Server::_marshallReleaseReadCommand(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::ReleaseReadCommandRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    this->implReleaseReadCommand(request.session, request.slot);
}
/*
 * Autogenerated marshalling method for 'AllocWriteMemory' (id $3dc1fae0d30f6af6)
 * Have 2 parameter(s), 3 return(s); method is sync
 */
void Server::_marshallAllocWriteMemory(const MessageHeader &hdr, const std::span<std::byte> &payload) {
    internals::AllocWriteMemoryRequest request;
    if(!deserialize(payload, request)) return this->_HandleError(false, "Failed to deserialize request");

    auto retVal = this->implAllocWriteMemory(request.session, request.bytesRequested);

    internals::AllocWriteMemoryResponse reply;
    reply.status = retVal.status;
    reply.offset = retVal.offset;
    reply.bytesAllocated = retVal.bytesAllocated;

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

    auto packet = reinterpret_cast<MessageHeader *>(this->txBuf);
    std::span<std::byte> data(packet->payload, numBytes);
    if(!serialize(data, reply)) return this->_HandleError(false, "Failed to serialize reply");

    this->_sendReply(hdr, numBytes);
}
#pragma clang diagnostic pop
