/*
    Copyright (c) 2008 Volker Krause <vkrause@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 "erroroverlay_p.h"
#include "ui_erroroverlay.h"
#include "selftestdialog_p.h"

#include <KDebug>
#include <KIcon>
#include <KLocalizedString>

#include <QtCore/QEvent>
#include <QPalette>

using namespace Akonadi;

//@cond PRIVATE

class ErrorOverlayStatic
{
public:
    QVector<QPair<QPointer<QWidget>, QPointer<QWidget> > > baseWidgets;
};

K_GLOBAL_STATIC(ErrorOverlayStatic, sInstanceOverlay)

// return true if o1 is a parent of o2
static bool isParentOf(QWidget *o1, QWidget *o2)
{
    if (!o1 || !o2) {
        return false;
    }
    if (o1 == o2) {
        return true;
    }
    if (o2->isWindow()) {
        return false;
    }
    return isParentOf(o1, o2->parentWidget());
}

ErrorOverlay::ErrorOverlay(QWidget *baseWidget, QWidget *parent)
    : QWidget(parent ? parent : baseWidget->window())
    , mBaseWidget(baseWidget)
    , mOverlayActive(false)
    , mBaseWidgetIsParent(false)
    , ui(new Ui::ErrorOverlay)
{
    Q_ASSERT(baseWidget);

    mBaseWidgetIsParent = isParentOf(mBaseWidget, this);

    // check existing overlays to detect cascading
    for (QVector<QPair< QPointer<QWidget>, QPointer<QWidget> > >::Iterator it = sInstanceOverlay->baseWidgets.begin();
         it != sInstanceOverlay->baseWidgets.end();) {
        if ((*it).first == 0 || (*it).second == 0) {
            // garbage collection
            it = sInstanceOverlay->baseWidgets.erase(it);
            continue;
        }
        if (isParentOf((*it).first, baseWidget)) {
            // parent already has an overlay, kill ourselves
            mBaseWidget = 0;
            hide();
            deleteLater();
            return;
        }
        if (isParentOf(baseWidget, (*it).first)) {
            // child already has overlay, kill that one
            delete(*it).second;
            it = sInstanceOverlay->baseWidgets.erase(it);
            continue;
        }
        ++it;
    }
    sInstanceOverlay->baseWidgets.append(qMakePair(mBaseWidget, QPointer<QWidget>(this)));

    connect(baseWidget, SIGNAL(destroyed()), SLOT(deleteLater()));
    mPreviousState = mBaseWidget->isEnabled();

    ui->setupUi(this);
    ui->notRunningIcon->setPixmap(KIcon(QLatin1String("akonadi")).pixmap(64));
    ui->brokenIcon->setPixmap(KIcon(QString::fromLatin1("dialog-error")).pixmap(64));
    ui->progressIcon->setPixmap(KIcon(QLatin1String("akonadi")).pixmap(32));
    ui->quitButton->setText(KStandardGuiItem::quit().text());
    ui->detailsQuitButton->setText(KStandardGuiItem::quit().text());

#ifndef KDEPIM_MOBILE_UI
    ui->quitButton->hide();
    ui->detailsQuitButton->hide();
#endif

    connect(ui->startButton, SIGNAL(clicked()), SLOT(startClicked()));
    connect(ui->quitButton, SIGNAL(clicked()), SLOT(quitClicked()));
    connect(ui->detailsQuitButton, SIGNAL(clicked()), SLOT(quitClicked()));
    connect(ui->selfTestButton, SIGNAL(clicked()), SLOT(selfTestClicked()));

    const ServerManager::State state = ServerManager::state();
    mOverlayActive = (state == ServerManager::Running);
    serverStateChanged(state);

    connect(ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)),
            SLOT(serverStateChanged(Akonadi::ServerManager::State)));

    QPalette p = palette();
    p.setColor(backgroundRole(), QColor(0, 0, 0, 128));
    p.setColor(foregroundRole(), Qt::white);
    setPalette(p);
    setAutoFillBackground(true);

    mBaseWidget->installEventFilter(this);

    reposition();
}

ErrorOverlay::~ ErrorOverlay()
{
    if (mBaseWidget && !mBaseWidgetIsParent) {
        mBaseWidget->setEnabled(mPreviousState);
    }
}

void ErrorOverlay::reposition()
{
    if (!mBaseWidget) {
        return;
    }

    // reparent to the current top level widget of the base widget if needed
    // needed eg. in dock widgets
    if (parentWidget() != mBaseWidget->window()) {
        setParent(mBaseWidget->window());
    }

    // follow base widget visibility
    // needed eg. in tab widgets
    if (!mBaseWidget->isVisible()) {
        hide();
        return;
    }
    if (mOverlayActive) {
        show();
    }

    // follow position changes
    const QPoint topLevelPos = mBaseWidget->mapTo(window(), QPoint(0, 0));
    const QPoint parentPos = parentWidget()->mapFrom(window(), topLevelPos);
    move(parentPos);

    // follow size changes
    // TODO: hide/scale icon if we don't have enough space
    resize(mBaseWidget->size());
}

bool ErrorOverlay::eventFilter(QObject *object, QEvent *event)
{
    if (object == mBaseWidget && mOverlayActive &&
        (event->type() == QEvent::Move || event->type() == QEvent::Resize ||
         event->type() == QEvent::Show || event->type() == QEvent::Hide ||
         event->type() == QEvent::ParentChange)) {
        reposition();
    }
    return QWidget::eventFilter(object, event);
}

void ErrorOverlay::startClicked()
{
    const ServerManager::State state = ServerManager::state();
    if (state == ServerManager::Running) {
        serverStateChanged(state);
    } else {
        ServerManager::start();
    }
}

void ErrorOverlay::quitClicked()
{
    qApp->quit();
}

void ErrorOverlay::selfTestClicked()
{
    SelfTestDialog dlg;
    dlg.exec();
}

void ErrorOverlay::serverStateChanged(ServerManager::State state)
{
    if (!mBaseWidget) {
        return;
    }

    if (state == ServerManager::Running && mOverlayActive) {
        mOverlayActive = false;
        hide();
        if (!mBaseWidgetIsParent) {
            mBaseWidget->setEnabled(mPreviousState);
        }
    } else if (!mOverlayActive) {
        mOverlayActive = true;
        if (mBaseWidget->isVisible()) {
            show();
        }

        if (!mBaseWidgetIsParent) {
            mPreviousState = mBaseWidget->isEnabled();
            mBaseWidget->setEnabled(false);
        }

        reposition();
    }

    if (mOverlayActive) {
        switch (state) {
        case ServerManager::NotRunning:
            ui->stackWidget->setCurrentWidget(ui->notRunningPage);
            break;
        case ServerManager::Broken:
            ui->stackWidget->setCurrentWidget(ui->brokenPage);
            break;
        case ServerManager::Starting:
            ui->progressPage->setToolTip(i18n("Personal information management service is starting..."));
            ui->progressDescription->setText(i18n("Personal information management service is starting..."));
            ui->stackWidget->setCurrentWidget(ui->progressPage);
            break;
        case ServerManager::Stopping:
            ui->progressPage->setToolTip(i18n("Personal information management service is shutting down..."));
            ui->progressDescription->setText(i18n("Personal information management service is shutting down..."));
            ui->stackWidget->setCurrentWidget(ui->progressPage);
            break;
        case ServerManager::Upgrading:
            ui->progressPage->setToolTip(i18n("Personal information management service is performing a database upgrade."));
            ui->progressDescription->setText(i18n("Personal information management service is performing a database upgrade.\n"
                                                  "This happens after a software update and is necessary to optimize performance.\n"
                                                  "Depending on the amount of personal information, this might take a few minutes."));
            ui->stackWidget->setCurrentWidget(ui->progressPage);
            break;
        case ServerManager::Running:
            break;
        }
    }
}

//@endcond

#include "moc_erroroverlay_p.cpp"
