/*
    Copyright 2008 Ingo Klöcker <kloecker@kde.org>
    Copyright 2010 Laurent Montel <montel@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 "collectiondialog.h"

#include "asyncselectionhandler_p.h"

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

#include <QHeaderView>
#include <QLabel>
#include <QVBoxLayout>
#include <QCheckBox>

#include <KLineEdit>
#include <KLocalizedString>
#include <KInputDialog>
#include <KMessageBox>

using namespace Akonadi;

class CollectionDialog::Private
{
public:
    Private(QAbstractItemModel *customModel, CollectionDialog *parent, CollectionDialogOptions options)
        : mParent(parent)
        , mMonitor(0)
    {
        // setup GUI
        QWidget *widget = mParent->mainWidget();
        QVBoxLayout *layout = new QVBoxLayout(widget);
        layout->setContentsMargins(0, 0, 0, 0);

        mTextLabel = new QLabel;
        layout->addWidget(mTextLabel);
        mTextLabel->hide();

        KLineEdit *filterCollectionLineEdit = new KLineEdit(widget);
        filterCollectionLineEdit->setClearButtonShown(true);
        filterCollectionLineEdit->setClickMessage(i18nc("@info/plain Displayed grayed-out inside the "
                                                        "textbox, verb to search", "Search"));
        layout->addWidget(filterCollectionLineEdit);

        mView = new EntityTreeView;
        mView->setDragDropMode(QAbstractItemView::NoDragDrop);
        mView->header()->hide();
        layout->addWidget(mView);

        mUseByDefault = new QCheckBox(i18n("Use folder by default"));
        mUseByDefault->hide();
        layout->addWidget(mUseByDefault);

        mParent->enableButton(KDialog::Ok, false);

        // setup models
        QAbstractItemModel *baseModel;

        if (customModel) {
            baseModel = customModel;
        } else {
            mMonitor = new Akonadi::ChangeRecorder(mParent);
            mMonitor->fetchCollection(true);
            mMonitor->setCollectionMonitored(Akonadi::Collection::root());

            EntityTreeModel *model = new EntityTreeModel(mMonitor, mParent);
            model->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation);
            baseModel = model;
        }

        mMimeTypeFilterModel = new CollectionFilterProxyModel(mParent);
        mMimeTypeFilterModel->setSourceModel(baseModel);
        mMimeTypeFilterModel->setExcludeVirtualCollections(true);

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

        mFilterCollection = new KRecursiveFilterProxyModel(mParent);
        mFilterCollection->setDynamicSortFilter(true);
        mFilterCollection->setSourceModel(mRightsFilterModel);
        mFilterCollection->setFilterCaseSensitivity(Qt::CaseInsensitive);
        mView->setModel(mFilterCollection);

        changeCollectionDialogOptions(options);
        mParent->connect(filterCollectionLineEdit, SIGNAL(textChanged(QString)),
                         mParent, SLOT(slotFilterFixedString(QString)));

        mParent->connect(mView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
                         mParent, SLOT(slotSelectionChanged()));

        mParent->connect(mView, SIGNAL(doubleClicked(QModelIndex)),
                         mParent, SLOT(slotDoubleClicked()));

        mSelectionHandler = new AsyncSelectionHandler(mFilterCollection, mParent);
        mParent->connect(mSelectionHandler, SIGNAL(collectionAvailable(QModelIndex)),
                         mParent, SLOT(slotCollectionAvailable(QModelIndex)));
        readConfig();
    }

    ~Private()
    {
        writeConfig();
    }

    void slotCollectionAvailable(const QModelIndex &index)
    {
        mView->expandAll();
        mView->setCurrentIndex(index);
    }

    void slotFilterFixedString(const QString &filter)
    {
        mFilterCollection->setFilterFixedString(filter);
        if (mKeepTreeExpanded) {
            mView->expandAll();
        }
    }

    void readConfig()
    {
        KConfig config( QLatin1String( "akonadi_contactrc" ) );
        KConfigGroup group( &config, QLatin1String( "CollectionDialog" ) );
        const QSize size = group.readEntry( "Size", QSize(800,  500) );
        if ( size.isValid() ) {
            mParent->resize( size );
        }
    }

    void writeConfig()
    {
        KConfig config( QLatin1String( "akonadi_contactrc" ) );
        KConfigGroup group( &config, QLatin1String( "CollectionDialog" ) );
        group.writeEntry( "Size", mParent->size() );
        group.sync();
    }

    CollectionDialog *mParent;

    ChangeRecorder *mMonitor;
    CollectionFilterProxyModel *mMimeTypeFilterModel;
    EntityRightsFilterModel *mRightsFilterModel;
    EntityTreeView *mView;
    AsyncSelectionHandler *mSelectionHandler;
    QLabel *mTextLabel;
    bool mAllowToCreateNewChildCollection;
    bool mKeepTreeExpanded;
    KRecursiveFilterProxyModel *mFilterCollection;
    QCheckBox *mUseByDefault;
    QStringList mContentMimeTypes;

    void slotDoubleClicked();
    void slotSelectionChanged();
    void slotAddChildCollection();
    void slotCollectionCreationResult(KJob *job);
    bool canCreateCollection(const Akonadi::Collection &parentCollection) const;
    void changeCollectionDialogOptions(CollectionDialogOptions options);
    bool canSelectCollection() const;
};

void CollectionDialog::Private::slotDoubleClicked()
{
    if (canSelectCollection()) {
       mParent->accept();
    }
}

bool CollectionDialog::Private::canSelectCollection() const
{
    bool result = (mView->selectionModel()->selectedIndexes().count() > 0);
    if (mAllowToCreateNewChildCollection) {
        const Akonadi::Collection parentCollection = mParent->selectedCollection();

        if (parentCollection.isValid()) {
            result = (parentCollection.rights() & Akonadi::Collection::CanCreateItem);
        }
    }
    return result;
}

void CollectionDialog::Private::slotSelectionChanged()
{
    mParent->enableButton(KDialog::Ok, mView->selectionModel()->selectedIndexes().count() > 0);
    if (mAllowToCreateNewChildCollection) {
        const Akonadi::Collection parentCollection = mParent->selectedCollection();
        const bool canCreateChildCollections = canCreateCollection(parentCollection);

        mParent->enableButton(KDialog::User1, (canCreateChildCollections && !parentCollection.isVirtual()));
        if (parentCollection.isValid()) {
            const bool canCreateItems = (parentCollection.rights() & Akonadi::Collection::CanCreateItem);
            mParent->enableButton(KDialog::Ok, canCreateItems);
        }
    }
}

void CollectionDialog::Private::changeCollectionDialogOptions(CollectionDialogOptions options)
{
    mAllowToCreateNewChildCollection = (options & AllowToCreateNewChildCollection);
    if (mAllowToCreateNewChildCollection) {
        mParent->setButtons(Ok | Cancel | User1);
        mParent->setButtonGuiItem(User1, KGuiItem(i18n("&New Subfolder..."), QLatin1String("folder-new"),
                                                  i18n("Create a new subfolder under the currently selected folder")));
        mParent->enableButton(KDialog::User1, false);
        connect(mParent, SIGNAL(user1Clicked()), mParent, SLOT(slotAddChildCollection()));
    }
    mKeepTreeExpanded = (options & KeepTreeExpanded);
    if (mKeepTreeExpanded) {
        mParent->connect(mRightsFilterModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
                         mView, SLOT(expandAll()), Qt::UniqueConnection);
        mView->expandAll();
    }
}

bool CollectionDialog::Private::canCreateCollection(const Akonadi::Collection &parentCollection) const
{
    if (!parentCollection.isValid()) {
        return false;
    }

    if ((parentCollection.rights() & Akonadi::Collection::CanCreateCollection)) {
        const QStringList dialogMimeTypeFilter = mParent->mimeTypeFilter();
        const QStringList parentCollectionMimeTypes = parentCollection.contentMimeTypes();
        Q_FOREACH (const QString &mimetype, dialogMimeTypeFilter) {
            if (parentCollectionMimeTypes.contains(mimetype)) {
                return true;
            }
        }
        return true;
    }
    return false;
}

void CollectionDialog::Private::slotAddChildCollection()
{
    const Akonadi::Collection parentCollection = mParent->selectedCollection();
    if (canCreateCollection(parentCollection)) {
        const QString name = KInputDialog::getText(i18nc("@title:window", "New Folder"),
                                                   i18nc("@label:textbox, name of a thing", "Name"),
                                                   QString(), 0, mParent);
        if (name.isEmpty()) {
            return;
        }

        Akonadi::Collection collection;
        collection.setName(name);
        collection.setParentCollection(parentCollection);
        if (!mContentMimeTypes.isEmpty()) {
            collection.setContentMimeTypes(mContentMimeTypes);
        }
        Akonadi::CollectionCreateJob *job = new Akonadi::CollectionCreateJob(collection);
        connect(job, SIGNAL(result(KJob*)), mParent, SLOT(slotCollectionCreationResult(KJob*)));
    }
}

void CollectionDialog::Private::slotCollectionCreationResult(KJob *job)
{
    if (job->error()) {
        KMessageBox::error(mParent, i18n("Could not create folder: %1", job->errorString()),
                           i18n("Folder creation failed"));
    }
}

CollectionDialog::CollectionDialog(QWidget *parent)
    : KDialog(parent)
    , d(new Private(0, this, CollectionDialog::None))
{
}

CollectionDialog::CollectionDialog(QAbstractItemModel *model, QWidget *parent)
    : KDialog(parent)
    , d(new Private(model, this, CollectionDialog::None))
{
}

CollectionDialog::CollectionDialog(CollectionDialogOptions options, QAbstractItemModel *model, QWidget *parent)
    : KDialog(parent)
    , d(new Private(model, this, options))
{
}

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

Akonadi::Collection CollectionDialog::selectedCollection() const
{
    if (selectionMode() == QAbstractItemView::SingleSelection) {
        const QModelIndex index = d->mView->currentIndex();
        if (index.isValid()) {
            return index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
        }
    }

    return Collection();
}

Akonadi::Collection::List CollectionDialog::selectedCollections() const
{
    Collection::List collections;
    const QItemSelectionModel *selectionModel = d->mView->selectionModel();
    const QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
    foreach (const QModelIndex &index, selectedIndexes) {
        if (index.isValid()) {
            const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
            if (collection.isValid()) {
                collections.append(collection);
            }
        }
    }

    return collections;
}

void CollectionDialog::setMimeTypeFilter(const QStringList &mimeTypes)
{
    if (mimeTypeFilter() == mimeTypes) {
        return;
    }

    d->mMimeTypeFilterModel->clearFilters();
    d->mMimeTypeFilterModel->addMimeTypeFilters(mimeTypes);

    if (d->mMonitor) {
        foreach (const QString &mimetype, mimeTypes) {
            d->mMonitor->setMimeTypeMonitored(mimetype);
        }
    }
}

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

void CollectionDialog::setAccessRightsFilter(Collection::Rights rights)
{
    if (accessRightsFilter() == rights) {
        return;
    }
    d->mRightsFilterModel->setAccessRights(rights);
}

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

void CollectionDialog::setDescription(const QString &text)
{
    d->mTextLabel->setText(text);
    d->mTextLabel->show();
}

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

void CollectionDialog::setSelectionMode(QAbstractItemView::SelectionMode mode)
{
    d->mView->setSelectionMode(mode);
}

QAbstractItemView::SelectionMode CollectionDialog::selectionMode() const
{
    return d->mView->selectionMode();
}

void CollectionDialog::changeCollectionDialogOptions(CollectionDialogOptions options)
{
    d->changeCollectionDialogOptions(options);
}

void CollectionDialog::setUseFolderByDefault(bool b)
{
    d->mUseByDefault->setChecked(b);
    d->mUseByDefault->show();
}

bool CollectionDialog::useFolderByDefault() const
{
    return d->mUseByDefault->isChecked();
}

void CollectionDialog::setContentMimeTypes(const QStringList &mimetypes)
{
    d->mContentMimeTypes = mimetypes;
}


#include "moc_collectiondialog.cpp"
