text: // Copyright (c) 2019-2021 Fango Developers
  // Copyright (c) 2018-2021 Fandom Gold Society
  // Copyright (c) 2018-2019 Conceal Network & Conceal Devs
  // Copyright (c) 2016-2019 The Karbowanec developers
  // Copyright (c) 2012-2018 The CryptoNote developers
  //
  // This file is part of Fango.
  //
  // Fango is free software distributed in the hope that it
  // will be useful, but WITHOUT ANY WARRANTY; without even the
  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  // PURPOSE. You can redistribute it and/or modify it under the terms
  // of the GNU General Public License v3 or later versions as published
  // by the Free Software Foundation. Fango includes elements written 
  // by third parties. See file labeled LICENSE for more details.
  // You should have received a copy of the GNU General Public License
  // along with Fango. If not, see <https://www.gnu.org/licenses/>.
  
  #pragma once
  
  #include "CryptoNote.h"
  #include <Common/MemoryInputStream.h>
  #include <Common/VectorOutputStream.h>
  #include "Serialization/KVBinaryInputStreamSerializer.h"
  #include "Serialization/KVBinaryOutputStreamSerializer.h"
  
  namespace System {
  class TcpConnection;
  }
  
  namespace CryptoNote {
  
  enum class LevinError: int32_t {
    OK = 0,
    ERROR_CONNECTION = -1,
    ERROR_CONNECTION_NOT_FOUND = -2,
    ERROR_CONNECTION_DESTROYED = -3,
    ERROR_CONNECTION_TIMEDOUT = -4,
    ERROR_CONNECTION_NO_DUPLEX_PROTOCOL = -5,
    ERROR_CONNECTION_HANDLER_NOT_DEFINED = -6,
    ERROR_FORMAT = -7,
  };
  
  const int32_t LEVIN_PROTOCOL_RETCODE_SUCCESS = 1;
  
  class LevinProtocol {
  public:
  
    LevinProtocol(System::TcpConnection& connection);
  
    template <typename Request, typename Response>
    bool invoke(uint32_t command, const Request& request, Response& response) {
      sendMessage(command, encode(request), true);
  
      Command cmd;
      readCommand(cmd);
  
      if (!cmd.isResponse) {
        return false;
      }
  
      return decode(cmd.buf, response); 
    }
  
    template <typename Request>
    void notify(uint32_t command, const Request& request, int) {
      sendMessage(command, encode(request), false);
    }
  
    struct Command {
      uint32_t command;
      bool isNotify;
      bool isResponse;
      BinaryArray buf;
  
      bool needReply() const;
    };
  
    bool readCommand(Command& cmd);
  
    void sendMessage(uint32_t command, const BinaryArray& out, bool needResponse);
    void sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode);
  
    template <typename T>
    static bool decode(const BinaryArray& buf, T& value) {
      try {
        Common::MemoryInputStream stream(buf.data(), buf.size());
        KVBinaryInputStreamSerializer serializer(stream);
        serialize(value, serializer);
      } catch (std::exception&) {
        return false;
      }
  
      return true;
    }
  
    template <typename T>
    static BinaryArray encode(const T& value) {
      BinaryArray result;
      KVBinaryOutputStreamSerializer serializer;
      serialize(const_cast<T&>(value), serializer);
      Common::VectorOutputStream stream(result);
      serializer.dump(stream);
      return result;
    }
  
  private:
  
    bool readStrict(uint8_t* ptr, size_t size);
    void writeStrict(const uint8_t* ptr, size_t size);
    System::TcpConnection& m_conn;
  };
  
  }
  