/*
    Copyright (c) 2014 Christian Mollekopf <mollekopf@kolabsys.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#ifndef AKONADI_ATTRIBUTEENTITY_H
#define AKONADI_ATTRIBUTEENTITY_H

#include <akonadi/job.h>
#include <QVector>
#include <qsharedpointer.h>
#include "attribute.h"
#include <kdebug.h>

namespace Akonadi {

/**
 * Parent class for entities that can have attributes.
 * This is supposed to eventually share the code between Akonadi::Tag and Akonadi::Entity.
 *
 * In the current form using this in Akonadi::Entity would break the implicit sharing of it's private class,
 * so AttributeEntity::Private would need to become a parent class of EntityPrivate and use the same clone()
 * calls etc.
 * An even better solution is probably ot make AttributeEntity a private member of Entity, with all Attribute related member functions forwarding to this class.
 */
class AKONADI_EXPORT AttributeEntity {
public:
    AttributeEntity();

    AttributeEntity(const AttributeEntity &);
    virtual ~AttributeEntity();

    //Each subclass must override this to avoid slicing
    virtual AttributeEntity &operator=(const AttributeEntity &);

    /**
     * Adds an attribute to the entity.
     *
     * If an attribute of the same type name already exists, it is deleted and
     * replaced with the new one.
     *
     * @param attribute The new attribute.
     *
     * @note The entity takes the ownership of the attribute.
     */
    void addAttribute( Attribute *attribute );

    /**
     * Removes and deletes the attribute of the given type @p name.
     */
    void removeAttribute( const QByteArray &name );

    /**
     * Returns @c true if the entity has an attribute of the given type @p name,
     * false otherwise.
     */
    bool hasAttribute( const QByteArray &name ) const;

    /**
     * Returns a list of all attributes of the entity.
     */
    Attribute::List attributes() const;

    /**
     * Removes and deletes all attributes of the entity.
     */
    void clearAttributes();

    /**
     * Returns the attribute of the given type @p name if available, 0 otherwise.
     */
    Attribute* attribute( const QByteArray &name ) const;

    /**
     * Describes the options that can be passed to access attributes.
     */
    enum CreateOption {
      AddIfMissing    ///< Creates the attribute if it is missing
    };

    /**
     * Returns the attribute of the requested type.
     * If the entity has no attribute of that type yet, a new one
     * is created and added to the entity.
     *
     * @param option The create options.
     */
    template <typename T> inline T* attribute( CreateOption option )
    {
      Q_UNUSED( option );

      const T dummy;
      if ( hasAttribute( dummy.type() ) ) {
        T* attr = dynamic_cast<T*>( attribute( dummy.type() ) );
        if ( attr ) {
          return attr;
        }
        kWarning( 5250 ) << "Found attribute of unknown type" << dummy.type()
          << ". Did you forget to call AttributeFactory::registerAttribute()?";
      }

      T* attr = new T();
      addAttribute( attr );
      return attr;
    }

    /**
     * Returns the attribute of the requested type or 0 if it is not available.
     */
    template <typename T> inline T* attribute() const
    {
      const T dummy;
      if ( hasAttribute( dummy.type() ) ) {
        T* attr = dynamic_cast<T*>( attribute( dummy.type() ) );
        if ( attr )
          return attr;
        kWarning( 5250 ) << "Found attribute of unknown type" << dummy.type()
          << ". Did you forget to call AttributeFactory::registerAttribute()?";
      }

      return 0;
    }

    /**
     * Removes and deletes the attribute of the requested type.
     */
    template <typename T> inline void removeAttribute()
    {
      const T dummy;
      removeAttribute( dummy.type() );
    }

    /**
     * Returns whether the entity has an attribute of the requested type.
     */
    template <typename T> inline bool hasAttribute() const
    {
      const T dummy;
      return hasAttribute( dummy.type() );
    }
private:
    friend class TagModifyJob;
    QSet<QByteArray> &removedAttributes() const;

    class Private;
    QSharedPointer<Private> d_ptr;
};

}

#endif