/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "mainwindow.h"
#include "addressbar.h"
#include "browser.h"
#include "config.h"
#include "configuration.h"
#include "menubar.h"
#include "webprofilemanager.h"
#include "session/session.h"
#include "session/sessiondialog.h"
#include "subwindow/subwindow.h"
#include "webengine/webview.h"
#include "webprofile.h"
#include "widgets/dockwidget.h"
#include "widgets/navigationbar.h"
#include "widgets/searchform.h"
#include <QApplication>
#include <QCloseEvent>
#include <QFileDialog>
#include <QJsonArray>
#include <QJsonObject>
#include <QLineEdit>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QMenuBar>
#include <QMessageBox>
#include <QShortcut>
#include <QStatusBar>
#include <QToolBar>
#include <QUrl>
#ifdef CONFIG_PLASMA_BLUR
#include <KWindowEffects>
#endif

MainWindow::MainWindow(const std::unique_ptr<Configuration> &config, QWidget *parent)
    : QMainWindow(parent)
    , configuration(config.get())
    , mdiArea(new QMdiArea(this))
{
    Q_ASSERT(config);

    m_menuBar = new MenuBar(config.get(), this);
    this->setMenuBar(m_menuBar);

#ifdef CONFIG_PLASMA_BLUR
    setAttribute(Qt::WA_TranslucentBackground, true);
    KWindowEffects::enableBlurBehind(this->winId(), true);
#endif

    // create UI
    setWindowTitle(config->value<QString>("mainwindow.title").value());
    resize(config->value<int>("mainwindow.width").value(), config->value<int>("mainwindow.height").value());
    if(config->value<bool>("mainwindow.maximized").value()) {
        setWindowState(Qt::WindowMaximized);
    }
    show();

    // current subwindow shortcut
    {
        QAction *subwindowMenuAction = new QAction(this);
        QMainWindow::addAction(subwindowMenuAction);
        config->setShortcut(subwindowMenuAction, "subwindow.shortcuts.menu");
        connect(subwindowMenuAction, &QAction::triggered, this, [this]() {
            QMdiSubWindow *window = mdiArea->currentSubWindow();
            if(window != nullptr) {
                // show the menu at the subwindow position
                // position has to be global, and mapped by the mdiArea (parentWidget() of the subwindow)
                const auto position = mdiArea->mapToGlobal(window->pos());
                window->systemMenu()->exec(position);
            }
        });
    }

    navigationToolBar = new NavigationBar(config.get(), this);
    navigationToolBar->setMovable(config->value<bool>("navigation.movable").value());
    addToolBar(Qt::TopToolBarArea, navigationToolBar);
    navigationToolBar->connectWebView(nullptr);

    addressBar = new AddressBar(config->section("addressbar"), this);
    navigationToolBar->addWidget(addressBar);

    mdiArea->setBackground(Qt::NoBrush);
    setCentralWidget(mdiArea);
    mdiArea->setFocus();

    // status bar
    searchBox = new SearchForm(this);
    statusBar()->addPermanentWidget(searchBox);
    searchBox->setVisible(false);

    // connect signlas
    connect(mdiArea, &QMdiArea::subWindowActivated, this, [this](QMdiSubWindow *window) {
        disconnect(viewChangedConnection);
        disconnect(searchBoxConnection);
        disconnect(statusBarConnection);

        auto *w = qobject_cast<SubWindow *>(window);
        if(w == nullptr) {
            // no current subwindow, clear everything
            setView(nullptr);
        } else {
            setView(w->currentView());
            viewChangedConnection = connect(w, &SubWindow::currentViewChanged, this, &MainWindow::setView);
            statusBarConnection = connect(w, &SubWindow::showStatusMessage, statusBar(), &QStatusBar::showMessage);
        }
    });

    // address bar
    connect(addressBar, &AddressBar::search, this, [this](const QString &term) {
        if(this->currentView != nullptr) {
            currentView->search(term);
            currentView->setFocus();
        }
    });
    connect(addressBar, &AddressBar::load, this, [this](const QUrl &url) {
        if(this->currentView != nullptr) {
            currentView->load(url);
            currentView->setFocus();
        }
    });

    connect(addressBar, &AddressBar::giveFocus, this, [this]() {
        if(this->currentView != nullptr) {
            currentView->setFocus();
        }
    });

    // search box
    auto *searchAction = new QAction(this);
    config->setShortcut(searchAction, "mainwindow.shortcuts.search");
    connect(searchAction, &QAction::triggered, this, [=]() {
        /* QTBUG-18665
         * When focusing out of the search box and hiding it, the first
         * (or earlier?) subwindow gets activated for some reason.
         */
        if(searchBox->isVisible()) {
            auto *w = mdiArea->currentSubWindow();
            searchBox->hide();
            mdiArea->setActiveSubWindow(w);
        } else {
            searchBox->show();
        }
    });
    QMainWindow::addAction(searchAction);
}

MainWindow::~MainWindow()
{
    disconnect(viewChangedConnection);
    disconnect(searchBoxConnection);
    disconnect(statusBarConnection);

    disconnect(addressBar);
}

void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget)
{
    QDockWidget *lastDock = nullptr;
    const auto docks = findChildren<QDockWidget *>();
    for(QDockWidget *dock : docks) {
        if(dockWidgetArea(dock) == area)
            lastDock = dock;
    }

    DockWidget *dock = new DockWidget(widget->windowTitle(), this);
    dock->setMinimumWidth(460);
    dock->setWidget(widget);

    if(lastDock == nullptr)
        QMainWindow::addDockWidget(area, dock);
    else
        tabifyDockWidget(lastDock, dock);
}

void MainWindow::removeDockWidget(QWidget *widget)
{
    const auto docks = this->findChildren<QDockWidget *>();
    for(QDockWidget *dock : docks) {
        if(dock->widget() == widget) {
            dock->widget()->setParent(nullptr);
            dock->close();
        }
    }
}

void MainWindow::createTab(const QUrl &url)
{
    auto *w = qobject_cast<SubWindow *>(mdiArea->currentSubWindow());
    if(w != nullptr) {
        w->addTab(url);
    }
}

const QVector<SubWindow *> MainWindow::subWindows() const
{
    QVector<SubWindow *> list;
    const auto subwindows = mdiArea->subWindowList();
    for(auto *w : subwindows) {
        auto *subwindow = qobject_cast<SubWindow *>(w);
        if(subwindow != nullptr)
            list.append(subwindow);
    }

    return list;
}

SubWindow *MainWindow::currentSubWindow() const
{
    return qobject_cast<SubWindow *>(mdiArea->currentSubWindow());
}

SubWindow *MainWindow::createSubWindow(WebProfile *profile, bool openProfileNewtab)
{
    bool shouldMaximize = true;
    // if there is a current window, use its maximize state
    if(auto *currentWindow = qobject_cast<SubWindow *>(mdiArea->currentSubWindow()); currentWindow != nullptr) {
        shouldMaximize = currentWindow->isMaximized();
    }

    auto *w = new SubWindow(configuration, this);
    m_menuBar->insertSubWindow(w);

    w->setProfile(profile);
    mdiArea->addSubWindow(w);
    if(shouldMaximize)
        w->showMaximized();
    else
        w->show();

    w->setFocus();

    if(openProfileNewtab)
        w->addTab(w->profile()->newtab());

    return w;
}

void MainWindow::setView(WebView *view)
{
    if(currentView != nullptr) {
        // disconnect old view
        disconnect(currentView, nullptr, addressBar, nullptr);
    }
    currentView = view;

    if(view != nullptr) {
        connect(view, &WebView::urlChanged, addressBar, &AddressBar::setUrl);
        addressBar->setUrl(view->url());

        connect(view, &WebView::loadProgress, addressBar, &AddressBar::setProgress);
        addressBar->setProgress(100);

    } else {
        addressBar->setUrl(QUrl());
        addressBar->setProgress(100);
    }

    navigationToolBar->connectWebView(view);
    searchBox->setView(view);
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    if(mdiArea->subWindowList().count() > 1) {
        int choice = QMessageBox::question(this, tr("Close multiple subwindows?"), tr("Do you want to close all subwindows?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
        if(choice == QMessageBox::No) {
            event->ignore();
            return;
        }
    }

    mdiArea->closeAllSubWindows();
    if(mdiArea->currentSubWindow() != nullptr)
        event->ignore();
    else
        event->accept();
}