/*
 * 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_H
#define LIB_PEOPLE_PERSON_H

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

#include <KDebug>

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


/**
 * @class Person
 *
 * This class is a subclass of Entity, representing a Person object (basically a wrapper around a
 * PIMO:Person in the Nepomuk store).
 */
class Person : public Entity {

    Q_OBJECT

public:
    explicit Person(const Nepomuk::Resource &pimoPerson, QObject *parent = 0);
    virtual ~Person();

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

        // If the Person is invalid, return 0.
        if (!isValid()) {
            kDebug() << "Person is invalid. Return invalid interface";
            return QSharedPointer<AbstractPersonInterfaceSubclass>();
        }

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

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

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

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

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

        return newIface;
    }

private:
    class Private;
    Private * const d;

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

};

typedef QSharedPointer<Person> PersonPtr;

uint qHash(Person key);
uint qHash(PersonPtr key);


#endif  // Header guard

