text: #pragma once
  
  
  #include <xmpp/adhoc_commands_handler.hpp>
  #include <network/tcp_socket_handler.hpp>
  #include <xmpp/xmpp_parser.hpp>
  #include <xmpp/body.hpp>
  
  #include <unordered_map>
  #include <memory>
  #include <string>
  #include <ctime>
  #include <map>
  
  #define STREAM_NS        "http://etherx.jabber.org/streams"
  #define COMPONENT_NS     "jabber:component:accept"
  #define MUC_NS           "http://jabber.org/protocol/muc"
  #define MUC_USER_NS      MUC_NS"#user"
  #define MUC_ADMIN_NS     MUC_NS"#admin"
  #define DISCO_NS         "http://jabber.org/protocol/disco"
  #define DISCO_ITEMS_NS   DISCO_NS"#items"
  #define DISCO_INFO_NS    DISCO_NS"#info"
  #define XHTMLIM_NS       "http://jabber.org/protocol/xhtml-im"
  #define STANZA_NS        "urn:ietf:params:xml:ns:xmpp-stanzas"
  #define STREAMS_NS       "urn:ietf:params:xml:ns:xmpp-streams"
  #define VERSION_NS       "jabber:iq:version"
  #define ADHOC_NS         "http://jabber.org/protocol/commands"
  #define PING_NS          "urn:xmpp:ping"
  #define DELAY_NS         "urn:xmpp:delay"
  #define MAM_NS           "urn:xmpp:mam:1"
  #define MAM_NS0           "urn:xmpp:mam:0"
  
  #define FORWARD_NS       "urn:xmpp:forward:0"
  #define CLIENT_NS        "jabber:client"
  #define DATAFORM_NS      "jabber:x:data"
  #define RSM_NS           "http://jabber.org/protocol/rsm"
  #define MUC_TRAFFIC_NS   "http://jabber.org/protocol/muc#traffic"
  
  /**
   * An XMPP component, communicating with an XMPP server using the protocole
   * described in XEP-0114: Jabber Component Protocol
   *
   * TODO: implement XEP-0225: Component Connections
   */
  class XmppComponent: public TCPSocketHandler
  {
  public:
    explicit XmppComponent(std::shared_ptr<Poller> poller, const std::string& hostname, const std::string& secret);
    virtual ~XmppComponent() = default;
  
    XmppComponent(const XmppComponent&) = delete;
    XmppComponent(XmppComponent&&) = delete;
    XmppComponent& operator=(const XmppComponent&) = delete;
    XmppComponent& operator=(XmppComponent&&) = delete;
  
    void on_connection_failed(const std::string& reason) override final;
    void on_connected() override final;
    void on_connection_close(const std::string& error) override final;
    void parse_in_buffer(const size_t size) override final;
  
    /**
     * Returns a unique id, to be used in the 'id' element of our iq stanzas.
     */
    static std::string next_id();
    bool is_document_open() const;
    /**
     * Connect to the XMPP server.
     */
    void start();
    /**
     * Reset the component so we can use the component on a new XMPP stream
     */
    void reset();
    /**
     * Serialize the stanza and add it to the out_buf to be sent to the
     * server.
     */
    void send_stanza(const Stanza& stanza);
    /**
     * Handle the opening of the remote stream
     */
    void on_remote_stream_open(const XmlNode& node);
    /**
     * Handle the closing of the remote stream
     */
    void on_remote_stream_close(const XmlNode& node);
    /**
     * Handle received stanzas
     */
    void on_stanza(const Stanza& stanza);
    /**
     * Send an error stanza. Message being the name of the element inside the
     * stanza, and explanation being a short human-readable sentence
     * describing the error.
     */
    void send_stream_error(const std::string& message, const std::string& explanation);
    /**
     * Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error
     */
    void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
                           const std::string& id, const std::string& error_type,
                           const std::string& defined_condition, const std::string& text,
                           const bool fulljid=true);
    /**
     * Send the closing signal for our document (not closing the connection though).
     */
    void close_document();
    /**
     * Send a message from from@served_hostname, with the given body
     *
     * If fulljid is false, the provided 'from' doesn't contain the
     * server-part of the JID and must be added.
     */
    void send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
                      const std::string& type, const bool fulljid, const bool nocopy=false);
    /**
     * Send a join from a new participant
     */
    void send_user_join(const std::string& from,
                        const std::string& nick,
                        const std::string& realjid,
                        const std::string& affiliation,
                        const std::string& role,
                        const std::string& to,
                        const bool self);
    /**
     * Send an error to indicate that the user tried to join an invalid room
     */
    void send_invalid_room_error(const std::string& muc_jid,
                                 const std::string& nick,
                                 const std::string& to);
    /**
     * Send an error to indicate that the user tried to send a message to an
     * invalid user.
     */
    void send_invalid_user_error(const std::string& user_name,
                                 const std::string& to);
    /**
     * Send the MUC topic to the user
     */
    void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to, const std::string& who);
    /**
     * Send a (non-private) message to the MUC
     */
    void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to);
    /**
     * Send a message, with a <delay/> element, part of a MUC history
     */
    void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
                              const std::string& jid_to, const std::time_t timestamp);
    /**
     * Send an unavailable presence for this nick
     */
    void send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self);
    /**
     * Indicate that a participant changed his nick
     */
    void send_nick_change(const std::string& muc_name,
                          const std::string& old_nick,
                          const std::string& new_nick,
                          const std::string& affiliation,
                          const std::string& role,
                          const std::string& jid_to,
                          const bool self);
    /**
     * An user is kicked from a room
     */
    void kick_user(const std::string& muc_name, const std::string& target, const std::string& reason,
                   const std::string& author, const std::string& jid_to, const bool self);
    /**
     * Send a generic presence error
     */
    void send_presence_error(const std::string& muc_name,
                             const std::string& nickname,
                             const std::string& jid_to,
                             const std::string& type,
                             const std::string& condition,
                             const std::string& error_code,
                             const std::string& text);
    /**
     * Send a presence from the MUC indicating a change in the role and/or
     * affiliation of a participant
     */
    void send_affiliation_role_change(const std::string& muc_name,
                                      const std::string& target,
                                      const std::string& affiliation,
                                      const std::string& role,
                                      const std::string& jid_to);
    /**
     * Send a result IQ with the gateway disco informations.
     */
    void send_self_disco_info(const std::string& id, const std::string& jid_to);
    /**
     * Send a result IQ with the given version, or the gateway version if the
     * passed string is empty.
     */
    void send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
                      const std::string& version="");
    /**
     * Send the list of all available ad-hoc commands to that JID. The list is
     * different depending on what JID made the request.
     */
    void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid,
                                  const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler);
    /**
     * Send an iq version request
     */
    void send_iq_version_request(const std::string& from,
                                 const std::string& jid_to);
    /**
     * Send an empty iq of type result
     */
    void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from);
    void send_iq_result_full_jid(const std::string& id, const std::string& to_jid,
                                 const std::string& from_full_jid);
  
    void handle_handshake(const Stanza& stanza);
    void handle_error(const Stanza& stanza);
  
    virtual void after_handshake() {}
  
    const std::string& get_served_hostname() const
    { return this->served_hostname; }
  
    /**
     * Whether or not we ever succeeded our authentication to the XMPP server
     */
    bool ever_auth;
    /**
     * Whether or not this is the first consecutive try on connecting to the
     * XMPP server.  We use this to delay the connection attempt for a few
     * seconds, if it is not the first try.
     */
    bool first_connection_try;
  
  private:
    /**
     * Return a buffer provided by the XML parser, to read data directly into
     * it, and avoiding some unnecessary copy.
     */
    void* get_receive_buffer(const size_t size) const override final;
    XmppParser parser;
    std::string stream_id;
    std::string secret;
    bool authenticated;
    /**
     * Whether or not OUR XMPP document is open
     */
    bool doc_open;
  protected:
    std::string served_hostname;
  
    std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
    AdhocCommandsHandler adhoc_commands_handler;
  };
  
  
  