//
/***************************************************************************
                          kmsystemtray.cpp  -  description
                             ------------------
    begin                : Fri Aug 31 22:38:44 EDT 2001
    copyright            : (C) 2001 by Ryan Breen
    email                : ryan@porivo.com

    Copyright (c) 2010-2017 Montel Laurent <montel@kde.org>
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kmsystemtray.h"
#include "kmmainwidget.h"
#include "settings/kmailsettings.h"
#include "mailcommon/mailutil.h"
#include "MailCommon/MailKernel"
#include "MailCommon/FolderTreeView"
#include <AkonadiCore/NewMailNotifierAttribute>

#include <kiconloader.h>
#include <kwindowsystem.h>
#include "kmail_debug.h"
#include <QMenu>
#include <KLocalizedString>
#include <QAction>
#include "widgets/kactionmenutransport.h"

#include <AkonadiCore/ChangeRecorder>
#include <AkonadiCore/EntityTreeModel>
#include <AkonadiCore/EntityMimeTypeFilterModel>

using namespace MailCommon;

/**
 * Construct a KSystemTray icon to be displayed when new mail
 * has arrived in a non-system folder.  The KMSystemTray listens
 * for updateNewMessageNotification events from each non-system
 * KMFolder and maintains a store of all folders with unread
 * messages.
 *
 * The KMSystemTray also provides a popup menu listing each folder
 * with its count of unread messages, allowing the user to jump
 * to the first unread message in each folder.
 */
using namespace KMail;

KMSystemTray::KMSystemTray(QObject *parent)
    : KStatusNotifierItem(parent)
{
    qCDebug(KMAIL_LOG) << "Initting systray";
    setToolTipTitle(i18n("KMail"));
    setToolTipIconByName(QStringLiteral("kmail"));
    setIconByName(QStringLiteral("kmail"));

    KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
    if (mainWidget) {
        QWidget *mainWin = mainWidget->window();
        if (mainWin) {
            mDesktopOfMainWin = KWindowInfo(mainWin->winId(), NET::WMDesktop).desktop();
        }
    }

    connect(this, &KMSystemTray::activateRequested, this, &KMSystemTray::slotActivated);
    connect(contextMenu(), &QMenu::aboutToShow,
            this, &KMSystemTray::slotContextMenuAboutToShow);

    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionStatisticsChanged, this, &KMSystemTray::slotCollectionStatisticsChanged);

    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionAdded, this, &KMSystemTray::initListOfCollection);
    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionRemoved, this, &KMSystemTray::initListOfCollection);
    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionSubscribed, this, &KMSystemTray::initListOfCollection);
    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionUnsubscribed, this, &KMSystemTray::initListOfCollection);

    initListOfCollection();
}

bool KMSystemTray::buildPopupMenu()
{
    KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
    if (!mainWidget || kmkernel->shuttingDown()) {
        return false;
    }

    if (!contextMenu()) {
        setContextMenu(new QMenu());
    }

    contextMenu()->clear();
    contextMenu()->setIcon(qApp->windowIcon());
    contextMenu()->setTitle(i18n("KMail"));
    QAction *action = nullptr;
    if ((action = mainWidget->action(QStringLiteral("check_mail")))) {
        contextMenu()->addAction(action);
    }
    if ((action = mainWidget->action(QStringLiteral("check_mail_in")))) {
        contextMenu()->addAction(action);
    }

    mSendQueued = mainWidget->sendQueuedAction();
    contextMenu()->addAction(mSendQueued);
    contextMenu()->addAction(mainWidget->sendQueueViaMenu());

    contextMenu()->addSeparator();
    if ((action = mainWidget->action(QStringLiteral("new_message")))) {
        contextMenu()->addAction(action);
    }
    if ((action = mainWidget->action(QStringLiteral("kmail_configure_kmail")))) {
        contextMenu()->addAction(action);
    }
    contextMenu()->addSeparator();
    if ((action = mainWidget->action(QStringLiteral("akonadi_work_offline")))) {
        contextMenu()->addAction(action);
        contextMenu()->addSeparator();
    }

    if ((action = mainWidget->action(QStringLiteral("file_quit")))) {
        contextMenu()->addAction(action);
    }
    return true;
}

KMSystemTray::~KMSystemTray()
{
}

void KMSystemTray::slotGeneralFontChanged()
{
    updateSystemTray();
}

/**
 * Update the count of unread messages.  If there are unread messages,
 * show the "unread new mail" KMail icon.
 * If there is no unread mail, restore the normal KMail icon.
 */
void KMSystemTray::updateCount()
{
    if (mCount == 0) {
        setIconByName(QStringLiteral("kmail"));
    } else {
        setIconByName(QStringLiteral("mail-mark-unread-new"));
    }
}

void KMSystemTray::setSystrayIconNotificationsEnabled(bool enabled)
{
    if (enabled != mIconNotificationsEnabled) {
        mIconNotificationsEnabled = enabled;
        updateSystemTray();
    }
}

/**
 * On left mouse click, switch focus to the first KMMainWidget.  On right
 * click, bring up a list of all folders with a count of unread messages.
 */
void KMSystemTray::slotActivated()
{
    KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
    if (!mainWidget) {
        return;
    }

    QWidget *mainWin = mainWidget->window();
    if (!mainWin) {
        return;
    }

    KWindowInfo cur = KWindowInfo(mainWin->winId(), NET::WMDesktop);

    const int currentDesktop = KWindowSystem::currentDesktop();
    const bool wasMinimized = cur.isMinimized();

    if (cur.valid()) {
        mDesktopOfMainWin = cur.desktop();
    }

    if (wasMinimized && (currentDesktop != mDesktopOfMainWin) && (mDesktopOfMainWin == NET::OnAllDesktops)) {
        KWindowSystem::setOnDesktop(mainWin->winId(), currentDesktop);
    }

    if (mDesktopOfMainWin == NET::OnAllDesktops) {
        KWindowSystem::setOnAllDesktops(mainWin->winId(), true);
    }

    KWindowSystem::activateWindow(mainWin->winId());

    if (wasMinimized) {
        kmkernel->raise();
    }
}

void KMSystemTray::slotContextMenuAboutToShow()
{
    // Rebuild popup menu before show to minimize race condition if
    // the base KMainWidget is closed.
    if (!buildPopupMenu()) {
        return;
    }

    if (mNewMessagesPopup != nullptr) {
        contextMenu()->removeAction(mNewMessagesPopup->menuAction());
        delete mNewMessagesPopup;
        mNewMessagesPopup = nullptr;
    }
    mNewMessagesPopup = new QMenu();
    fillFoldersMenu(mNewMessagesPopup, kmkernel->treeviewModelSelection());

    connect(mNewMessagesPopup, &QMenu::triggered, this, &KMSystemTray::slotSelectCollection);

    if (mCount > 0) {
        mNewMessagesPopup->setTitle(i18n("New Messages In"));
        contextMenu()->insertAction(mSendQueued, mNewMessagesPopup->menuAction());
    }
}

void KMSystemTray::fillFoldersMenu(QMenu *menu, const QAbstractItemModel *model, const QString &parentName, const QModelIndex &parentIndex)
{
    const int rowCount = model->rowCount(parentIndex);
    for (int row = 0; row < rowCount; ++row) {
        const QModelIndex index = model->index(row, 0, parentIndex);
        const Akonadi::Collection collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
        qint64 count = 0;
        if (!excludeFolder(collection)) {
            Akonadi::CollectionStatistics statistics = collection.statistics();
            count = qMax(0LL, statistics.unreadCount());
            if (count > 0) {
                if (ignoreNewMailInFolder(collection)) {
                    count = 0;
                } else {
                    mCount += count;
                }
            }
        }
        QString label = parentName.isEmpty() ? QString() : QString(parentName + QLatin1String("->"));
        label += model->data(index).toString();
        label.replace(QLatin1Char('&'), QStringLiteral("&&"));
        if (count > 0) {
            // insert an item
            QAction *action = menu->addAction(label);
            action->setData(collection.id());
        }
        if (model->rowCount(index) > 0) {
            fillFoldersMenu(menu, model, label, index);
        }
    }
}

void KMSystemTray::hideKMail()
{
    KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
    if (!mainWidget) {
        return;
    }
    QWidget *mainWin = mainWidget->window();
    Q_ASSERT(mainWin);
    if (mainWin) {
        mDesktopOfMainWin = KWindowInfo(mainWin->winId(), NET::WMDesktop).desktop();
        // iconifying is unnecessary, but it looks cooler
        KWindowSystem::minimizeWindow(mainWin->winId());
        mainWin->hide();
    }
}

void KMSystemTray::initListOfCollection()
{
    mCount = 0;
    const QAbstractItemModel *model = kmkernel->collectionModel();
    if (model->rowCount() == 0) {
        QTimer::singleShot(1000, this, &KMSystemTray::initListOfCollection);
        return;
    }
    unreadMail(model);

    if (status() == KStatusNotifierItem::Passive && (mCount > 0)) {
        setStatus(KStatusNotifierItem::Active);
    } else if (status() == KStatusNotifierItem::Active && (mCount == 0)) {
        setStatus(KStatusNotifierItem::Passive);
    }

    //qCDebug(KMAIL_LOG)<<" mCount :"<<mCount;
    updateCount();
}

void KMSystemTray::unreadMail(const QAbstractItemModel *model, const QModelIndex &parentIndex)
{
    const int rowCount = model->rowCount(parentIndex);
    for (int row = 0; row < rowCount; ++row) {
        const QModelIndex index = model->index(row, 0, parentIndex);
        const Akonadi::Collection collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();

        if (!excludeFolder(collection)) {
            const Akonadi::CollectionStatistics statistics = collection.statistics();
            const qint64 count = qMax(0LL, statistics.unreadCount());

            if (count > 0) {
                if (!ignoreNewMailInFolder(collection)) {
                    mCount += count;
                }
            }
        }
        if (model->rowCount(index) > 0) {
            unreadMail(model, index);
        }
    }
    // Update tooltip to reflect count of unread messages
    setToolTipSubTitle(mCount == 0 ? i18n("There are no unread messages")
                       : i18np("1 unread message",
                               "%1 unread messages",
                               mCount));
}

bool KMSystemTray::hasUnreadMail() const
{
    return mCount != 0;
}

void KMSystemTray::slotSelectCollection(QAction *act)
{
    const Akonadi::Collection::Id id = act->data().value<Akonadi::Collection::Id>();
    kmkernel->selectCollectionFromId(id);
    KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
    if (!mainWidget) {
        return;
    }
    QWidget *mainWin = mainWidget->window();
    if (mainWin && !mainWin->isVisible()) {
        activate();
    }
}

void KMSystemTray::updateSystemTray()
{
    initListOfCollection();
}

void KMSystemTray::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &)
{
    //Exclude sent mail folder

    if (CommonKernel->outboxCollectionFolder().id() == id
        || CommonKernel->sentCollectionFolder().id() == id
        || CommonKernel->templatesCollectionFolder().id() == id
        || CommonKernel->trashCollectionFolder().id() == id
        || CommonKernel->draftsCollectionFolder().id() == id) {
        return;
    }
    initListOfCollection();
}

bool KMSystemTray::excludeFolder(const Akonadi::Collection &collection) const
{
    if (!collection.isValid() || !collection.contentMimeTypes().contains(KMime::Message::mimeType())) {
        return true;
    }
    if (CommonKernel->outboxCollectionFolder() == collection
        || CommonKernel->sentCollectionFolder() == collection
        || CommonKernel->templatesCollectionFolder() == collection
        || CommonKernel->trashCollectionFolder() == collection
        || CommonKernel->draftsCollectionFolder() == collection) {
        return true;
    }

    if (MailCommon::Util::isVirtualCollection(collection)) {
        return true;
    }
    return false;
}

bool KMSystemTray::ignoreNewMailInFolder(const Akonadi::Collection &collection)
{
    if (collection.hasAttribute<Akonadi::NewMailNotifierAttribute>()) {
        if (collection.attribute<Akonadi::NewMailNotifierAttribute>()->ignoreNewMail()) {
            return true;
        }
    }
    return false;
}
