/*
    main part of parley
    SPDX-FileCopyrightText: 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
    SPDX-FileCopyrightText: 2004-2007 Peter Hedlund <peter.hedlund@kdemail.net>
    SPDX-FileCopyrightText: 2007-2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
    SPDX-FileCopyrightText: 2008 Daniel Laidig <d.laidig@gmx.de>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "parleymainwindow.h"
#include "dashboard/dashboard.h"
#include "editor/editor.h"
#include "parleyactions.h"
#include "practice/configure/configurepracticedialog.h"
#include "practice/guifrontend.h"
#include "practice/practicemainwindow.h"
#include "practice/practicesummarycomponent.h"
#include "prefs.h"
#include "settings/parleyprefs.h"
#include "statistics/statisticsmainwindow.h"
#include <KActionCollection>
#include <KMessageBox>
#include <KNSWidgets/Action>
#include <KRecentFilesAction>
#include <KToolBar>
#include <KXMLGUIFactory>
#include <QMenuBar>
#include <QTimer>
#include <config-parley.h>

using namespace Editor;

ParleyMainWindow *ParleyMainWindow::s_instance = nullptr;
ParleyMainWindow *ParleyMainWindow::instance()
{
    return s_instance;
}

ParleyMainWindow::ParleyMainWindow(const QUrl &filename)
    : KXmlGuiWindow(nullptr)
    , m_currentComponent(NoComponent)
    , m_sessionManager(this)
{
    s_instance = this;
    m_document = new ParleyDocument(this);

    connect(m_document, &ParleyDocument::documentChanged, this, &ParleyMainWindow::documentUpdated);

    setCentralWidget(new QWidget());
    centralWidget()->setLayout(new QHBoxLayout());
    centralWidget()->layout()->setContentsMargins(0, 0, 0, 0);

    initActions();

    bool startWithDashboard = false;

    setupGUI(ToolBar | Keys | Create); ///@todo frameworks KXMLGui warnings

    if (!filename.isEmpty()) {
        m_document->open(filename);
    } else {
        bool openLastFile = Prefs::autoOpenLast();
        if (openLastFile && m_recentFilesAction->actions().count() > 0
            && m_recentFilesAction->action(m_recentFilesAction->actions().count() - 1)->isEnabled()) {
            m_recentFilesAction->action(m_recentFilesAction->actions().count() - 1)->trigger();
        } else {
            startWithDashboard = true;
        }
    }

    // save position of dock windows etc
    setAutoSaveSettings();

    if (startWithDashboard) {
        showDashboard();
    } else {
        showEditor();
    }

    connect(this, &ParleyMainWindow::preferencesChanged, this, &ParleyMainWindow::slotApplyPreferences);
    menuBar()->show();
}

ParleyMainWindow::~ParleyMainWindow()
{
    guiFactory()->removeClient(m_currentGuiClient);
    centralWidget()->layout()->removeWidget(m_currentCentralWidget);
    delete m_currentCentralWidget;

    // Prevent calling this slot with already deleted m_document as it is no longer needed anyway
    if (m_document->document()) {
        disconnect(m_document->document().get(), &KEduVocDocument::destroyed, this, &ParleyMainWindow::slotUpdateWindowCaption);
    }
    delete m_document;
}

void ParleyMainWindow::addRecentFile(const QUrl &url, const QString &name)
{
    m_recentFilesAction->addUrl(url, name);
    m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group(QStringLiteral("Recent Files")));
}

void ParleyMainWindow::removeRecentFile(const QUrl &url)
{
    m_recentFilesAction->removeUrl(url);
    m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group(QStringLiteral("Recent Files")));
}

void ParleyMainWindow::documentUpdated(const std::shared_ptr<KEduVocDocument> &doc)
{
    if (doc) {
        connect(doc.get(), &KEduVocDocument::docModified, this, &ParleyMainWindow::slotUpdateWindowCaption);
        connect(doc.get(), &KEduVocDocument::destroyed, this, &ParleyMainWindow::slotUpdateWindowCaption);
        slotUpdateWindowCaption();
    }
}

void ParleyMainWindow::updateRecentFilesModel()
{
    Q_EMIT recentFilesChanged();
}

void ParleyMainWindow::saveOptions()
{
    Prefs::self()->save();
}

void ParleyMainWindow::slotUpdateWindowCaption()
{
    QString title;
    bool modified = false;
    if (m_document->document()) {
        title = i18nc("Title and a modified status indicator.  [*] is exact and will be shown only when document is modified",
                      "%1 [*]",
                      m_document->document()->title());
        modified = m_document->document()->isModified();
        if (title == i18n("Untitled")) {
            title = QStringLiteral("[*]");
        }
    }
    setCaption(title, modified);
}

void ParleyMainWindow::slotGeneralOptions()
{
    ParleyPrefs *dialog = new ParleyPrefs(m_document->document().get(), this, QStringLiteral("settings"), Prefs::self());
    connect(dialog, &ParleyPrefs::settingsChanged, this, &ParleyMainWindow::preferencesChanged);
    dialog->show();
}

void ParleyMainWindow::slotApplyPreferences()
{
    m_document->enableAutoBackup((m_currentComponent != DashboardComponent) && Prefs::autoBackup());
}

void ParleyMainWindow::slotCloseDocument()
{
    if (!queryClose()) {
        return;
    }
    showDashboard();
    m_document->close();
}

void ParleyMainWindow::configurePractice()
{
    ConfigurePracticeDialog configurePracticeDialog(m_document->document().get(), this, QStringLiteral("practice settings"), Prefs::self());
    configurePracticeDialog.exec();
}

void ParleyMainWindow::startPractice()
{
    if (Prefs::learningLanguage() == Prefs::knownLanguage()) {
        KMessageBox::error(this, i18n("You cannot start to practice when the known language is the same as the language to learn."), i18n("Select languages"));
        return;
    }
    qDebug() << "Starting Switch Practice";
    switchComponent(PracticeComponent);
    qDebug() << "Finished Switch Practice";
}

void ParleyMainWindow::practiceFinished()
{
    switchComponent(m_componentBeforePractice);
}

bool ParleyMainWindow::queryClose()
{
    if (m_document->queryClose()) {
        Prefs::self()->save();
        return true;
    }
    return false;
}

QSize ParleyMainWindow::sizeHint() const
{
    return QSize(800, 600).expandedTo(KXmlGuiWindow::minimumSizeHint());
}

void ParleyMainWindow::slotFileNew()
{
    m_document->slotFileNew();
    slotUpdateWindowCaption();
}

void ParleyMainWindow::initActions()
{
    ParleyActions::create(ParleyActions::FileNew, this, SLOT(slotFileNew()), actionCollection());
    ParleyActions::create(ParleyActions::FileOpen, m_document, SLOT(slotFileOpen()), actionCollection());

    KNSWidgets::Action *pAction = new KNSWidgets::Action(i18n("Download New Vocabularies..."), QStringLiteral("parley.knsrc"), actionCollection());
    actionCollection()->addAction(QStringLiteral("file_ghns"), pAction);
    QObject::connect(pAction, &KNSWidgets::Action::dialogFinished, m_document, &ParleyDocument::slotGHNS);

    pAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
    actionCollection()->setDefaultShortcut(pAction, QKeySequence(Qt::CTRL | Qt::Key_G));
    pAction->setToolTip(i18n("Downloads new vocabulary collections"));

    ParleyActions::create(ParleyActions::FileOpenDownloaded, m_document, SLOT(openGHNS()), actionCollection());

    m_recentFilesAction = ParleyActions::createRecentFilesAction(m_document, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
    m_recentFilesAction->loadEntries(KSharedConfig::openConfig()->group(QStringLiteral("Recent Files")));

    ParleyActions::create(ParleyActions::FileSave, m_document, SLOT(save()), actionCollection());
    ParleyActions::create(ParleyActions::FileSaveAs, m_document, SLOT(saveAs()), actionCollection());
#ifdef HAVE_LIBXSLT
    ParleyActions::create(ParleyActions::FileExport, m_document, SLOT(exportDialog()), actionCollection());
#endif

    ParleyActions::create(ParleyActions::FileProperties, m_document, SLOT(documentProperties()), actionCollection());

    ParleyActions::create(ParleyActions::FileClose, this, SLOT(slotCloseDocument()), actionCollection());
    ParleyActions::create(ParleyActions::FileQuit, this, SLOT(close()), actionCollection());
    ParleyActions::create(ParleyActions::Preferences, this, SLOT(slotGeneralOptions()), actionCollection());
}

void ParleyMainWindow::showDashboard()
{
    switchComponent(DashboardComponent);
}

void ParleyMainWindow::showEditor()
{
    switchComponent(EditorComponent);
}

void ParleyMainWindow::showPracticeConfiguration()
{
    switchComponent(ConfigurePracticeComponent);
}

void ParleyMainWindow::showPractice()
{
    switchComponent(PracticeComponent);
}

void ParleyMainWindow::showPracticeSummary()
{
    switchComponent(PracticeSummary);
}

void ParleyMainWindow::switchComponent(Component component)
{
    if (component == PracticeComponent) {
        StatisticsMainWindow *statisticsWidget = qobject_cast<StatisticsMainWindow *>(m_currentCentralWidget);
        if (statisticsWidget) {
            statisticsWidget->syncConfig();
        }

        // Don't start a practice when there are no words to practice.
        // This has to be checked before deleting the old component.
        m_sessionManager.setDocument(m_document->document().get());
        if (!m_sessionManager.allEntryCount()) {
            return;
        }
    }

    // Remove and delete the old component window if there is one active.
    if (m_currentCentralWidget) {
        Q_ASSERT(m_currentGuiClient); // gui client and central widget are same object
        guiFactory()->removeClient(m_currentGuiClient);
        centralWidget()->layout()->removeWidget(m_currentCentralWidget);
        delete m_currentCentralWidget;
        m_currentCentralWidget = nullptr;
    }

    switch (component) {
    case DashboardComponent: {
        Dashboard *dashboard = new Dashboard(this);
        m_currentCentralWidget = dashboard;
        m_currentGuiClient = dashboard;
        showDocumentActions(true, false);
        // dashboard->updateRecentFilesModel();
        break;
    }
    case ConfigurePracticeComponent: {
        StatisticsMainWindow *statisticsWidget = new StatisticsMainWindow(m_document->document(), this);
        m_currentCentralWidget = statisticsWidget;
        m_currentGuiClient = statisticsWidget;
        showDocumentActions(true, true);
        break;
    }
    case EditorComponent: {
        EditorWindow *editor = new EditorWindow(this);
        m_currentCentralWidget = editor;
        m_currentGuiClient = editor;
        showDocumentActions(true, true);
        editor->updateDocument(m_document->document());
        break;
    }
    case PracticeComponent: {
        ///@todo trust the dirty bit
        m_document->document()->setModified(true);
        Practice::PracticeMainWindow *practiceWindow = new Practice::PracticeMainWindow(&m_sessionManager, this);
        connect(practiceWindow, &Practice::PracticeMainWindow::stopPractice, this, &ParleyMainWindow::showPracticeSummary);
        m_currentCentralWidget = practiceWindow;
        m_currentGuiClient = practiceWindow;
        qDebug() << " Practice Slotted up";
        showDocumentActions(false, false);
        practiceWindow->startPractice();
        qDebug() << " Practice Slotted up2";
        break;
    }
    case PracticeSummary: {
        Practice::PracticeSummaryComponent *summary = new Practice::PracticeSummaryComponent(&m_sessionManager, this);
        m_currentCentralWidget = summary;
        m_currentGuiClient = summary;
        showDocumentActions(true, true);
        break;
    }
    default:
        break;
    }

    guiFactory()->addClient(m_currentGuiClient);
    centralWidget()->layout()->addWidget(m_currentCentralWidget);
    m_currentCentralWidget->show();
    switch (component) {
    case DashboardComponent: {
        setVisibleToolbar(QString());
        break;
    }
    case ConfigurePracticeComponent: {
        setVisibleToolbar(QStringLiteral("statisticsToolBar"));
        break;
    }
    case EditorComponent: {
        setVisibleToolbar(QStringLiteral("editorToolBar"));
        break;
    }
    case PracticeComponent: {
        setVisibleToolbar(QStringLiteral("practiceToolBar"));
        break;
    }
    case PracticeSummary: {
        setVisibleToolbar(QStringLiteral("practiceSummaryToolBar"));
        break;
    }
    default:
        break;
    }
    m_currentComponent = component;
    setupToolbarMenuActions();
}

void ParleyMainWindow::showDocumentActions(bool open, bool edit)
{
    actionCollection()->action(QStringLiteral("file_new"))->setVisible(open);
    actionCollection()->action(QStringLiteral("file_open"))->setVisible(open);
    actionCollection()->action(QStringLiteral("file_open_recent"))->setVisible(open);
    actionCollection()->action(QStringLiteral("file_open_downloaded"))->setVisible(open);

    actionCollection()->action(QStringLiteral("file_save"))->setVisible(edit);
    actionCollection()->action(QStringLiteral("file_save_as"))->setVisible(edit);
    actionCollection()->action(QStringLiteral("file_close"))->setVisible(edit);
#ifdef HAVE_LIBXSLT
    actionCollection()->action("file_export")->setVisible(edit);
#endif
    actionCollection()->action(QStringLiteral("file_properties"))->setVisible(edit);
    actionCollection()->action(QStringLiteral("file_close"))->setVisible(edit);
}

void ParleyMainWindow::setVisibleToolbar(const QString &name)
{
    const QList<KToolBar *> toolbars = toolBars();
    for (KToolBar *toolbar : toolbars) {
        if (toolbar && toolbar->objectName() == name) {
            toolbar->show();
        } else if (toolbar) {
            toolbar->hide();
        }
    }
}

ParleyDocument *ParleyMainWindow::parleyDocument()
{
    return m_document;
}

ParleyMainWindow::Component ParleyMainWindow::currentComponent()
{
    return m_currentComponent;
}

#include "moc_parleymainwindow.cpp"
