/*
 * This file is part of people
 *
 * Copyright (C) 2010 Collabora Ltd. <info@collabora.co.uk>
 *   @author George Goldberg <george.goldberg@collabora.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef LIB_PEOPLE_PERSON_SET_H
#define LIB_PEOPLE_PERSON_SET_H

#include "abstract-person-set-interface.h"
#include "person.h"

#include <KDebug>

#include <QtCore/QObject>
#include <QtCore/QSharedPointer>

/**
 * @class PersonSet
 *
 * This class represents a group of Person objects, allowing bulk actions to be performed on them
 * and for a group of Persons to be stored in a single object. It also emits signals when Persons
 * are added and removed from the PersonSet, allowing subclasses which provide dynamically updated
 * sets of Persons.
 */
class PersonSet : public QObject {

    Q_OBJECT

public:
    explicit PersonSet(QObject *parent = 0);
    virtual ~PersonSet();

    /**
     * @brief get extension interfaces for the PersonSet.
     * @return a QSharedPointer containing the interface.
     *
     * This method should be used to request arbitrary interfaces (extensions) of PersonSet
     * functionality. This interfaces will be provided by libraries that extend the People library,
     * for example the KTelepathy library.
     *
     * Example: The KTelepathy library provides a TelepathyPersonSetInterface class which can be
     * obtained from the PersonSet by the following code snippet:
     *
     * @code
     *
     * #include <People/PersonSet>
     * #include <KTelepathy/TelepathyPersonSetInterface>
     *
     * PersonSetPtr ps(new PersonSet);
     *
     * QSharedPointer<TelepathyPersonSetInterface> ti = ps->interface<TelepathyPersonSetInterface>();
     *
     * @endcode
     *
     * The returned interface is cached and ref-counted by the PersonSet. It should always
     * be stored and used within its QSharedPointer container and should never be passed around
     * as a raw pointer. Once finished with, the QSharedPointer should be deleted/allowed to go
     * out of scope, but the contained pointer should *never* be deleted.
     */
    template <class AbstractPersonSetInterfaceSubclass>
    inline QSharedPointer<AbstractPersonSetInterfaceSubclass> interface()
    {
        kDebug();

        // See if the interface cache contains an interface of the right type.
        Q_FOREACH (QWeakPointer<AbstractPersonSetInterface> iface, cachedInterfaces()) {
            // If the iface is null, move to the next iteration.
            if (iface.isNull()) {
                kDebug() << "Null interface. Next please...";
                continue;
            }

            // See if the AbstractInterface is an instance of the correct subclass.
            QSharedPointer<AbstractPersonSetInterfaceSubclass> i = qSharedPointerObjectCast<AbstractPersonSetInterfaceSubclass>(iface);

            if (i.isNull()) {
                kDebug() << "Wrong interface type. Next please...";
                continue;
            }

            // Everything is OK, so use the cached interface.
            kDebug() << "Found the right cached interface. Returning.";
            return i;
        }

        kDebug() << "Didn't find the right cached interface. Create a new one.";

        // Create a new instance of the interface, with this object as the parent
        AbstractPersonSetInterfaceSubclass *i = new AbstractPersonSetInterfaceSubclass();

        // Check that it is really a child class of the right abstract interface
        // FIXME: This feels like a bit of a hack. Any better way of causing a compile failure?
        AbstractPersonSetInterface *ai = static_cast<AbstractPersonSetInterface*>(i);

        if (!ai) {
            // This is not a valid class to ask for.
            kWarning() << "Tried to get interface of invalid type.";
            return QSharedPointer<AbstractPersonSetInterfaceSubclass>();
        }

        // Interface is OK. Cache it.
        QSharedPointer<AbstractPersonSetInterfaceSubclass> newIface(i);
        cacheInterface(qSharedPointerCast<AbstractPersonSetInterface>(newIface));

        kDebug() << "Cached new interface.";

        return newIface;
    }

    /**
     * @brief add a new person to the set.
     *
     * Add a new person to the Set. The personAdded signal will be emitted if the person added to
     * this set is actually new and not already in the set.
     */
    void addPerson(const PersonPtr &person);

    /**
     * @brief remove a person from the set.
     *
     * Remove a person from the set. The personRemoved signal will be emitted if the person removed
     * from the set is actually in the set to be removed from it.
     */
    void removePerson(const PersonPtr &person);

    /**
     * Overload to remove a person by Nepomuk resource URI only.
     */
    void removePerson(const QUrl &uri);

    /**
     * @brief Get all the people who are stored in this set.
     * @return a QSet containing PersonPtrs to all the people currently stored in this set.
     *
     * This method provides a QSet with QSharedPointers to all the Person objects currently
     * stored in this PersonSet.
     */
    QSet<PersonPtr> people() const;

Q_SIGNALS:
    /**
     * @brief emitted when a new person is added to this PersonSet.
     */
    void personAdded(const PersonPtr &person);

    /**
     * @brief emitted when a person is removed from this PersonSet.
     */
    void personRemoved(const PersonPtr &person);

private:
    class Private;
    Private * const d;

    QList<QWeakPointer<AbstractPersonSetInterface> > cachedInterfaces() const;
    void cacheInterface(QSharedPointer<AbstractPersonSetInterface> interface);

};

typedef QSharedPointer<PersonSet> PersonSetPtr;


#endif  // Header guard

