/*
    SPDX-FileCopyrightText: 2000-2004 Michael Edwardes <mte@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 Javier Campos Morales <javi_c@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 Felix Rodriguez <frodriguez@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 John C <thetacoturtle@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 Thomas Baumgart <ipwizard@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 Kevin Tambascio <ktambascio@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2004 Ace Jones <ace.j@hotpop.com>
    SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
    SPDX-FileCopyrightText: 2021 Dawid Wróbel <me@dawidwrobel.com>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "kreportsview.h"

// ----------------------------------------------------------------------------
// QT Includes

#include <QFileDialog>
#include <QLocale>
#include <QMenu>
#include <QTreeWidget>
#include <QUrlQuery>

// ----------------------------------------------------------------------------
// KDE Includes

#include <KMessageBox>
#include <KRecentDirs>
#include <KLocalizedString>

// ----------------------------------------------------------------------------
// Project Includes

#include "ui_reportcontrol.h"

#include "icons.h"
#include "journalmodel.h"
#include "kbalancechartdlg.h"
#include "kreportconfigurationfilterdlg.h"
#include "kreportsview_p.h"
#include "menuenums.h"
#include "mymoneyenums.h"
#include "mymoneyexception.h"
#include "mymoneyfile.h"
#include "mymoneyreport.h"
#include "objectinfotable.h"
#include "pivottable.h"
#include "querytable.h"
#include "reportsmodel.h"

using namespace reports;
using namespace eMyMoney;
using namespace Icons;

#define VIEW_LEDGER         "ledger"
#define VIEW_SCHEDULE       "schedule"
#define VIEW_WELCOME        "welcome"
#define VIEW_HOME           "home"
#define VIEW_REPORTS        "reports"

/**
 * Creates a unique file name in a specific directory
 * @param rootDir Directory used to search for a unique file name
 * @param reportName Name of the report to create a file name
 * @param extension Extension to add to the file name
 * @returns the absolute file path or an empty string if no free name was found after more than 100 attempts
 */
static QString createSaveFileName(const QString& rootDir, const QString& reportName, const QString& extension)
{
    static QRegularExpression rx(QLatin1String("\\s+"));
    QString name(reportName);
    name.replace(rx, "-");
    QString suffix;
    QFileInfo fi;
    int index = 0;
    do {
        fi.setFile(rootDir + QDir::separator() + name + suffix + extension);
        suffix = QString(QLatin1String("-%1")).arg(++index);
    } while (fi.exists() && index < 100);

    return index < 100 ? fi.absoluteFilePath() : QString();
}

/**
  * KReportsView Implementation
  */

KReportsView::KReportsView(QWidget *parent) :
    KMyMoneyViewBase(*new KReportsViewPrivate(this), parent)
{
    connect(pActions[eMenu::Action::ReportNew], &QAction::triggered, this, &KReportsView::slotDuplicate);
    connect(pActions[eMenu::Action::ReportCopy], &QAction::triggered, this, &KReportsView::slotCopyView);
    connect(pActions[eMenu::Action::ReportConfigure], &QAction::triggered, this, &KReportsView::slotConfigure);
    connect(pActions[eMenu::Action::ReportExport], &QAction::triggered, this, &KReportsView::slotExportView);
    connect(pActions[eMenu::Action::ReportDelete], &QAction::triggered, this, &KReportsView::slotDelete);
    connect(pActions[eMenu::Action::ReportClose], &QAction::triggered, this, &KReportsView::slotCloseCurrent);

    pActions[eMenu::Action::ReportCopy]->setShortcut(QKeySequence::Copy);
    pActions[eMenu::Action::ReportNew]->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Plus));
    pActions[eMenu::Action::ReportConfigure]->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_P));
    pActions[eMenu::Action::ReportDelete]->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Delete));
    pActions[eMenu::Action::ReportClose]->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_W));
}

KReportsView::~KReportsView()
{
}

void KReportsView::executeAction(eMenu::Action action, const SelectedObjects& selections)
{
    Q_D(KReportsView);
    switch (action) {
    case eMenu::Action::ReportOpen:
        slotOpenReport(selections.firstSelection(SelectedObjects::Report));
        break;

    case eMenu::Action::Print:
        if (d->isActiveView()) {
            slotPrintView();
        }
        break;

    case eMenu::Action::PrintPreview:
        if (d->isActiveView()) {
            slotPrintPreviewView();
        }
        break;

    case eMenu::Action::ReportCopy:
        if (d->isActiveView()) {
            slotCopyView();
        }
        break;

    case eMenu::Action::ChartAccountBalance: {
        const auto account = MyMoneyFile::instance()->accountsModel()->itemById(selections.firstSelection(SelectedObjects::Account));
        if (!account.id().isEmpty()) {
            QPointer<KBalanceChartDlg> dlg = new KBalanceChartDlg(account, this);
            connect(dlg.data(), &KBalanceChartDlg::openReport, this, QOverload<const QString&>::of(&KReportsView::slotOpenReport));
            dlg->exec();
            delete dlg;
        }
    } break;

    case eMenu::Action::FileClose:
        slotCloseAll();
        break;

    case eMenu::Action::ReportAccountTransactions:
        d->showTransactionReport();
        break;

    default:
        break;
    }
}

void KReportsView::refresh()
{
    Q_D(KReportsView);
    if (!isVisible()) {
        d->m_needsRefresh = true;
        return;
    }

    d->m_needsRefresh = false;
    qobject_cast<QSortFilterProxyModel*>(d->ui.m_tocTreeViewDefault->model())->invalidate();
    qobject_cast<QSortFilterProxyModel*>(d->ui.m_tocTreeViewCustom->model())->invalidate();
}

void KReportsView::showEvent(QShowEvent * event)
{
    Q_D(KReportsView);
    if (d->m_needLoad) {
        d->init();
        connect(d->ui.m_closeButton, &QToolButton::clicked, this, [&]() {
            Q_D(KReportsView);
            d->ui.m_filterContainer->hide();
            d->ui.m_searchWidget->clear();
            d->ui.m_reportTabWidget->setFocus();
        });
        connect(pActions[eMenu::Action::ShowFilterWidget], &QAction::triggered, this, [&]() {
            if (isVisible()) {
                Q_D(KReportsView);
                d->ui.m_filterContainer->show();
                d->ui.m_searchWidget->setFocus();
            }
        });
        connect(d->ui.m_searchWidget, &QLineEdit::textChanged, this, [&](const QString& text) {
            Q_D(KReportsView);
            d->setFilter(text);
        });
        connect(d->ui.m_reportTabWidget, &QTabWidget::currentChanged, this, [this](int) {
            Q_D(KReportsView);
            if (d->m_configurationSidebarOpen) {
                doConfigure(NoConfigureOption);
            }
        });

        // remove close button from list tab
        QTabBar* bar = d->ui.m_reportTabWidget->findChild<QTabBar*>();
        if (bar) {
            QTabBar::ButtonPosition closeSide =
                (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, d->ui.m_reportTabWidget->widget(0));
            QWidget* w = bar->tabButton(0, closeSide);
            bar->setTabButton(0, closeSide, nullptr);
            w->deleteLater();
        }
    }
    if (d->m_needsRefresh) {
        refresh();
        d->setFilter(d->ui.m_searchWidget->text());
    }

    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget()))
        Q_EMIT reportSelected(tab->report());
    else
        Q_EMIT reportSelected(MyMoneyReport());

    // don't forget base class implementation
    QWidget::showEvent(event);
}

bool KReportsView::eventFilter(QObject* watched, QEvent* event)
{
    Q_D(KReportsView);

    if (event->type() == QEvent::KeyPress) {
        if (watched == d->ui.m_searchWidget || qobject_cast<QTreeView*>(watched)) {
            const auto kev = static_cast<QKeyEvent*>(event);
            if (kev->modifiers() == Qt::NoModifier && kev->key() == Qt::Key_Escape) {
                d->ui.m_closeButton->animateClick();
                return true;
            } else if (kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Enter) {
                auto view = qobject_cast<QTreeView*>(watched);
                if (view) {
                    const QModelIndex idx = view->currentIndex();
                    if (idx.isValid()) {
                        Q_EMIT slotDoubleClicked(idx);
                        return true;
                    }
                }
            }
        }

    } else if (event->type() == QEvent::ShortcutOverride) {
        const auto kev = static_cast<QKeyEvent*>(event);
        const auto keySeq = QKeySequence(kev->modifiers() | kev->key());
        const auto buttons = findChildren<QToolButton*>();
        for (const auto& button : std::as_const(buttons)) {
            const auto action = button->defaultAction();
            if (action) {
                if (action->shortcuts().contains(keySeq)) {
                    action->trigger();
                    event->accept();
                    return true;
                }
            }
        }
    }

    return QWidget::eventFilter(watched, event);
}

void KReportsView::updateActions(const SelectedObjects& selections)
{
    Q_D(KReportsView);
    bool enable = false;

    if (!selections.selection(SelectedObjects::Account).isEmpty()) {
        const auto file = MyMoneyFile::instance();
        const auto accId = selections.selection(SelectedObjects::Account).at(0);
        if (!file->isStandardAccount(accId)) {
            d->m_currentAccount = file->accountsModel()->itemById(accId);
            switch (d->m_currentAccount.accountType()) {
            case eMyMoney::Account::Type::Asset:
            case eMyMoney::Account::Type::Liability:
            case eMyMoney::Account::Type::Equity:
            case eMyMoney::Account::Type::Checkings:
                enable = true;
                break;
            default:
                break;
            }
        }
    }
    pActions[eMenu::Action::ReportAccountTransactions]->setEnabled(enable);

    // only access the widgets if they are initialized
    if (!d->m_needLoad) {
        if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget())) {
            tab->enableAllReportActions();
        }
    }
}

void KReportsView::slotOpenUrl(const QUrl &url)
{
    QString view = url.fileName();
    if (view.isEmpty())
        return;
    QString command = QUrlQuery(url).queryItemValue("command");
    QString id = QUrlQuery(url).queryItemValue("id");
    QString tid = QUrlQuery(url).queryItemValue("tid");

    if (view == VIEW_REPORTS) {

        if (command.isEmpty()) {
            // slotRefreshView();
        } else if (command == QLatin1String("print"))
            slotPrintView();
        else if (command == QLatin1String("copy"))
            slotCopyView();
        else if (command == QLatin1String("save"))
            slotExportView();
        else if (command == QLatin1String("configure"))
            slotConfigure();
        else if (command == QLatin1String("duplicate"))
            slotDuplicate();
        else if (command == QLatin1String("close"))
            slotCloseCurrent();
        else if (command == QLatin1String("delete"))
            slotDelete();
        else
            qWarning() << i18n("Unknown command '%1' in KReportsView::slotOpenUrl()", qPrintable(command));

    } else if (view == VIEW_LEDGER) {
        const auto gotoAccount = pActions[eMenu::Action::GoToAccount];
        gotoAccount->setData(id);

        // convert transaction id to journalEntryId and make it the current selection
        SelectedObjects selection;
        const auto indexes = MyMoneyFile::instance()->journalModel()->indexesByTransactionId(tid);
        for (const auto& idx : indexes) {
            if (idx.data(eMyMoney::Model::JournalSplitAccountIdRole).toString() == id) {
                tid = idx.data(eMyMoney::Model::IdRole).toString();
                break;
            }
        }
        selection.setSelection(SelectedObjects::JournalEntry, tid);
        Q_EMIT requestSelectionChange(selection);

        MyMoneyUtils::triggerAction(gotoAccount);
    } else {
        qWarning() << i18n("Unknown view '%1' in KReportsView::slotOpenUrl()", qPrintable(view));
    }
}

void KReportsView::slotPrintView()
{
    Q_D(const KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget()))
        tab->print();
}

void KReportsView::slotPrintPreviewView()
{
    Q_D(const KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget()))
        tab->printPreview();
}

void KReportsView::slotCopyView()
{
    Q_D(KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget()))
        tab->copyToClipboard();
}

void KReportsView::slotExportView()
{
    Q_D(const KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget())) {
        QPointer<QFileDialog> dialog = new QFileDialog(this, i18n("Export as"), KRecentDirs::dir(":kmymoney-export"));
        dialog->setMimeTypeFilters({
            QStringLiteral("text/csv"),
            QStringLiteral("text/html"),
            QStringLiteral("application/xml"),
#ifndef Q_OS_WIN32
            QStringLiteral("application/pdf"),
#endif
        });
        dialog->setFileMode(QFileDialog::AnyFile);
        dialog->setAcceptMode(QFileDialog::AcceptSave);
        dialog->selectFile(tab->report().name());

        QUrl newURL;
        QString selectedMimeType;
        if (dialog->exec() == QDialog::Accepted) {
            newURL = dialog->selectedUrls().first();
            selectedMimeType = dialog->selectedMimeTypeFilter();
        }
        delete dialog;

        if (!newURL.isEmpty()) {
            KRecentDirs::add(":kmymoney-export", newURL.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
            QString newName = newURL.toDisplayString(QUrl::PreferLocalFile);

            try {
                tab->saveAs(newName, selectedMimeType);
            } catch (const MyMoneyException &e) {
                KMessageBox::error(this, i18n("Failed to save: %1", QString::fromLatin1(e.what())));
            }
        }
    }
}

void KReportsView::slotConfigure()
{
    doConfigure(NoConfigureOption);
}

void KReportsView::doConfigure(ConfigureOption configureOption)
{
    Q_D(KReportsView);
    QString cm = "KReportsView::slotConfigure";

    auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget());

    if (!tab) // nothing to do
        return;
    d->m_configurationSidebarOpen = true;
    int tabNr = d->ui.m_reportTabWidget->currentIndex();

    tab->updateDataRange(); // range will be needed during configuration, but cannot be obtained earlier

    MyMoneyReport report = tab->report();
    // modified default or generated reports are custom reports
    if (report.isBuiltIn() || report.isGenerated()) {
        report.setIsCustom();
        report.setName(i18n("%1 (Customized)", report.name()));
        report.setComment(i18n("Custom Report"));
    }

    KReportConfigurationFilterDlg::Type dlgType =
        report.isStaticEvaluation() ? KReportConfigurationFilterDlg::Type::StaticEvaluationSafe : KReportConfigurationFilterDlg::Type::Default;
    auto dlg = qobject_cast<KReportConfigurationFilterDlg*>(tab->configurationSidebar());
    if (!dlg) {
        dlg = new KReportConfigurationFilterDlg(report, dlgType, tab);
        dlg->setWindowFlags(Qt::Widget);

        QPointer<KReportTab> configuredTab(tab);
        connect(dlg, &KReportConfigurationFilterDlg::configurationApplied, this, [this, configuredTab, tabNr](const MyMoneyReport& configuredReport) {
            Q_D(KReportsView);
            if (!configuredTab) {
                return;
            }

            MyMoneyReport newReport = configuredReport;
            newReport.setModified(true);

            // If this report has an ID, then MODIFY it, otherwise ADD it
            MyMoneyFileTransaction ft;
            try {
                if (!newReport.id().isEmpty()) {
                    MyMoneyFile::instance()->modifyReport(newReport);
                    ft.commit();
                    configuredTab->modifyReport(newReport);

                    if (tabNr >= 0 && tabNr < d->ui.m_reportTabWidget->count()) {
                        d->ui.m_reportTabWidget->setTabText(tabNr, newReport.name());
                        d->ui.m_reportTabWidget->setCurrentIndex(tabNr);
                    }
                } else {
                    MyMoneyFile::instance()->addReport(newReport);
                    ft.commit();
                    d->addReportTab(newReport, OpenImmediately);
                    d->ui.m_tocTreeViewDefault->clearSelection();
                    d->selectReportInViewById(d->ui.m_tocTreeViewCustom, newReport.id());
                }
            } catch (const MyMoneyException& e) {
                KMessageBox::error(this, i18n("Failed to configure report: %1", QString::fromLatin1(e.what())));
            }
        });

        connect(dlg, &KReportConfigurationFilterDlg::closeRequested, this, [this, configuredTab, configureOption]() {
            Q_D(KReportsView);
            if (!configuredTab) {
                return;
            }
            if (configureOption == LoadReportOnCancel) {
                configuredTab->loadTab();
            }
            d->m_configurationSidebarOpen = false;
            configuredTab->closeConfigurationSidebar();
        });
    }

    dlg->loadReport(report);
    tab->showConfigurationSidebar(dlg);
}

void KReportsView::slotDuplicate()
{
    Q_D(KReportsView);
    QString cm = "KReportsView::slotDuplicate";

    auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget());

    if (!tab) {
        // nothing to do
        return;
    }

    MyMoneyReport dupe = tab->report();
    dupe.setName(i18n("Copy of %1", dupe.name()));
    if (dupe.isBuiltIn()) {
        dupe.setIsCustom();
        dupe.setComment(i18n("Custom Report"));
    }
    dupe.clearId();

    QPointer<KReportConfigurationFilterDlg> dlg = new KReportConfigurationFilterDlg(dupe);
    if (dlg->exec()) {
        MyMoneyReport newReport = dlg->getConfig();
        newReport.setModified(true);
        MyMoneyFileTransaction ft;
        try {
            MyMoneyFile::instance()->addReport(newReport);
            ft.commit();
            d->addReportTab(newReport, OpenImmediately);
            d->ui.m_tocTreeViewDefault->clearSelection();
            d->selectReportInViewById(d->ui.m_tocTreeViewCustom, newReport.id());
        } catch (const MyMoneyException &e) {
            QString error = i18n("Cannot add report, reason: \"%1\"", e.what());

            // write to messagehandler
            qWarning() << cm << error;

            // also inform user
            KMessageBox::error(d->ui.m_reportTabWidget, error, i18n("Critical Error"));
        }
    }
    delete dlg;
}

void KReportsView::slotDelete()
{
    Q_D(KReportsView);
    auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget());
    if (!tab) {
        // nothing to do
        return;
    }

    MyMoneyReport report = tab->report();
    if (! report.id().isEmpty()) {
        if (KMessageBox::Continue == d->deleteReportDialog(report.name())) {
            // close the tab and then remove the report so that it is not
            // generated again during the following loadView() call
            slotClose(d->ui.m_reportTabWidget->currentIndex());

            MyMoneyFileTransaction ft;
            MyMoneyFile::instance()->removeReport(report);
            ft.commit();
        }
    } else {
        KMessageBox::information(this,
                                 QString("<qt>") +
                                 i18n("<b>%1</b> is a default report, so it cannot be deleted.",
                                      report.name()) + QString("</qt>"), i18n("Delete Report?"));
    }
}


void KReportsView::slotOpenReport(const QString& id)
{
    Q_D(KReportsView);
    if (id.isEmpty()) {
        // nothing to do
        return;
    }

    KReportTab* page = nullptr;

    // Find the tab which contains the report
    int index = 1;
    while (index < d->ui.m_reportTabWidget->count()) {
        auto current = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(index));

        if (current && current->report().id() == id) {
            page = current;
            break;
        }

        ++index;
    }

    // Show the tab, or create a new one, as needed
    if (page)
        d->ui.m_reportTabWidget->setCurrentIndex(index);
    else
        d->addReportTab(MyMoneyFile::instance()->report(id), OpenImmediately);
}

void KReportsView::slotOpenReport(const MyMoneyReport& report)
{
    Q_D(KReportsView);
    if (d->m_needLoad)
        d->init();
    KReportTab* page = nullptr;

    // Find the tab which contains the report indicated by this list item
    int index = 1;
    while (index < d->ui.m_reportTabWidget->count()) {
        auto current = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(index));

        if (current && current->report().name() == report.name()) {
            page = current;
            break;
        }

        ++index;
    }

    // Show the tab, or create a new one, as needed
    if (page)
        d->ui.m_reportTabWidget->setCurrentIndex(index);
    else
        d->addReportTab(report, OpenImmediately);

    if (!isVisible())
        Q_EMIT switchViewRequested(View::Reports);
}

void KReportsView::slotDoubleClicked(const QModelIndex& index)
{
    Q_D(KReportsView);

    QModelIndex sourceIndex;
    auto* proxy = qobject_cast<const QSortFilterProxyModel*>(index.model());
    const ReportsModel* sourceModel;
    if (proxy) {
        sourceIndex = proxy->mapToSource(index);
        sourceModel = qobject_cast<const ReportsModel*>(proxy->sourceModel());
    } else {
        sourceIndex = index;
        sourceModel = qobject_cast<const ReportsModel*>(index.model());
    }

    const auto& report = sourceModel->itemByIndex(sourceIndex);

    if (!report.isBuiltIn() && !report.isCustom())
        return;

    KReportTab* page = nullptr;

    // Find the tab which contains the report indicated by this list item
    int tabIndex = 1;
    for (; tabIndex < d->ui.m_reportTabWidget->count(); ++tabIndex) {
        auto current = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(tabIndex));
        if (current) {
            // If this report has an ID, we'll use the ID to match
            if (!report.id().isEmpty()) {
                if (current->report().id() == report.id()) {
                    page = current;
                    break;
                }
            }
            // Otherwise, use the name to match.  THIS ASSUMES that no 2 default reports
            // have the same name...but that would be pretty a boneheaded thing to do.
            else {
                if (current->report().name() == report.name()) {
                    page = current;
                    break;
                }
            }
        }
    }

    // Show the tab, or create a new one, as needed
    if (page)
        d->ui.m_reportTabWidget->setCurrentIndex(tabIndex);
    else {
        d->addReportTab(report, OpenImmediately);
    }
}

void KReportsView::slotToggleChart()
{
    Q_D(const KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget()))
        tab->toggleChart();
}

bool KReportsView::hasClosableView() const
{
    Q_D(const KReportsView);
    return d->ui.m_reportTabWidget->count() > 1;
}

void KReportsView::closeCurrentView()
{
    Q_D(const KReportsView);
    const auto idx = d->ui.m_reportTabWidget->currentIndex();
    if (idx > 0) {
        slotClose(idx);
    }
}

void KReportsView::slotCloseCurrent()
{
    Q_D(const KReportsView);
    slotClose(d->ui.m_reportTabWidget->currentIndex());
}

void KReportsView::slotClose(int index)
{
    Q_D(const KReportsView);
    if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(index))) {
        d->ui.m_reportTabWidget->removeTab(index);
        tab->setReadyToDelete(true);
    }
}

void KReportsView::slotCloseAll()
{
    Q_D(const KReportsView);
    if(!d->m_needLoad) {
        while (true) {
            if (auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(1))) {
                d->ui.m_reportTabWidget->removeTab(1);
                tab->setReadyToDelete(true);
            } else
                break;
        }
    }
}

void KReportsView::slotContextMenu(const QPoint& p)
{
    Q_D(const KReportsView);
    auto* view = qobject_cast<QTreeView*>(sender());
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    auto contextmenu = new QMenu(this);
    contextmenu->addSection(i18nc("@title:menu Report context menu", "Report options"));
    contextmenu->addAction(pActions[eMenu::Action::ReportOpen]->icon(), pActions[eMenu::Action::ReportOpen]->text(), [this, view]() {
        slotOpenFromView(view);
    });
    contextmenu->addAction(pActions[eMenu::Action::Print]->icon(), pActions[eMenu::Action::Print]->text(), [this, view]() {
        slotPrintFromView(view);
    });
    if (indexes.count() == 1) {
        contextmenu->addAction(pActions[eMenu::Action::ReportExport]->icon(), pActions[eMenu::Action::ReportExport]->text(), [this, view]() {
            slotExportFromView(view);
        });
        contextmenu->addAction(pActions[eMenu::Action::ReportConfigure]->icon(), pActions[eMenu::Action::ReportConfigure]->text(), [this, view]() {
            slotConfigureFromView(view);
        });
        contextmenu->addAction(pActions[eMenu::Action::ReportNew]->icon(), pActions[eMenu::Action::ReportNew]->text(), [this, view]() {
            slotNewFromView(view);
        });
    } else {
        contextmenu->addAction(pActions[eMenu::Action::ReportExport]->icon(), pActions[eMenu::Action::ReportExport]->text(), [this, view]() {
            slotExportFromView(view);
        });
    }
    if (view == d->ui.m_tocTreeViewCustom) {
        contextmenu->addAction(pActions[eMenu::Action::ReportDelete]->icon(), pActions[eMenu::Action::ReportDelete]->text(), [this, view]() {
            slotDeleteFromView(view);
        });
    }

    if (view) {
        contextmenu->popup(view->viewport()->mapToGlobal(p));
    }
}

void KReportsView::slotOpenFromView(QTreeView* view)
{
    Q_D(const KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    for (const auto& index : indexes) {
        slotDoubleClicked(index);
    }
}

void KReportsView::slotPrintFromView(QTreeView* view)
{
    Q_D(const KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    for (const auto& index : indexes) {
        slotDoubleClicked(index);
        slotPrintView();
    }
}

void KReportsView::slotExportFromView(QTreeView* view)
{
    Q_D(const KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    QString rootDir = QFileDialog::getExistingDirectory(this, i18n("Export into directory"), KRecentDirs::dir(":kmymoney-export"), QFileDialog::ShowDirsOnly);
    if (rootDir.isEmpty())
        return;

    for (const auto& index : indexes) {
        slotDoubleClicked(index);
        const auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->currentWidget());
        if (!tab)
            continue;

        MyMoneyFile* file = MyMoneyFile::instance();
        const auto& report = file->reportsModel()->itemByIndex(index);
        auto name = createSaveFileName(rootDir, report.name(), QLatin1String(".pdf"));
        tab->saveAs(name, "application/pdf");
    }
}

void KReportsView::slotConfigureFromView(QTreeView* view)
{
    Q_D(const KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    for (const auto& index : indexes) {
        slotDoubleClicked(index);
        slotConfigure();
    }
}

void KReportsView::slotNewFromView(QTreeView* view)
{
    Q_D(const KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    for (const auto& index : indexes) {
        slotDoubleClicked(index);
        slotDuplicate();
    }
}

void KReportsView::slotDeleteFromView(QTreeView* view)
{
    Q_D(KReportsView);
    const auto indexes = d->selectedSourceIndexes(view);
    if (indexes.isEmpty()) {
        return;
    }

    for (const auto& index : indexes) {
        MyMoneyFile* file = MyMoneyFile::instance();
        const auto& report = file->reportsModel()->itemByIndex(index);

        // If this report does not have an ID, it's a default report and cannot be deleted
        if (!report.id().isEmpty() && KMessageBox::Continue == d->deleteReportDialog(report.name())) {
            // check if report's tab is open; start from 1 because 0 is toc tab
            for (int i = 1; i < d->ui.m_reportTabWidget->count(); ++i) {
                auto tab = dynamic_cast<KReportTab*>(d->ui.m_reportTabWidget->widget(i));
                if (tab && tab->report().id() == report.id()) {
                    slotClose(i); // if open, close it, so no crash when switching to it
                    break;
                }
            }
            MyMoneyFileTransaction ft;
            MyMoneyFile::instance()->removeReport(report);
            ft.commit();
        }
    }
}

// Make sure, that these definitions are only used within this file
// this does not seem to be necessary, but when building RPMs the
// build option 'final' is used and all CPP files are concatenated.
// So it could well be, that in another CPP file these definitions
// are also used.
#undef VIEW_LEDGER
#undef VIEW_SCHEDULE
#undef VIEW_WELCOME
#undef VIEW_HOME
#undef VIEW_REPORTS
