#ifndef BIBUNM_MESSAGE_H
#define BIBUNM_MESSAGE_H

#include "bibun_messenging/payload_request.h"
#include "bibun_messenging/ack.h"

#include <chrono>
#include <string>
#include <list>
#include <vector>

namespace bibunm
{

class Message : public internal_objects::PayloadRequest
{
   friend class Receiver;
   friend class Client; // TODO rethink this. Clients can send acks for this message.
public:
   enum class AckMode: int
   {
      NONE = 0,
      FAF = 1,
      TEND = 2
   };

   struct Config
   {
      Config();

      Config(std::string app_id,
             std::list<std::string> push_routes = {},
             std::chrono::seconds validFor = std::chrono::minutes(1),
             AckMode mode = AckMode::NONE
      );

      std::string application_id;
      std::chrono::seconds valid_for;
      std::list<std::string> routes;
      AckMode ack_mode;
   };

   Message() = delete;

   Message(Payload payload, Config config);

   /// @name Move and Copy
   /// Move semantics for Message are defaulted... all members are movable. However, Messages are unique and therefore
   /// copies must generate a new message id.
   /// @{
   Message(Message &&) = default;
   Message &operator=(Message &&) = default;

   Message(const Message &other);
   Message &operator=(const Message &other);
   /// @}

   std::string msgId() const;
   std::string appId() const;

   const std::list<std::string> &pushRoutes() const;

   std::chrono::seconds validFor() const;

   // TODO. Should be private. friends must be the Senders and Forwarders.
   internal_objects::Ack generate_ack() const;

   // TODO. think of way that normal users can't access this, only forwarders.
   void appendOriginNode(std::string node_name);

   virtual ~Message();

protected:
   virtual std::map<std::string, std::string> create_protocol_headers() const;

   virtual void handle_protocol_headers(std::map<std::string, std::string> &protocol_headers);
private:
   ///
   /// \brief Constructor for creating Message from decoded request.
   ///
   /// \param payload The received payload.
   /// \param push_routes The routes the message must be pushed to.
   /// \param protocol_headers The protocol headers that were received.
   /// \param user_headers Any user headers we may have received.
   Message(Payload payload, std::list<std::string> push_routes, std::map<std::string, std::string> &protocol_headers);

   void generate_new_id();

   ///
   /// \brief Name on the wire for encoding purposes.
   /// \return
   ///
   virtual const char *wireName() const final;

   static const char *_wire_name;

   std::string _msg_id;
   std::vector<std::string> _origin_nodes;
   Config _config;
};

}

#endif // BIBUNM_MESSAGE_H
