/*
    This file is part of Akonadi Contact.

    Copyright (c) 2007-2009 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 "collectioncombobox.h"
#include "collectioncombobox_p.h"

#include "asyncselectionhandler_p.h"
#include "collectiondialog.h"

#include <akonadi/changerecorder.h>
#include <akonadi/collectionfetchscope.h>
#include <akonadi/collectionfilterproxymodel.h>
#include <akonadi/entityrightsfiltermodel.h>
#include <akonadi/entitytreemodel.h>
#include <akonadi/session.h>

#include <kdescendantsproxymodel.h>
#include "collectionutils_p.h"

#include <QtCore/QAbstractItemModel>
#include <QtCore/QEvent>
#include <QPointer>
#include <QMouseEvent>

using namespace Akonadi;

class CollectionComboBox::Private
{
public:
    Private(QAbstractItemModel *customModel, CollectionComboBox *parent)
        : mParent(parent)
        , mMonitor(0)
        , mModel(0)
    {
        if (customModel) {
            mBaseModel = customModel;
        } else {
            mMonitor = new Akonadi::ChangeRecorder(mParent);
            mMonitor->fetchCollection(true);
            mMonitor->setCollectionMonitored(Akonadi::Collection::root());

            mModel = new EntityTreeModel(mMonitor, mParent);
            mModel->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation);

            mBaseModel = mModel;
        }

        KDescendantsProxyModel *proxyModel = new KDescendantsProxyModel(parent);
        proxyModel->setDisplayAncestorData(true);
        proxyModel->setSourceModel(mBaseModel);

        mMimeTypeFilterModel = new CollectionFilterProxyModel(parent);
        mMimeTypeFilterModel->setSourceModel(proxyModel);

        mRightsFilterModel = new EntityRightsFilterModel(parent);
        mRightsFilterModel->setSourceModel(mMimeTypeFilterModel);

        mParent->setModel(mRightsFilterModel);
        mParent->model()->sort(mParent->modelColumn());

        mSelectionHandler = new AsyncSelectionHandler(mRightsFilterModel, mParent);
        mParent->connect(mSelectionHandler, SIGNAL(collectionAvailable(QModelIndex)),
                         mParent, SLOT(activated(QModelIndex)));

        mParent->connect(mParent, SIGNAL(activated(int)),
                         mParent, SLOT(activated(int)));
    }

    ~Private()
    {
    }

    void activated(int index);
    void activated(const QModelIndex &index);

    CollectionComboBox *mParent;

    ChangeRecorder *mMonitor;
    EntityTreeModel *mModel;
    QAbstractItemModel *mBaseModel;
    CollectionFilterProxyModel *mMimeTypeFilterModel;
    EntityRightsFilterModel *mRightsFilterModel;
    AsyncSelectionHandler *mSelectionHandler;
};

void CollectionComboBox::Private::activated(int index)
{
    const QModelIndex modelIndex = mParent->model()->index(index, 0);
    if (modelIndex.isValid()) {
        emit mParent->currentChanged(modelIndex.data(EntityTreeModel::CollectionRole).value<Collection>());
    }
}

void CollectionComboBox::Private::activated(const QModelIndex &index)
{
    mParent->setCurrentIndex(index.row());
}

MobileEventHandler::MobileEventHandler(CollectionComboBox *comboBox, CollectionFilterProxyModel *mimeTypeFilter,
                                       EntityRightsFilterModel *accessRightsFilter, QAbstractItemModel *customModel)
    : QObject(comboBox)
    , mComboBox(comboBox)
    , mMimeTypeFilter(mimeTypeFilter)
    , mAccessRightsFilter(accessRightsFilter)
    , mCustomModel(customModel)
{
}

bool MobileEventHandler::eventFilter(QObject *object, QEvent *event)
{
    if (object == mComboBox && mComboBox->isEnabled() && event->type() == QEvent::MouseButtonPress) {

        const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);

        // we receive mouse events from other widgets as well, so check for ours
        if (mComboBox->rect().contains(mouseEvent->pos())) {
            QMetaObject::invokeMethod(this, "openDialog", Qt::QueuedConnection);
        }

        return true;
    }

    return QObject::eventFilter(object, event);
}

void MobileEventHandler::openDialog()
{
    QPointer<Akonadi::CollectionDialog> dialog(new Akonadi::CollectionDialog(mCustomModel));
    dialog->setMimeTypeFilter(mMimeTypeFilter->mimeTypeFilters());
    dialog->setAccessRightsFilter(mAccessRightsFilter->accessRights());

    if (dialog->exec() == QDialog::Accepted && dialog != 0) {
        const Akonadi::Collection collection = dialog->selectedCollection();
        const QModelIndex index = Akonadi::EntityTreeModel::modelIndexForCollection(mComboBox->model(), collection);
        mComboBox->setCurrentIndex(index.row());
    }
    delete dialog;
}

CollectionComboBox::CollectionComboBox(QWidget *parent)
    : KComboBox(parent)
    , d(new Private(0, this))
{
#ifdef KDEPIM_MOBILE_UI
    MobileEventHandler *handler = new MobileEventHandler(this, d->mMimeTypeFilterModel, d->mRightsFilterModel, d->mBaseModel);
    installEventFilter(handler);
#endif
}

CollectionComboBox::CollectionComboBox(QAbstractItemModel *model, QWidget *parent)
    : KComboBox(parent)
    , d(new Private(model, this))
{
#ifdef KDEPIM_MOBILE_UI
    MobileEventHandler *handler = new MobileEventHandler(this, d->mMimeTypeFilterModel, d->mRightsFilterModel, d->mBaseModel);
    installEventFilter(handler);
#endif
}

CollectionComboBox::~CollectionComboBox()
{
    delete d;
}

void CollectionComboBox::setMimeTypeFilter(const QStringList &contentMimeTypes)
{
    d->mMimeTypeFilterModel->clearFilters();
    d->mMimeTypeFilterModel->addMimeTypeFilters(contentMimeTypes);

    if (d->mMonitor) {
        foreach (const QString &mimeType, contentMimeTypes) {
            d->mMonitor->setMimeTypeMonitored(mimeType, true);
        }
    }
}

QStringList CollectionComboBox::mimeTypeFilter() const
{
    return d->mMimeTypeFilterModel->mimeTypeFilters();
}

void CollectionComboBox::setAccessRightsFilter(Collection::Rights rights)
{
    d->mRightsFilterModel->setAccessRights(rights);
}

Akonadi::Collection::Rights CollectionComboBox::accessRightsFilter() const
{
    return d->mRightsFilterModel->accessRights();
}

void CollectionComboBox::setDefaultCollection(const Collection &collection)
{
    d->mSelectionHandler->waitForCollection(collection);
}

Akonadi::Collection CollectionComboBox::currentCollection() const
{
    const QModelIndex modelIndex = model()->index(currentIndex(), 0);
    if (modelIndex.isValid()) {
        return modelIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Collection>();
    } else {
        return Akonadi::Collection();
    }
}

void CollectionComboBox::setExcludeVirtualCollections(bool b)
{
    d->mMimeTypeFilterModel->setExcludeVirtualCollections(b);
}

bool CollectionComboBox::excludeVirtualCollections() const
{
    return d->mMimeTypeFilterModel->excludeVirtualCollections();
}

#include "moc_collectioncombobox.cpp"
#include "moc_collectioncombobox_p.cpp"
