/*
    Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>

    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.
*/

#include "entity.h"
#include "entity_p.h"
#include "collection.h"

#include <kglobal.h>

using namespace Akonadi;

K_GLOBAL_STATIC(Akonadi::Collection, s_defaultParentCollection)

/**
 * Helper method for assignment operator and copy constructor.
 */
static void assignEntityPrivate(QSharedDataPointer<EntityPrivate> &one, const QSharedDataPointer<EntityPrivate> &other)
{
    // We can't simply do one = other here, we have to use a temp.
    // Otherwise ProtocolHelperTest::testParentCollectionAfterCollectionParsing()
    // will break.
    //
    // The reason are assignments like
    //   col = col.parentCollection()
    //
    // Here, parentCollection() actually returns a reference to a pointer owned
    // by col. So when col (or rather, it's private class) is deleted, the pointer
    // to the parent collection and therefore the reference becomes invalid.
    //
    // With a single-line assignment here, the parent collection would be deleted
    // before it is assigned, and therefore the resulting object would point to
    // uninitalized memory.
    QSharedDataPointer<EntityPrivate> temp = other;
    one = temp;
}

Entity::Entity(const Entity &other)
{
    assignEntityPrivate(d_ptr, other.d_ptr);
}

Entity::Entity(EntityPrivate *dd)
    : d_ptr(dd)
{
}

Entity::~Entity()
{
}

void Entity::setId(Id id)
{
    d_ptr->mId = id;
}

Entity::Id Entity::id() const
{
    return d_ptr->mId;
}

void Entity::setRemoteId(const QString &id)
{
    d_ptr->mRemoteId = id;
}

QString Entity::remoteId() const
{
    return d_ptr->mRemoteId;
}

void Entity::setRemoteRevision(const QString &revision)
{
    d_ptr->mRemoteRevision = revision;
}

QString Entity::remoteRevision() const
{
    return d_ptr->mRemoteRevision;
}

bool Entity::isValid() const
{
    return (d_ptr->mId >= 0);
}

bool Entity::operator==(const Entity &other) const
{
    // Invalid items are the same, no matter what their internal ID is
    return (!isValid() && !other.isValid()) || (d_ptr->mId == other.d_ptr->mId);
}

bool Akonadi::Entity::operator!=(const Entity &other) const
{
    return (isValid() || other.isValid()) && (d_ptr->mId != other.d_ptr->mId);
}

Entity &Entity ::operator=(const Entity &other)
{
    if (this != &other) {
        assignEntityPrivate(d_ptr, other.d_ptr);
    }

    return *this;
}

bool Akonadi::Entity::operator<(const Entity &other) const
{
    return d_ptr->mId < other.d_ptr->mId;
}

void Entity::addAttribute(Attribute *attr)
{
    Q_ASSERT(attr);
    Attribute *existing = d_ptr->mAttributes.value(attr->type());
    if (existing) {
        if (attr == existing) {
            return;
        }
        d_ptr->mAttributes.remove(attr->type());
        delete existing;
    }
    d_ptr->mAttributes.insert(attr->type(), attr);
    d_ptr->mDeletedAttributes.remove(attr->type());
}

void Entity::removeAttribute(const QByteArray &type)
{
    d_ptr->mDeletedAttributes.insert(type);
    delete d_ptr->mAttributes.take(type);
}

bool Entity::hasAttribute(const QByteArray &type) const
{
    return d_ptr->mAttributes.contains(type);
}

Attribute::List Entity::attributes() const
{
    return d_ptr->mAttributes.values();
}

void Akonadi::Entity::clearAttributes()
{
    foreach (Attribute *attr, d_ptr->mAttributes) {
        d_ptr->mDeletedAttributes.insert(attr->type());
        delete attr;
    }
    d_ptr->mAttributes.clear();
}

Attribute *Entity::attribute(const QByteArray &type) const
{
    return d_ptr->mAttributes.value(type);
}

uint qHash(const Akonadi::Entity &entity)
{
    return qHash(entity.id());
}

Collection &Entity::parentCollection()
{
    if (!d_ptr->mParent) {
        d_ptr->mParent = new Collection();
    }
    return *(d_ptr->mParent);
}

Collection Entity::parentCollection() const
{
    if (!d_ptr->mParent) {
        return *(s_defaultParentCollection);
    } else {
        return *(d_ptr->mParent);
    }
}

void Entity::setParentCollection(const Collection &parent)
{
    delete d_ptr->mParent;
    d_ptr->mParent = new Collection(parent);
}

AKONADI_DEFINE_PRIVATE(Entity)
