/*
 * 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 "webview.h"
#include "subwindow/subwindow.h"
#include "webpage.h"
#include <QContextMenuEvent>
#include <QDialog>
#include <QMenu>
#include <QSlider>
#include <QStatusBar>
#include <QStyle>
#include <QToolButton>
#include <QVBoxLayout>
#include <QWebEngineContextMenuData>
#include <QWebEngineHistoryItem>
#include <QWidgetAction>
#include <web/profilemanager.h>
#include <web/webprofile.h>
#include "browser.h"

inline QAction *historyAction(QWebEngineView *view, const QWebEngineHistoryItem &item)
{
    QAction *action = new QAction(view);
    if(item.title().isEmpty())
        action->setText(item.url().toString());
    else
        action->setText(QObject::tr("%1 (%2)").arg(item.title(), item.url().toString()));

    QObject::connect(action, &QAction::triggered, view, [view, item]() {
        view->history()->goToItem(item);
    });
    return action;
}

WebView::WebView(WebProfile *profile, QWidget *parent)
    : QWebEngineView(parent)
{
    Q_CHECK_PTR(profile);
    m_profile = profile;
    setPage(new WebPage(profile, this));

    m_parentWindow = qobject_cast<SubWindow *>(parent);

    // load status and progress
    connect(this, &QWebEngineView::loadStarted, this, [this]() {
        m_loaded = false;
    });
    connect(this, &QWebEngineView::loadFinished, this, [this]() {
        m_loaded = true;
    });

#if defined(QTBUG_65223_WORKAROUND)
    connect(this, &QWebEngineView::loadProgress, this, [this](int progress) {
        if(progress == 100) {
            emit loadFinished(true);
        }
    });
#endif
}

void WebView::setProfile(WebProfile *profile)
{
    m_profile = profile;
    const auto url = this->url();
    setPage(new WebPage(profile, this));
    this->load(url);
}

bool WebView::isLoaded() const
{
    return m_loaded;
}

void WebView::search(const QString &term)
{
    const QString searchUrl = m_profile->search().arg(QString(QUrl::toPercentEncoding(term)));
    load(searchUrl);
}

WebView *WebView::createWindow(QWebEnginePage::WebWindowType type)
{
    if(m_parentWindow == nullptr) {
        qDebug("WebView::createWindow: parent window not found, returning nullptr");
        return nullptr;
    }

    // parent Window has been found
    auto index = m_parentWindow->addTab();
    WebView *view = m_parentWindow->view(index);
    switch(type) {
    case QWebEnginePage::WebBrowserWindow:
        // a complete web browser window
        break;

    case QWebEnginePage::WebBrowserTab:
        // a web browser tab
        m_parentWindow->setCurrentTab(index);
        break;

    case QWebEnginePage::WebDialog:
        // a window without decorations
        break;

    case QWebEnginePage::WebBrowserBackgroundTab:
        // a web browser tab, but don't swap to it
        break;
    }

    return view;
}

void WebView::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu *menu = new QMenu(this);
    const auto ctxdata = page()->contextMenuData();

    {
        auto *navButtons = new QWidgetAction(this);

        auto *buttons = new QWidget(this);
        auto *buttonsLayout = new QHBoxLayout();
        buttonsLayout->setContentsMargins(8, 0, 8, 0);
        buttonsLayout->setSpacing(2);

        auto *backButton = new QToolButton(this);
        backButton->setEnabled(history()->canGoBack());
        backButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack));
        connect(backButton, &QToolButton::clicked, this, [this, menu]() {
            this->back();
            menu->close();
        });
        buttonsLayout->addWidget(backButton);

        auto *forwardButton = new QToolButton(this);
        forwardButton->setEnabled(history()->canGoForward());
        forwardButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward));
        connect(forwardButton, &QToolButton::clicked, this, [this, menu]() {
            this->forward();
            menu->close();
        });
        buttonsLayout->addWidget(forwardButton);

        auto *refreshButton = new QToolButton(this);
        refreshButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
        connect(refreshButton, &QToolButton::clicked, this, [this, menu]() {
            this->reload();
            menu->close();
        });
        buttonsLayout->addWidget(refreshButton);

        buttonsLayout->addStretch();

        auto *muteButton = new QToolButton(this);
        muteButton->setCheckable(true);
        muteButton->setChecked(this->page()->isAudioMuted());
        QIcon muteIcon;
        muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolume), QIcon::Normal, QIcon::Off);
        muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolumeMuted), QIcon::Normal, QIcon::On);
        muteButton->setIcon(muteIcon);
        connect(muteButton, &QToolButton::clicked, this, [this, menu](bool checked) {
            this->page()->setAudioMuted(checked);
            menu->close();
        });
        buttonsLayout->addWidget(muteButton);

        buttons->setLayout(buttonsLayout);
        navButtons->setDefaultWidget(buttons);

        menu->addAction(navButtons);
        menu->addSeparator();
    }

    if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeNone) {
        auto *backMenu = new QMenu(tr("Back"), this);
        if(!history()->canGoBack()) {
            backMenu->setEnabled(false);
        } else {
            connect(backMenu, &QMenu::aboutToShow, this, [this, backMenu]() {
                backMenu->clear();
                const auto backItems = history()->backItems(10);
                for(const QWebEngineHistoryItem &item : backItems) {
                    backMenu->addAction(historyAction(this, item));
                }
            });
        }
        menu->addMenu(backMenu);

        auto *forwardMenu = new QMenu(tr("Forward"), this);
        if(!history()->canGoForward()) {
            forwardMenu->setEnabled(false);
        } else {
            connect(forwardMenu, &QMenu::aboutToShow, this, [this, forwardMenu]() {
                forwardMenu->clear();
                const auto forwardItems = history()->forwardItems(10);
                for(const QWebEngineHistoryItem &item : forwardItems) {
                    forwardMenu->addAction(historyAction(this, item));
                }
            });
        }
        menu->addMenu(forwardMenu);

        connect(menu->addAction(tr("Reload")), &QAction::triggered, this, [this]() {
            page()->triggerAction(QWebEnginePage::Reload);
        });
        connect(menu->addAction(tr("Reload and bypass Cache")), &QAction::triggered, this, [this]() {
            page()->triggerAction(QWebEnginePage::ReloadAndBypassCache);
        });

    } else if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeImage) {
        connect(menu->addAction(tr("Copy image to clipboard")), &QAction::triggered, this, [this]() {
            page()->triggerAction(QWebEnginePage::CopyImageToClipboard);
        });
        connect(menu->addAction(tr("Copy image URL to clipboard")), &QAction::triggered, this, [this]() {
            page()->triggerAction(QWebEnginePage::CopyImageUrlToClipboard);
        });
        if(!ctxdata.mediaUrl().isEmpty()) {
            if(this->url() != ctxdata.mediaUrl()) {
                connect(menu->addAction(tr("Open image")), &QAction::triggered, this, [this, ctxdata]() {
                    load(ctxdata.mediaUrl());
                });
                connect(menu->addAction(tr("Open image in new tab")), &QAction::triggered, this, [this, ctxdata]() {
                    createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.mediaUrl());
                });
            }
            connect(menu->addAction(tr("Save image")), &QAction::triggered, this, [this, ctxdata]() {
                page()->download(ctxdata.mediaUrl());
            });
        }

    } else {
        delete menu;
        menu = page()->createStandardContextMenu();
    }

    if(!ctxdata.linkUrl().isEmpty()) {
        menu->addSeparator();
        connect(menu->addAction(tr("Open link in new tab")), &QAction::triggered, this, [this, ctxdata]() {
            createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.linkUrl());
        });

        auto *profileManager = dynamic_cast<Browser *>(qApp)->getProfileManager();
        QMenu *newTabMenu = profileManager->createProfileMenu([this, ctxdata](WebProfile *profile) {
            auto *view = this->createWindow(QWebEnginePage::WebBrowserTab);
            view->setProfile(profile);
            view->load(ctxdata.linkUrl());
        },
            this);
        newTabMenu->setTitle(tr("Open link in new tab with profile"));
        menu->addMenu(newTabMenu);

        connect(menu->addAction(tr("Open link in new window")), &QAction::triggered, this, [this, ctxdata]() {
            createWindow(QWebEnginePage::WebBrowserWindow)->load(ctxdata.linkUrl());
        });

        connect(menu->addAction(tr("Copy link address")), &QAction::triggered, this, [this]() {
            page()->triggerAction(QWebEnginePage::CopyLinkToClipboard);
        });
    }

    // zoom widget
    {
        menu->addSeparator();

        auto *zoomSlider = new QSlider(Qt::Horizontal);
        zoomSlider->setMinimum(5);
        zoomSlider->setMaximum(50);
        zoomSlider->setValue(zoomFactor() * 10);

        auto *zoomAction = menu->addAction(tr("Zoom: %1x").arg(zoomFactor()));
        connect(zoomAction, &QAction::triggered, this, [zoomSlider]() {
            zoomSlider->setValue(10);
        });

        connect(zoomSlider, &QSlider::valueChanged, this, [this, zoomAction](int value) {
            zoomAction->setText(tr("Zoom: %1x").arg(static_cast<qreal>(value) / 10));
            setZoomFactor(static_cast<qreal>(value) / 10);
        });

        auto *zoomWidgetAction = new QWidgetAction(this);
        zoomWidgetAction->setDefaultWidget(zoomSlider);

        menu->addAction(zoomWidgetAction);
    }

    menu->setMinimumWidth(250);
    menu->exec(event->globalPos());
}

void WebView::triggerViewAction(WebView::ViewAction action)
{
    switch(action) {
    case GoHome:
        load(m_profile->homepage());
        break;
    case BookmarkPage:
        emit newBookmark(this->title(), this->url());
        break;
    }
}