#ifndef BIBUNM_REQUEST_H
#define BIBUNM_REQUEST_H

#include <map>
#include <string>

///
/// \brief BiBun Messenging.
///
/// This is the component/namespace/library responsible for implementing the BiBun messenging protocol.
/// The BiBun messenging protocol implements the main framework for server nodes and client applications alike. See the
/// bibunm::Client class for a good entry point into writing a C++ client application or language binding for BiBun
/// clients.
///
namespace bibunm
{

// Forward declaration for Sender and Receiver allow us to indicate friendship (also in derived classes).
class Sender;
class Receiver;

///
/// \brief Internal C++ representation of BiBun commands.
///
/// Through various interfaces this inheritance hierarchy is encoded/decoded into/from messages travelling on the wire.
/// If you are using these directly as a Client this is probalby a bad thing. You only really need Message and Registry
/// requests.
///
namespace internal_objects
{

///
/// \brief Abstract request for the BiBun protocol.
///
/// The request provides a uniform interface for encoding and decoding of messages on the BiBun protocol.
///
class Request
{
   friend class ::bibunm::Sender;
   friend class ::bibunm::Receiver;
public:
   ///
   /// \brief Create request object with provided routes and user headers.
   /// \param routes A list of destination identities (routes) that this request must be sent to reach its endpoint.
   /// \param user_headers User defined headers for their own mini-protocol.
   ///
   Request();

   /// @name Move and Copy
   /// Move and copy semantics for Request are defaulted. All members are copyable and movable.
   /// @{
   Request(const Request &) = default;
   Request(Request &&) noexcept = default;
   Request &operator=(const Request &) = default;
   Request &operator=(Request &&) noexcept = default;
   /// @}

   ///
   /// \brief On the wire name for the command.
   /// \return Name on the wire.
   ///
   virtual const char *wireName() const = 0;


   virtual ~Request();
protected:
   ///
   /// \brief From the Request object hierachy, create a map of all the protocol headers for encoding.
   ///
   /// This call is expensive as it constructs a set header key value pairs... only use for encoding requests.
   /// \return The encoded map of headers to be sent.
   ///
   virtual std::map<std::string, std::string> create_protocol_headers() const = 0;

   ///
   /// \brief Move a value from a map cheaply if it is found and return it.
   /// \param protocol_headers The map to inspect.
   /// \param key The key to look for.
   /// \return The extracted value string.
   ///
   static std::string move_from_headers(std::map<std::string, std::string> &protocol_headers, const std::string &key);

   ///
   /// \brief Populate class members from decoded headers.
   ///
   /// This function is usually called when decoding headers and building a request object. The decoder creates a
   /// request object with one of its derived classes... the passed map contains all the decoded headers from a message
   /// and each stage of construction in the Request object hierachy extracts the parts of the maps that it needs. To
   /// extract a header from a map, the handle_headers functions in derived classes will call Request::move_from_headers
   /// and this will throw if the expected header is not found. Otherwise, the header can be cheaply moved from the
   /// map into a class member. If the map is not empty by the time this function is called it will throw. All throws
   /// are expected to be bibunm::ProtocolError.
   /// \param protocol_hdrs Map to extract headers from.
   ///
   virtual void handle_protocol_headers(std::map<std::string, std::string> &protocol_hdrs);
private:
   static const char _hdr_sep;
};

}
}

#endif // BIBUNM_REQUEST_H
