#include "webviewwindow.h"
#include "utils.h"

#include "webviewqt.h"
#include "rktwebview_qt.h"
#include "rktutils.h"
#include "command.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QWebEngineScriptCollection>
#include <QWebEngineScript>
#include <QWebEngineProfile>
#include <QWebEngineProfileBuilder>
#include <QWebEnginePage>
#include <QMoveEvent>
#include <QWindow>
#include <QWebEngineProfileBuilder>


/*
static void displ(QSslCertificate & cert, QSslCertificate::SubjectInfo  &a) {
    QStringList issuerInfo = cert.issuerInfo(a);
    int i, N;
    printf("%d\n", a);
    for(i = 0, N = issuerInfo.size(); i < N; i++) {
        printf("%d[%d]: %s\n", a, i, issuerInfo[i].toUtf8().constData());
    }
};

static void displ1(QSslCertificate &cert, QList<QSslCertificate::SubjectInfo > l) {
    int i, N;
    for(i = 0, N = l.size(); i < N; i++) {
        displ(cert, l[i]);
    }
};
*/

WebviewWindow::WebviewWindow(QWebEngineProfile *profile, WebviewWindow *parent)
    : QMainWindow{parent}
{
    static int profile_nr = 0;

    _view = nullptr;
    _must_close = false;

    _devtools = nullptr;
    _parent = parent;

    _window_created = false;
    _moved = 0;
    _resized = 0;

    _profile = profile;
    _navigation_event_sent = false;

    if (parent != nullptr) {
        setWindowModality(Qt::WindowModality::WindowModal);
        setWindowFlag(Qt::WindowType::Dialog, true);
    }

    connect(&_resize_timer, &QTimer::timeout, this, &WebviewWindow::triggerResize);
    connect(&_move_timer, &QTimer::timeout, this, &WebviewWindow::triggerMove);
}

void WebviewWindow::navigationRequested(QWebEngineNavigationRequest &req)
{
    QWebEngineNavigationRequest::NavigationType t = req.navigationType();
    if (t == QWebEngineNavigationRequest::NavigationType::TypedNavigation ||
        t == QWebEngineNavigationRequest::RedirectNavigation) {
        _navigation_event_sent = false;
        req.accept();
    } else {
        EventContainer e("navigation-request");

        QString u = req.url().toString();

        e["url"] = u;

        QString type;
        switch (req.navigationType()) {
        case QWebEngineNavigationRequest::NavigationType::LinkClickedNavigation: type = "link-clicked";
            break;
        case QWebEngineNavigationRequest::NavigationType::TypedNavigation: type = "typed";
            break;
        case QWebEngineNavigationRequest::NavigationType::FormSubmittedNavigation: type = "form-submit";
            break;
        case QWebEngineNavigationRequest::NavigationType::BackForwardNavigation: type = "back-or-forward";
            break;
        case QWebEngineNavigationRequest::NavigationType::ReloadNavigation: type = "reload";
            break;
        case QWebEngineNavigationRequest::NavigationType::RedirectNavigation: type = "redirect";
            break;
        default: type = "other";
            break;
        }

        e["type"] = type;
        _container->triggerEvent(_view->id(), e);

        _navigation_event_sent = true;

        req.reject();
    }
}

void WebviewWindow::handleCertificate(const QWebEngineCertificateError &certificateError)
{
    /*
    QList<QSslCertificate> certs = _view->page()->profile()->additionalTrustedCertificates();

    auto dodisp = [](QList<QSslCertificate> certs)
    {
        int i;
        for(i = 0; i < certs.size(); i++) {
            QSslCertificate cert = certs[i];
            QList<QSslCertificate::SubjectInfo> attrs;
            attrs.append(QSslCertificate::Organization);
            attrs.append(QSslCertificate::OrganizationalUnitName);
            attrs.append(QSslCertificate::CountryName);
            attrs.append(QSslCertificate::CommonName);
            displ1(cert, attrs);
        }
    };
    dodisp(certs);
    dodisp(certificateError.certificateChain());
*/

    ERROR1("Certificate Error: %s\n", certificateError.description().toUtf8().constData());
    QList<QSslCertificate> chain = certificateError.certificateChain();
    int i;
    for(i = 0; i < chain.size(); i++) {
        const QSslCertificate &cert = chain[i];
        if (cert.isSelfSigned()) {
            QStringList l = cert.issuerInfo(QSslCertificate::OrganizationalUnitName);
            if (!l.empty()) {
                QString ou = l[0];
                if (ou != "" && ou == _ou_token) {
                    QWebEngineCertificateError &e = const_cast<QWebEngineCertificateError &>(certificateError);
                    e.acceptCertificate();
                }
            }
        }
    }
}

void WebviewWindow::processJsEvents()
{
    QWebEnginePage *p = _view->page();
    p->runJavaScript("if (window.rkt_get_events) { window.rkt_get_events(); } else { JSON.stringify([]); }",
                     [this](const QVariant &v) {
                         QString s = v.toString();
                         QJsonParseError err;
                         QJsonDocument doc(QJsonDocument::fromJson(s.toUtf8(), &err));
                         if (err.error == QJsonParseError::NoError) {
                             QJsonArray a = doc.array();
                             int i, N;
                             for(i = 0, N = a.size(); i < N; i++) {
                                 QJsonObject js_evt = a[i].toObject();

                                 EventContainer e("js-evt");
                                 e["js-evt"] = js_evt;

                                 this->_container->triggerEvent(_view->id(), e);
                             }
                         } else {

                         }
                     }
                     );
}

void WebviewWindow::closeEvent(QCloseEvent *evt)
{
    if (_must_close) {
        EventContainer e("closed");
        _container->triggerEvent(_view->id(), e);
        _container->removeView(_view->id());
        _view->deleteLater();
        this->deleteLater();
        if (_devtools != nullptr) {
            _devtools->close();
            _devtools->deleteLater();
        }
    } else {
        evt->ignore();
        EventContainer e("can-close?");
        _container->triggerEvent(_view->id(), e);
    }
}

void WebviewWindow::closeView()
{
    _must_close = true;
    close();
}

bool WebviewWindow::windowCreated()
{
    return _window_created;
}

int WebviewWindow::moveCount()
{
    return _moved;
}

int WebviewWindow::resizeCount()
{
    return _resized;
}

void WebviewWindow::setOUToken(const QString &token)
{
    _ou_token = token;
}

bool WebviewWindow::navigationEventSent()
{
    return _navigation_event_sent;
}

void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c)
{
    _container = c;
    _view = v;
    this->setCentralWidget(v);

    QWebEnginePage *page = _view->page();

    connect(page, &QWebEnginePage::loadFinished, this, [this](bool ok) {
        _container->pageLoaded(_view->id(), ok);
    });
    connect(page, &QWebEnginePage::loadStarted, this, [this]() {
        _container->onPageLoad(_view->id());
    });
    connect(page, &QWebEnginePage::navigationRequested, this, &WebviewWindow::navigationRequested);

    connect(page, &QWebEnginePage::certificateError, this, &WebviewWindow::handleCertificate);

    connect(page, &QWebEnginePage::windowCloseRequested, this, &WebviewWindow::closeView);

    connect(page, &QWebEnginePage::linkHovered, this, &WebviewWindow::linkHovered);

    connect(page, &QWebEnginePage::printRequestedByFrame, this, &WebviewWindow::evtRequested);
}

WebViewQt *WebviewWindow::view()
{
    return _view;
}

void WebviewWindow::runJs(const QString &js)
{
    QWebEnginePage *p = _view->page();
    p->runJavaScript(js);
}

void WebviewWindow::callJs(const QString &js, Command *c)
{
    QWebEnginePage *p = _view->page();
    QString _js = QString("{\n") +
                          "  let f = function() {\n" +
                          "    " + js + "\n" +
                          "  };\n" +
                          "  try {\n" +
                          "     let obj = { oke: true, result: f(), exn: false };\n" +
                          "     obj;\n" +
                          "  } catch(e) {\n" +
                          "     let obj = { oke: false, result: false, exn: e.message };\n" +
                          "     obj;\n" +
                          "  }\n" +
                          "}";
    p->runJavaScript(_js, [c](const QVariant &v) {
                                c->result = v;
                                QJsonObject obj = v.toJsonObject();
                                bool ok = obj["oke"].toBool();
                                c->js_result_ok = ok;
                                c->result = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Compact));
                                c->done = true;
    });
}

void WebviewWindow::openDevTools()
{
    _devtools = new QMainWindow(this);
    QWebEngineView *devtools_view = new QWebEngineView(_devtools);
    _devtools->setCentralWidget(devtools_view);
    _devtools->resize(800, 600);
    _devtools->show();
    _view->page()->setDevToolsPage(devtools_view->page());
    connect(_devtools, &WebviewWindow::destroyed, this, [this](QObject *) {
        _devtools = nullptr;
    });
}

void WebviewWindow::linkHovered(const QUrl &u)
{
    QString s = u.toString();
    EventContainer hover_event("link-hovered");
    hover_event["url"] = s;
    _container->triggerEvent(_view->id(), hover_event);
}

void WebviewWindow::evtRequested(QWebEngineFrame frame)
{
    if (frame.name() == "rkt-evt-frame") {
        processJsEvents();
    }
}


void WebviewWindow::moveEvent(QMoveEvent *event)
{
    _x = event->pos().x();
    _y = event->pos().y();
    _move_timer.setSingleShot(true);
    _move_timer.start(500);
    _moved += 1;
    //triggerMove();
}

void WebviewWindow::triggerMove()
{
    EventContainer xy("move");
    xy["x"] = _x;
    xy["y"] = _y;
    _container->triggerEvent(_view->id(), xy);
}

QWebEngineProfile *WebviewWindow::profile()
{
    return _profile;
}

void WebviewWindow::resizeEvent(QResizeEvent *event)
{
    _w = event->size().width();
    _h = event->size().height();
    _resize_timer.setSingleShot(true);
    _resize_timer.start(500);
    _resized += 1;
    //triggerResize();
}

void WebviewWindow::triggerResize()
{
    EventContainer s("resize");
    s["w"] = _w;
    s["h"] = _h;
    _container->triggerEvent(_view->id(), s);
}

void WebviewWindow::showEvent(QShowEvent *event)
{
    _window_created = true;

    EventContainer show("show");
    _container->triggerEvent(_view->id(), show);
}

void WebviewWindow::hideEvent(QHideEvent *event)
{
    if (!_must_close) {
        EventContainer hide("hide");
        _container->triggerEvent(_view->id(), hide);
    }
}


