/*
 * 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 "session.h"
#include "../webengine/webview.h"
#include "browser.h"
#include "configuration.h"
#include "mainwindow/mainwindow.h"
#include "subwindow/subwindow.h"
#include "webengine/webprofilemanager.h"
#include "webengine/webview.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QWebEngineHistory>
#include <memory>

QJsonObject Session::fromCommandLine(const QString &profile, const QStringList &urls)
{
    QJsonObject session;

    QJsonArray subwindows;
    {
        QJsonObject window;
        window.insert("profile", profile);

        QJsonArray tabs;
        for(const auto &url : urls) {
            QJsonObject tab;
            tab.insert("url", url);
            tab.insert("profile", profile);
            tabs.append(tab);
        }
        window.insert("tabs", tabs);

        subwindows.append(window);
    }
    session.insert("subwindows", subwindows);

    return session;
}

//////

QJsonObject Session::_session(const QVector<MainWindow *> windows)
{
    QJsonObject sessionData;

    {
        QJsonArray windowList;
        for(const auto *window : windows) {
            windowList.append(Session::_window(window));
        }
        sessionData.insert("windows", windowList);
    }

    return sessionData;
}

QJsonObject Session::_window(const MainWindow *window)
{
    QJsonObject windowData;

    {
        QJsonArray subwindows;
        for(const auto *subwindow : window->subWindows()) {
            subwindows.append(Session::_subwindow(subwindow));
        }
        windowData.insert("subwindows", subwindows);
    }

    return windowData;
}

QJsonObject Session::_subwindow(const SubWindow *subwindow)
{
    const auto *profileManager = WebProfileManager::instance();
    Q_CHECK_PTR(profileManager);

    QJsonObject subwindowData;

    subwindowData.insert("profile", profileManager->id(subwindow->profile()));

    {
        QJsonArray tabs;
        for(int i = 0; i < subwindow->tabCount(); ++i) {
            tabs.append(Session::view(subwindow->view(i)));
        }
        subwindowData.insert("tabs", tabs);
    }

    return subwindowData;
}

QJsonObject Session::view(const WebView *view)
{
    const auto *profileManager = WebProfileManager::instance();
    Q_CHECK_PTR(profileManager);

    QByteArray historyData;
    QDataStream historyStream(&historyData, QIODevice::WriteOnly);
    historyStream << *view->history();

    QJsonObject viewData;
    viewData.insert("profile", profileManager->id(view->profile()));

    // store history: compress, toBase64 (to make printable), toQString (to store in json)
    viewData.insert("history", QString(qCompress(historyData).toBase64()));

    return viewData;
}

void Session::restoreView(WebView *view, const QJsonObject &data)
{
    const auto *profileManager = WebProfileManager::instance();
    Q_CHECK_PTR(profileManager);

    auto *profile = profileManager->profile(data["profile"].toString());
    if(profile != nullptr)
        view->setProfile(profile);

    auto url = data.value("url");
    if(url.isString())
        view->load(QUrl::fromUserInput(url.toString()));
    else {
        // restore history: toLatin1 (turn into bytearray), fromBase64, uncompress
        QByteArray historyData = qUncompress(QByteArray::fromBase64(data["history"].toString().toLatin1()));
        QDataStream historyStream(&historyData, QIODevice::ReadOnly);
        historyStream >> *view->history();
    }
}

void Session::restoreSession(const QJsonObject &sessionData)
{
    auto *browser = dynamic_cast<Browser *>(qApp);
    Q_CHECK_PTR(browser);
    const auto *profileManager = WebProfileManager::instance();
    Q_CHECK_PTR(profileManager);

    for(const auto subwindowData : sessionData["subwindows"].toArray()) {
        MainWindow *window = nullptr;
        SubWindow *subwindow = nullptr;

        if(!browser->windows().isEmpty()) {
            window = browser->windows().last();
            subwindow = window->currentSubWindow();
        } else {
            window = browser->createWindow();
            subwindow = window->createSubWindow(profileManager->profile(subwindowData.toObject()["profile"].toString()));
        }

        Q_CHECK_PTR(window);
        Q_CHECK_PTR(subwindow);

        for(const auto tabData : subwindowData.toObject()["tabs"].toArray()) {
            auto *view = subwindow->view(subwindow->addTab());
            Session::restoreView(view, tabData.toObject());
        }
    }

    for(const auto windowData : sessionData["windows"].toArray()) {
        auto *window = browser->createWindow();

        for(const auto subwindowData : windowData.toObject()["subwindows"].toArray()) {
            auto *subwindow = window->createSubWindow(profileManager->profile(subwindowData.toObject()["profile"].toString()));

            for(const auto tabData : subwindowData.toObject()["tabs"].toArray()) {
                auto *view = subwindow->view(subwindow->addTab());
                Session::restoreView(view, tabData.toObject());
            }
        }
    }
}