/*
  Copyright (c) 2005-2017 by Jakob Schröter <js@camaya.net>
  This file is part of the gloox library. http://camaya.net/gloox

  This software is distributed under a license. The full license
  agreement can be found in the file LICENSE in this distribution.
  This software may not be copied, modified, sold or distributed
  other than expressed in the named license agreement.

  This software is distributed without any warranty.
*/


#ifndef STANZA_H__
#define STANZA_H__

#include "gloox.h"
#include "tag.h"
#include "jid.h"
#include "stanzaextension.h"

namespace gloox
{

  class Error;

  /**
   * @brief This is the base class for XMPP stanza abstractions.
   *
   * @author Jakob Schröter <js@camaya.net>
   * @since 0.4
   */
  class GLOOX_API Stanza
  {
    public:
      /**
       * Virtual destructor.
       */
      virtual ~Stanza();

      /**
       * Sets the 'from' address of the Stanza. This useful for @link gloox::Component Components @endlink.
       * @param from The from address.
       */
      void setFrom( const JID& from ) { m_from = from; }

      /**
       * Returns the JID the stanza comes from.
       * @return The origin of the stanza.
       */
      const JID& from() const { return m_from; }

      /**
       * Returns the receiver of the stanza.
       * @return The stanza's destination.
       */
      const JID& to() const { return m_to; }

      /**
       * Returns the id of the stanza, if set.
       * @return The ID of the stanza.
       */
      const std::string& id() const { return m_id; }

      /**
       * A convenience function that returns the stanza error condition, if any.
       * @return The stanza error condition, may be 0.
       */
      const Error* error() const;

      /**
       * Retrieves the value of the xml:lang attribute of this stanza.
       * Default is 'en'.
       * @return The stanza's default language.
       */
      const std::string& xmlLang() const { return m_xmllang; }

      /**
       * Use this function to add a StanzaExtension to this Stanza.
       * @param se The StanzaExtension to add.
       * @note The Stanza will become the owner of the StanzaExtension and
       * will take care of deletion.
       * @since 0.9
       */
      void addExtension( const StanzaExtension* se );

      /**
       * Finds a StanzaExtension of a particular type.
       * @param type StanzaExtensionType to search for.
       * @return A pointer to the StanzaExtension, or 0 if none was found.
       */
      const StanzaExtension* findExtension( int type ) const;

      /**
       * Finds a StanzaExtension of a particular type.
       * Example:
       * @code
       * const MyExtension* c = presence.findExtension<MyExtension>( ExtMyExt );
       * @endcode
       * @param type The extension type to look for.
       * @return The static_cast' type, or 0 if none was found.
       */
      template< class T >
      inline const T* findExtension( int type ) const
      {
        return static_cast<const T*>( findExtension( type ) );
      }

      /**
       * Returns the list of the Stanza's extensions.
       * @return The list of the Stanza's extensions.
       */
      const StanzaExtensionList& extensions() const { return m_extensionList; }

      /**
       * Removes (deletes) all the stanza's extensions.
       */
      void removeExtensions();

      /**
       * This function is used by StanzaExtensionFactory to signal ClientBase
       * that this Stanza instance contains an embedded Stanza that needs to be
       * checked for further StanzaExtensions.
       * You should not need to use this function directly.
       */
      void setEmbeddedStanza() { m_hasEmbeddedStanza = true; }

      /**
       * This function indicates whether this Stanza instance contains an embedded
       * Stanza that needs to be checked for further StanzaExtensions.
       * You should not need to use this function directly.
       * @return Whether this Stanza instance contains an embedded
       * Stanza.
       */
      bool hasEmbeddedStanza() const { return m_hasEmbeddedStanza; }
      
      /**
       * This function returns the embedded Stanza. It is only needed by
       * ClientBase/StanzaExtensionFactory.
       * If hasEmbeddedStanza() is true, this function checks every
       * embedded StanzaExtension for an embedded Stanza and returns the first it finds.
       * You should not need to use this function directly.
       * @return The embedded Stanza. May be 0.
       */
      Stanza* embeddedStanza() const;
      
      /**
       * This function returns the embedded Tag that the embedded Stanza is based on.
       * It is only needed by ClientBase/StanzaExtensionFactory.
       * If hasEmbeddedStanza() is true, this function checks every
       * embedded StanzaExtension for an embedded Tag and returns the first it finds.
       * You should not need to use this function directly.
       *
       * @return The embedded Tag. May be 0.
       */
      Tag* embeddedTag() const;

      /**
       * Creates a Tag representation of the Stanza. The Tag is completely
       * independent of the Stanza and will not be updated when the Stanza
       * is modified.
       * @return A pointer to a Tag representation. It is the job of the caller to delete the Tag.
       */
      virtual Tag* tag() const = 0;

    protected:
      /**
       * Creates a new Stanza, taking from and to addresses from the given Tag.
       * @param tag The Tag to create the Stanza from.
       * @since 1.0
       */
      Stanza( Tag* tag );

      /**
       * Creates a new Stanza object and initializes the receiver's JID.
       * @param to The receipient of the Stanza.
       * @since 1.0
       */
      Stanza( const JID& to );

      StanzaExtensionList m_extensionList;
      std::string m_id;
      std::string m_xmllang;
      JID m_from;
      JID m_to;

      static const std::string& findLang( const StringMap* map,
                                          const std::string& defaultData,
                                          const std::string& lang );

      static void setLang( StringMap** map,
                           std::string& defaultLang,
                           const Tag* tag );

      static void setLang( StringMap** map,
                           std::string& defaultLang,
                           const std::string& data,
                           const std::string& xmllang );

      static void getLangs( const StringMap* map,
                            const std::string& defaultData,
                            const std::string& name, Tag* tag );

    private:
      Stanza( const Stanza& );
      
      bool m_hasEmbeddedStanza;

  };

}

#endif // STANZA_H__
