//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: simple.fbe
// FBE version: 1.14.4.0
//------------------------------------------------------------------------------

#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4065) // C4065: switch statement contains 'default' but no 'case' labels
#endif

#include "simple_protocol.h"

namespace FBE {

namespace simple {

size_t Sender::send(const ::simple::SimpleRequest& value)
{
    // Serialize the value into the FBE stream
    size_t serialized = SimpleRequestModel.serialize(value);
    assert((serialized > 0) && "simple::SimpleRequest serialization failed!");
    assert(SimpleRequestModel.verify() && "simple::SimpleRequest validation failed!");

    // Log the value
    if (this->_logging)
    {
        std::string message = value.string();
        this->onSendLog(message);
    }

    // Send the serialized value
    return this->send_serialized(serialized);
}

size_t Sender::send(const ::simple::SimpleResponse& value)
{
    // Serialize the value into the FBE stream
    size_t serialized = SimpleResponseModel.serialize(value);
    assert((serialized > 0) && "simple::SimpleResponse serialization failed!");
    assert(SimpleResponseModel.verify() && "simple::SimpleResponse validation failed!");

    // Log the value
    if (this->_logging)
    {
        std::string message = value.string();
        this->onSendLog(message);
    }

    // Send the serialized value
    return this->send_serialized(serialized);
}

size_t Sender::send(const ::simple::SimpleReject& value)
{
    // Serialize the value into the FBE stream
    size_t serialized = SimpleRejectModel.serialize(value);
    assert((serialized > 0) && "simple::SimpleReject serialization failed!");
    assert(SimpleRejectModel.verify() && "simple::SimpleReject validation failed!");

    // Log the value
    if (this->_logging)
    {
        std::string message = value.string();
        this->onSendLog(message);
    }

    // Send the serialized value
    return this->send_serialized(serialized);
}

size_t Sender::send(const ::simple::SimpleNotify& value)
{
    // Serialize the value into the FBE stream
    size_t serialized = SimpleNotifyModel.serialize(value);
    assert((serialized > 0) && "simple::SimpleNotify serialization failed!");
    assert(SimpleNotifyModel.verify() && "simple::SimpleNotify validation failed!");

    // Log the value
    if (this->_logging)
    {
        std::string message = value.string();
        this->onSendLog(message);
    }

    // Send the serialized value
    return this->send_serialized(serialized);
}

size_t Sender::send(const ::simple::DisconnectRequest& value)
{
    // Serialize the value into the FBE stream
    size_t serialized = DisconnectRequestModel.serialize(value);
    assert((serialized > 0) && "simple::DisconnectRequest serialization failed!");
    assert(DisconnectRequestModel.verify() && "simple::DisconnectRequest validation failed!");

    // Log the value
    if (this->_logging)
    {
        std::string message = value.string();
        this->onSendLog(message);
    }

    // Send the serialized value
    return this->send_serialized(serialized);
}

bool Receiver::onReceive(size_t type, const void* data, size_t size)
{
    switch (type)
    {
        case FBE::simple::SimpleRequestModel::fbe_type():
        {
            // Deserialize the value from the FBE stream
            SimpleRequestModel.attach(data, size);
            assert(SimpleRequestModel.verify() && "simple::SimpleRequest validation failed!");
            [[maybe_unused]] size_t deserialized = SimpleRequestModel.deserialize(SimpleRequestValue);
            assert((deserialized > 0) && "simple::SimpleRequest deserialization failed!");

            // Log the value
            if (this->_logging)
            {
                std::string message = SimpleRequestValue.string();
                this->onReceiveLog(message);
            }

            // Call receive handler with deserialized value
            onReceive(SimpleRequestValue);
            return true;
        }
        case FBE::simple::SimpleResponseModel::fbe_type():
        {
            // Deserialize the value from the FBE stream
            SimpleResponseModel.attach(data, size);
            assert(SimpleResponseModel.verify() && "simple::SimpleResponse validation failed!");
            [[maybe_unused]] size_t deserialized = SimpleResponseModel.deserialize(SimpleResponseValue);
            assert((deserialized > 0) && "simple::SimpleResponse deserialization failed!");

            // Log the value
            if (this->_logging)
            {
                std::string message = SimpleResponseValue.string();
                this->onReceiveLog(message);
            }

            // Call receive handler with deserialized value
            onReceive(SimpleResponseValue);
            return true;
        }
        case FBE::simple::SimpleRejectModel::fbe_type():
        {
            // Deserialize the value from the FBE stream
            SimpleRejectModel.attach(data, size);
            assert(SimpleRejectModel.verify() && "simple::SimpleReject validation failed!");
            [[maybe_unused]] size_t deserialized = SimpleRejectModel.deserialize(SimpleRejectValue);
            assert((deserialized > 0) && "simple::SimpleReject deserialization failed!");

            // Log the value
            if (this->_logging)
            {
                std::string message = SimpleRejectValue.string();
                this->onReceiveLog(message);
            }

            // Call receive handler with deserialized value
            onReceive(SimpleRejectValue);
            return true;
        }
        case FBE::simple::SimpleNotifyModel::fbe_type():
        {
            // Deserialize the value from the FBE stream
            SimpleNotifyModel.attach(data, size);
            assert(SimpleNotifyModel.verify() && "simple::SimpleNotify validation failed!");
            [[maybe_unused]] size_t deserialized = SimpleNotifyModel.deserialize(SimpleNotifyValue);
            assert((deserialized > 0) && "simple::SimpleNotify deserialization failed!");

            // Log the value
            if (this->_logging)
            {
                std::string message = SimpleNotifyValue.string();
                this->onReceiveLog(message);
            }

            // Call receive handler with deserialized value
            onReceive(SimpleNotifyValue);
            return true;
        }
        case FBE::simple::DisconnectRequestModel::fbe_type():
        {
            // Deserialize the value from the FBE stream
            DisconnectRequestModel.attach(data, size);
            assert(DisconnectRequestModel.verify() && "simple::DisconnectRequest validation failed!");
            [[maybe_unused]] size_t deserialized = DisconnectRequestModel.deserialize(DisconnectRequestValue);
            assert((deserialized > 0) && "simple::DisconnectRequest deserialization failed!");

            // Log the value
            if (this->_logging)
            {
                std::string message = DisconnectRequestValue.string();
                this->onReceiveLog(message);
            }

            // Call receive handler with deserialized value
            onReceive(DisconnectRequestValue);
            return true;
        }
        default: break;
    }

    return false;
}

bool Proxy::onReceive(size_t type, const void* data, size_t size)
{
    switch (type)
    {
        case FBE::simple::SimpleRequestModel::fbe_type():
        {
            // Attach the FBE stream to the proxy model
            SimpleRequestModel.attach(data, size);
            assert(SimpleRequestModel.verify() && "simple::SimpleRequest validation failed!");

            size_t fbe_begin = SimpleRequestModel.model.get_begin();
            if (fbe_begin == 0)
                return false;
            // Call proxy handler
            onProxy(SimpleRequestModel, type, data, size);
            SimpleRequestModel.model.get_end(fbe_begin);
            return true;
        }
        case FBE::simple::SimpleResponseModel::fbe_type():
        {
            // Attach the FBE stream to the proxy model
            SimpleResponseModel.attach(data, size);
            assert(SimpleResponseModel.verify() && "simple::SimpleResponse validation failed!");

            size_t fbe_begin = SimpleResponseModel.model.get_begin();
            if (fbe_begin == 0)
                return false;
            // Call proxy handler
            onProxy(SimpleResponseModel, type, data, size);
            SimpleResponseModel.model.get_end(fbe_begin);
            return true;
        }
        case FBE::simple::SimpleRejectModel::fbe_type():
        {
            // Attach the FBE stream to the proxy model
            SimpleRejectModel.attach(data, size);
            assert(SimpleRejectModel.verify() && "simple::SimpleReject validation failed!");

            size_t fbe_begin = SimpleRejectModel.model.get_begin();
            if (fbe_begin == 0)
                return false;
            // Call proxy handler
            onProxy(SimpleRejectModel, type, data, size);
            SimpleRejectModel.model.get_end(fbe_begin);
            return true;
        }
        case FBE::simple::SimpleNotifyModel::fbe_type():
        {
            // Attach the FBE stream to the proxy model
            SimpleNotifyModel.attach(data, size);
            assert(SimpleNotifyModel.verify() && "simple::SimpleNotify validation failed!");

            size_t fbe_begin = SimpleNotifyModel.model.get_begin();
            if (fbe_begin == 0)
                return false;
            // Call proxy handler
            onProxy(SimpleNotifyModel, type, data, size);
            SimpleNotifyModel.model.get_end(fbe_begin);
            return true;
        }
        case FBE::simple::DisconnectRequestModel::fbe_type():
        {
            // Attach the FBE stream to the proxy model
            DisconnectRequestModel.attach(data, size);
            assert(DisconnectRequestModel.verify() && "simple::DisconnectRequest validation failed!");

            size_t fbe_begin = DisconnectRequestModel.model.get_begin();
            if (fbe_begin == 0)
                return false;
            // Call proxy handler
            onProxy(DisconnectRequestModel, type, data, size);
            DisconnectRequestModel.model.get_end(fbe_begin);
            return true;
        }
        default: break;
    }

    return false;
}

std::future<::simple::SimpleResponse> Client::request(const ::simple::SimpleRequest& value, uint64_t timeout)
{
    std::scoped_lock locker(this->_lock);

    std::promise<::simple::SimpleResponse> promise;
    std::future<::simple::SimpleResponse> future = promise.get_future();

    uint64_t current = utc();

    // Send the request message
    size_t serialized = Sender::send(value);
    if (serialized > 0)
    {
        // Calculate the unique timestamp
        this->_timestamp = (current <= this->_timestamp) ? this->_timestamp + 1 : current;

        // Register the request
        _requests_by_id_SimpleResponse.insert(std::make_pair(value.id, std::make_tuple(this->_timestamp, timeout * 1000000, std::move(promise))));
        if (timeout > 0)
            _requests_by_timestamp_SimpleResponse.insert(std::make_pair(this->_timestamp, value.id));
    }
    else
        promise.set_exception(std::make_exception_ptr(std::runtime_error("Send request failed!")));

    return future;
}

std::future<void> Client::request(const ::simple::DisconnectRequest& value, uint64_t timeout)
{
    std::promise<void> promise;
    std::future<void> future = promise.get_future();

    // Send the request message
    size_t serialized = Sender::send(value);
    if (serialized > 0)
        promise.set_value();
    else
        promise.set_exception(std::make_exception_ptr(std::runtime_error("Send request failed!")));

    return future;
}

bool Client::onReceiveResponse(const ::simple::SimpleResponse& response)
{
    std::scoped_lock locker(this->_lock);

    auto it_SimpleResponse = _requests_by_id_SimpleResponse.find(response.id);
    if (it_SimpleResponse != _requests_by_id_SimpleResponse.end())
    {
        auto timestamp = std::get<0>(it_SimpleResponse->second);
        [[maybe_unused]] auto timespan = std::get<1>(it_SimpleResponse->second);
        auto& promise = std::get<2>(it_SimpleResponse->second);
        promise.set_value(response);
        _requests_by_id_SimpleResponse.erase(response.id);
        _requests_by_timestamp_SimpleResponse.erase(timestamp);
        return true;
    }

    return false;
}

bool Client::onReceiveReject(const ::simple::SimpleReject& reject)
{
    std::scoped_lock locker(this->_lock);

    auto it_SimpleResponse = _requests_by_id_SimpleResponse.find(reject.id);
    if (it_SimpleResponse != _requests_by_id_SimpleResponse.end())
    {
        auto timestamp = std::get<0>(it_SimpleResponse->second);
        [[maybe_unused]] auto timespan = std::get<1>(it_SimpleResponse->second);
        auto& promise = std::get<2>(it_SimpleResponse->second);
        promise.set_exception(std::make_exception_ptr(std::runtime_error(reject.string())));
        _requests_by_id_SimpleResponse.erase(reject.id);
        _requests_by_timestamp_SimpleResponse.erase(timestamp);
        return true;
    }

    return false;
}

void Client::reset_requests()
{
    Sender::reset();
    Receiver::reset();

    for (auto& request : _requests_by_id_SimpleResponse)
        std::get<2>(request.second).set_exception(std::make_exception_ptr(std::runtime_error("Reset client!")));
    _requests_by_id_SimpleResponse.clear();
    _requests_by_timestamp_SimpleResponse.clear();
}

void Client::watchdog_requests(uint64_t utc)
{
    auto it_request_by_timestamp_SimpleResponse = _requests_by_timestamp_SimpleResponse.begin();
    while (it_request_by_timestamp_SimpleResponse != _requests_by_timestamp_SimpleResponse.end())
    {
        auto& it_request_by_id_SimpleResponse = _requests_by_id_SimpleResponse[it_request_by_timestamp_SimpleResponse->second];
        auto id = it_request_by_timestamp_SimpleResponse->second;
        auto timestamp = std::get<0>(it_request_by_id_SimpleResponse);
        auto timespan = std::get<1>(it_request_by_id_SimpleResponse);
        if ((timestamp + timespan) <= utc)
        {
            auto& promise = std::get<2>(it_request_by_id_SimpleResponse);
            promise.set_exception(std::make_exception_ptr(std::runtime_error("Timeout!")));
            _requests_by_id_SimpleResponse.erase(id);
            _requests_by_timestamp_SimpleResponse.erase(timestamp);
            it_request_by_timestamp_SimpleResponse = _requests_by_timestamp_SimpleResponse.begin();
            continue;
        }
        else
            break;
    }

}

} // namespace simple

} // namespace FBE

#if defined(_MSC_VER)
#pragma warning(pop)
#endif
