/*
 * 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_PEOPLE_MANAGER_H
#define LIB_PEOPLE_PEOPLE_MANAGER_H

#include "abstract-people-manager-interface.h"

#include <KDebug>

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

/**
 * @class PeopleManager
 *
 * This class represents the central store of all people. It is the starting point for all
 * applications wishing to interact with People.
 */
class PeopleManager : public QObject {

    Q_OBJECT

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

    /**
     * @brief get extension interfaces for the PeopleManager.
     * @return a QSharedPointer containing the interface.
     *
     * This method should be used to request arbitrary interfaces (extensions) of PeopleManager
     * functionality. This interfaces will be provided by libraries that extend the People library,
     * for example the KTelepathy library.
     *
     * Example: The KTelepathy library provides a TelepathyPeopleManagerInterface class which can be
     * obtained from the PeopleManager by the following code snippet:
     *
     * @code
     * 
     * #include <People/PeopleManager>
     * #include <KTelepathy/TelepathyPeopleManagerInterface>
     *
     * PeopleManagerPtr pm(new PeopleManager);
     *
     * QSharedPointer<TelepathyPeopleManagerInterface> ti = pm->interface<TelepathyPeopleManagerInterface>();
     * 
     * @endcode
     *
     * The returned interface is cached and ref-counted by the PeopleManager. 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 AbstractPeopleManagerInterfaceSubclass>
    inline QSharedPointer<AbstractPeopleManagerInterfaceSubclass> interface()
    {
        kDebug();

        // See if the interface cache contains an interface of the right type.
        Q_FOREACH (QWeakPointer<AbstractPeopleManagerInterface> 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<AbstractPeopleManagerInterfaceSubclass> i = qSharedPointerObjectCast<AbstractPeopleManagerInterfaceSubclass>(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
        AbstractPeopleManagerInterfaceSubclass *i = new AbstractPeopleManagerInterfaceSubclass();

        // 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?
        AbstractPeopleManagerInterface *ai = static_cast<AbstractPeopleManagerInterface*>(i);

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

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

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

        return newIface;
    }

private:
    class Private;
    Private * const d;

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

};


#endif  // Header guard

