#ifndef BIBUNM_RECEIVER_H
#define BIBUNM_RECEIVER_H

#include "zmq_bazooka/sockets.h"
#include "bibun_messenging/request.h"
#include "bibun_messenging/payload.h"

#include <list>
#include <memory>

namespace bibunm
{

class Receiver
{
public:
   // Note: a Receiver should always destruct before the socket it uses.
   Receiver(bzook::Socket *socket);

   Receiver(const Receiver &) = delete;
   Receiver(Receiver &&other);

   ///
   /// \brief Receive a full request on the socket.
   /// \return The request.
   ///
   std::unique_ptr<internal_objects::Request> receive();

   ///
   /// \brief Receive a frame into _recv_buffer.
   ///
   void readFrame();

   ///
   /// \brief Get a copy of the frame from the buffer
   /// \return The copied frame in a string.
   ///
   inline std::string extractFrame() const
   {
      return std::string(_recv_buffer, static_cast<size_t>(_recv_sz));
   }

   ///
   /// \brief Indicate that a ProtocolError must be thrown if no more frames are available.
   /// \param next_section The next expected section, to indicate in errors what is missing.
   ///
   void expectMore(std::string next_section);

   virtual ~Receiver();
private:

   ///
   /// \brief Receive routes of the request.
   /// \return A map of these routes.
   ///
   std::list<std::string> receive_routes();

   ///
   /// \brief Receive the request's command/on-the-wire name.
   /// \return A string of the command name.
   ///
   std::string receive_command();

   ///
   /// \brief Receive headers until a delimiter frame.
   /// \return A map of these headers.
   ///
   std::map<std::string, std::string> receive_headers();

   ///
   /// \brief Read in the payload for the request.
   /// \param protocol_headers The protocol headers found from receive_headers(). Payload hdrs wll be removed.
   /// \return A uniquely owned payload to ensure it is destroyed if no one assumes ownership.
   ///
   std::unique_ptr<Payload> receive_payload(std::map<std::string, std::string> &protocol_headers);

   ///
   /// \brief Receive a termination frame and throw if it is not actually one.
   ///
   void receive_termination();

   ///
   /// \brief Checks if current frame is a delimiter frame.
   /// \param length How bytes were read with read_frame().
   /// \return Whether the frame in _recv_buffer is a delimiter frame.
   ///
   bool is_delimiter_frame();

   bzook::Socket *_socket;

   // The buffer grows if large frames start coming in.
   char _recv_buffer[100];  // Buffer to hold a frame.
   int _recv_sz;            // Last frame received's size.
};

}

#endif // BIBUNM_RECEIVER_H
