/*
    This file is part of Akonadi.

    Copyright (c) 2006 Tobias Koenig <tokoe@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.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
    USA.
*/

#include "debugwidget.h"

#include "tracernotificationinterface.h"
#include "connectionpage.h"

#include <AkonadiWidgets/controlgui.h>

#include <KLocalizedString>
#include <QTabWidget>
#include <KTextEdit>
#include <AkonadiCore/ServerManager>

#include <QPushButton>
#include <QSplitter>
#include <QVBoxLayout>
#include <QCheckBox>
#include <QFileDialog>

using org::freedesktop::Akonadi::DebugInterface;

DebugWidget::DebugWidget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout(this);

    QString service = QStringLiteral("org.freedesktop.Akonadi");
    if (Akonadi::ServerManager::hasInstanceIdentifier()) {
        service += QLatin1String(".") + Akonadi::ServerManager::instanceIdentifier();
    }
    mDebugInterface = new DebugInterface(service, QStringLiteral("/debug"), QDBusConnection::sessionBus(), this);
    QCheckBox *cb = new QCheckBox(QStringLiteral("Enable debugger"), this);
    cb->setChecked(mDebugInterface->isValid() && mDebugInterface->tracer().value() == QLatin1String("dbus"));
    connect(cb, &QCheckBox::toggled, this, &DebugWidget::enableDebugger);
    layout->addWidget(cb);

    QSplitter *splitter = new QSplitter(Qt::Vertical, this);
    splitter->setObjectName(QStringLiteral("debugSplitter"));
    layout->addWidget(splitter);

    mConnectionPages = new QTabWidget(splitter);
    mConnectionPages->setTabsClosable(true);

    mGeneralView = new KTextEdit(splitter);
    mGeneralView->setReadOnly(true);

    ConnectionPage *page = new ConnectionPage(QStringLiteral("All"));
    page->showAllConnections(true);
    mConnectionPages->addTab(page, QStringLiteral("All"));

    connect(mConnectionPages, &QTabWidget::tabCloseRequested, this, &DebugWidget::tabCloseRequested);

    org::freedesktop::Akonadi::TracerNotification *iface = new org::freedesktop::Akonadi::TracerNotification(QString(), QStringLiteral("/tracing/notifications"), QDBusConnection::sessionBus(), this);

    connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionStarted, this, &DebugWidget::connectionStarted);
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionEnded, this, &DebugWidget::connectionEnded);
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::signalEmitted, this, &DebugWidget::signalEmitted);
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::warningEmitted, this, &DebugWidget::warningEmitted);
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::errorEmitted, this, &DebugWidget::errorEmitted);

    // in case we started listening when the connection is already ongoing
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionDataInput, this, &DebugWidget::connectionStarted);
    connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionDataOutput, this, &DebugWidget::connectionStarted);

    QHBoxLayout *buttonLayout = new QHBoxLayout;
    layout->addLayout(buttonLayout);

    QPushButton *clearAllButton = new QPushButton(QStringLiteral("Clear All Tabs"), this);
    QPushButton *clearCurrentButton = new QPushButton(QStringLiteral("Clear Current Tab"), this);
    QPushButton *clearGeneralButton = new QPushButton(QStringLiteral("Clear Information View"), this);
    QPushButton *closeAllTabsButton = new QPushButton(QStringLiteral("Close All Tabs"), this);
    QPushButton *saveRichtextButton = new QPushButton(QStringLiteral("Save as RichText..."), this);

    buttonLayout->addWidget(clearAllButton);
    buttonLayout->addWidget(clearCurrentButton);
    buttonLayout->addWidget(clearGeneralButton);
    buttonLayout->addWidget(closeAllTabsButton);
    buttonLayout->addWidget(saveRichtextButton);

    connect(clearAllButton, &QPushButton::clicked, this, &DebugWidget::clearAllTabs);
    connect(clearCurrentButton, &QPushButton::clicked, this, &DebugWidget::clearCurrentTab);
    connect(clearGeneralButton, &QPushButton::clicked, mGeneralView, &KTextEdit::clear);
    connect(closeAllTabsButton, &QPushButton::clicked, this, &DebugWidget::closeAllTabs);
    connect(saveRichtextButton, &QPushButton::clicked, this, &DebugWidget::saveRichText);

    Akonadi::ControlGui::widgetNeedsAkonadi(this);
}

void DebugWidget::connectionStarted(const QString &identifier, const QString &msg)
{
    Q_UNUSED(msg);
    if (mPageHash.contains(identifier)) {
        return;
    }

    ConnectionPage *page = new ConnectionPage(identifier);
    mConnectionPages->addTab(page, identifier);

    mPageHash.insert(identifier, page);
}

void DebugWidget::connectionEnded(const QString &identifier, const QString &)
{
    if (!mPageHash.contains(identifier)) {
        return;
    }

    QWidget *widget = mPageHash[ identifier ];

    mConnectionPages->removeTab(mConnectionPages->indexOf(widget));

    mPageHash.remove(identifier);
    delete widget;
}

void DebugWidget::tabCloseRequested(int index)
{
    if (index != 0) {
        QWidget *page = mConnectionPages->widget(index);
        QMutableHashIterator<QString, ConnectionPage *> it(mPageHash);
        while (it.hasNext()) {
            it.next();
            if (it.value() == page) {
                it.remove();
                break;
            }
        }

        mConnectionPages->removeTab(index);
        delete page;
    }
}

void DebugWidget::clearAllTabs()
{
    ConnectionPage *page = qobject_cast<ConnectionPage *>(mConnectionPages->widget(0));
    if (page) {
        page->clear();
    }

    QMutableHashIterator<QString, ConnectionPage *> it(mPageHash);
    while (it.hasNext()) {
        it.next().value()->clear();
    }
}

void DebugWidget::clearCurrentTab()
{
    ConnectionPage *page = qobject_cast<ConnectionPage *>(mConnectionPages->currentWidget());
    if (!page) {
        return;
    }

    page->clear();
}

void DebugWidget::closeAllTabs()
{
    ConnectionPage *page = qobject_cast<ConnectionPage *>(mConnectionPages->widget(0));
    if (page) {
        page->clear();
    }

    while (mConnectionPages->count() > 1) {
        mConnectionPages->removeTab(1);
    }
    qDeleteAll(mPageHash);
    mPageHash.clear();
}

void DebugWidget::signalEmitted(const QString &signalName, const QString &msg)
{
    mGeneralView->append(QStringLiteral("<font color=\"green\">%1 ( %2 )</font>").arg(signalName, msg));
}

void DebugWidget::warningEmitted(const QString &componentName, const QString &msg)
{
    mGeneralView->append(QStringLiteral("<font color=\"blue\">%1: %2</font>").arg(componentName, msg));
}

void DebugWidget::errorEmitted(const QString &componentName, const QString &msg)
{
    mGeneralView->append(QStringLiteral("<font color=\"red\">%1: %2</font>").arg(componentName, msg));
}

void DebugWidget::enableDebugger(bool enable)
{
    mDebugInterface->setTracer(enable ? QStringLiteral("dbus") : QStringLiteral("null"));
}

void DebugWidget::saveRichText()
{
    ConnectionPage *page = qobject_cast<ConnectionPage *>(mConnectionPages->currentWidget());
    if (!page) {
        return;
    }

    const QString fileName = QFileDialog::getSaveFileName(this);
    if (fileName.isEmpty()) {
        return;
    }

    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)) {
        return;
    }

    file.write(page->toHtml().toUtf8());
    file.close();
}

