/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * A web viewer with more features.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgwebview.h"

#include <qprintdialog.h>
#include <qclipboard.h>
#include <qpointer.h>
#include <qevent.h>
#include <qprintpreviewdialog.h>
#include <qapplication.h>
#include <qdesktopservices.h>
#include <qtextdocumentwriter.h>
#include <qtextdocument.h>
#ifdef SKG_WEBENGINE
#include <qwebenginepage.h>
#else
#include <qwebframe.h>
#endif
#include <qfileinfo.h>
#include <qmath.h>

#include <qmenu.h>
#include <qicon.h>
#include <kstandardaction.h>
#include <qsavefile.h>
#include <qdom.h>
#include <qnetworkreply.h>
#include <qnetworkrequest.h>
#include <qdir.h>

#include "math.h"

#include "skgtraces.h"
#include "skgmainpanel.h"
#ifdef SKG_WEBENGINE
class SKGWebEnginePage : public QWebEnginePage
{
    Q_DISABLE_COPY(SKGWebEnginePage)
public:
    explicit SKGWebEnginePage(QObject* p = nullptr)
        : QWebEnginePage(p)
    {}

    virtual bool acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame) override
    {
        if (type == QWebEnginePage::NavigationTypeLinkClicked) {
            if (url.toString().startsWith(QLatin1String("http://linkclicked/"))) {
                SKGWebView* v = qobject_cast<SKGWebView*>(this->view());
                if (v) {
                    v->emitLinkClicked(url);
                    return false;
                }
            } else {
                SKGMainPanel::getMainPanel()->openPage(url);
                return false;
            }
        }
        return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame);
    }
};

SKGWebView::SKGWebView(QWidget* iParent, const char* name, bool iWithContextualMenu)
    : QWebEngineView(iParent), m_ContextualMenu(iWithContextualMenu)
{
    setObjectName(name);
    setPage(new SKGWebEnginePage(this));
    if (m_ContextualMenu) {
        this->installEventFilter(this);
        page()->installEventFilter(this);
    }

    connect(this, &SKGWebView::fileExporter, this, [](const QString & iFileName) {
        QDesktopServices::openUrl(QUrl::fromLocalFile(iFileName));
    });
}

void SKGWebView::emitLinkClicked(const QUrl& iURL)
{
    Q_EMIT linkClicked(iURL);
}
#else
SKGWebView::SKGWebView(QWidget* iParent, const char* name)
    : QWebView(iParent)
{
    setObjectName(name);
    this->installEventFilter(this);
    page()->installEventFilter(this);

    connect(this, &SKGWebView::fileExporter, this, [](const QString & iFileName) {
        QDesktopServices::openUrl(QUrl::fromLocalFile(iFileName));
    });

    connect(this, &SKGWebView::linkClicked, this, [ = ](const QUrl & val) {
        SKGMainPanel::getMainPanel()->openPage(val);
    });

    this->page()->setForwardUnsupportedContent(true);
    connect(this->page(), &QWebPage::unsupportedContent, this, [ = ](QNetworkReply * reply) {
        openReply(reply);
    });
    connect(this->page(), &QWebPage::downloadRequested, this, [ = ](const QNetworkRequest & request) {
        QNetworkAccessManager manager;
        openReply(manager.get(request));
    });
}

void SKGWebView::openReply(QNetworkReply* reply)
{
    QString fileName = QDir::tempPath() + '/' + "export.csv";
    QFile file(fileName);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write(reply->readAll());
        file.close();
    }
    QDesktopServices::openUrl(QUrl::fromLocalFile(fileName));
    reply->deleteLater();
}
#endif

SKGWebView::~SKGWebView()
    = default;

QString SKGWebView::getState()
{
    SKGTRACEINFUNC(10);
    QDomDocument doc(QStringLiteral("SKGML"));
    QDomElement root = doc.createElement(QStringLiteral("parameters"));
    doc.appendChild(root);
    root.setAttribute(QStringLiteral("zoomFactor"), SKGServices::intToString(qMax(qRound(30.0 * log10(zoomFactor())), -10)));
    return doc.toString();
}

void SKGWebView::setState(const QString& iState)
{
    SKGTRACEINFUNC(10);
    QDomDocument doc(QStringLiteral("SKGML"));
    doc.setContent(iState);
    QDomElement root = doc.documentElement();
    QString zoomPosition = root.attribute(QStringLiteral("zoomFactor"));
    if (zoomPosition.isEmpty()) {
        zoomPosition = '0';
    }
    double z = qPow(10, (static_cast<qreal>(SKGServices::stringToInt(zoomPosition)) / 30.0));
    setZoomFactor(z);
    emit zoomChanged(z);
}

void SKGWebView::contextMenuEvent(QContextMenuEvent* iEvent)
{
    if (iEvent) {
        auto menu = new QMenu(this);
#ifdef SKG_WEBENGINE
        menu->addAction(pageAction(QWebEnginePage::Copy));
#else
        menu->addAction(pageAction(QWebPage::Copy));
#endif

        QAction* actPrint = menu->addAction(SKGServices::fromTheme(QStringLiteral("printer")), i18nc("Action", "Print..."));
        connect(actPrint, &QAction::triggered, this, &SKGWebView::onPrint);

        menu->addAction(static_cast<QAction*>(KStandardAction::printPreview(this, SLOT(onPrintPreview()), this)));

        QAction* actExport = menu->addAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Noun, user action", "Export..."));
        connect(actExport, &QAction::triggered, this, &SKGWebView::onExport);

        menu->popup(this->mapToGlobal(iEvent->pos()));

        iEvent->accept();
    }
}

bool SKGWebView::eventFilter(QObject* iObject, QEvent* iEvent)
{
    SKGTRACEINFUNC(10);
    if (iEvent && iEvent->type() == QEvent::Wheel) {
        QWheelEvent* e = static_cast<QWheelEvent*>(iEvent);
        if (e) {
            if (e->orientation() == Qt::Vertical && QApplication::keyboardModifiers() &Qt::ControlModifier) {
                int numDegrees = e->delta() / 8;
                int numTicks = numDegrees / 15;

                if (numTicks > 0) {
                    onZoomIn();
                } else {
                    onZoomOut();
                }
                e->setAccepted(true);
                return true;
            }
        }
    }
    return QWidget::eventFilter(iObject, iEvent);
}

void SKGWebView::onZoomIn()
{
    _SKGTRACEINFUNC(10);
    int z = qMin(static_cast<int>(qRound(30.0 * log10(zoomFactor()))) + 1, 10);
    setZoomFactor(qPow(10, static_cast<qreal>(z) / 30.0));

    emit zoomChanged(z);
}

void SKGWebView::onZoomOut()
{
    _SKGTRACEINFUNC(10);
    int z = qMax(static_cast<int>(qRound(30.0 * log10(zoomFactor()))) - 1, -10);
    setZoomFactor(qPow(10, static_cast<qreal>(z) / 30.0));

    emit zoomChanged(z);
}

void SKGWebView::onZoomOriginal()
{
    _SKGTRACEINFUNC(10);
    setZoomFactor(0);

    emit zoomChanged(0);
}

void SKGWebView::exportInFile(const QString& iFileName)
{
    QString extension = QFileInfo(iFileName).suffix().toUpper();
    if (extension == QStringLiteral("ODT")) {
#ifdef SKG_WEBENGINE
        page()->toHtml([ = ](const QString & result) {
            QTextDocument doc;
            QTextDocumentWriter docWriter(iFileName);
            doc.setHtml(result);
            docWriter.write(&doc);

            emit fileExporter(iFileName);
        });
#else
        QTextDocument doc;
        QTextDocumentWriter docWriter(iFileName);
        doc.setHtml(page()->mainFrame()->toHtml());
        docWriter.write(&doc);

        emit fileExporter(iFileName);
#endif
    } else if (extension == QStringLiteral("PDF")) {
#ifdef SKG_WEBENGINE
        page()->printToPdf(iFileName);
        connect(page(), &QWebEnginePage::pdfPrintingFinished, this, &SKGWebView::fileExporter);
#else
        QPrinter printer;
        printer.setOutputFileName(iFileName);
        print(&printer);

        emit fileExporter(iFileName);
#endif
    } else if (extension == QStringLiteral("HTML") || extension == QStringLiteral("HTM")) {

#ifdef SKG_WEBENGINE
        page()->toHtml([ = ](const QString & result) {
            QSaveFile file(iFileName);
            if (file.open(QIODevice::WriteOnly)) {
                QTextStream out(&file);
                out << result;

                // Close file
                file.commit();
                emit fileExporter(iFileName);
            }
        });
#else
        QSaveFile file(iFileName);
        if (file.open(QIODevice::WriteOnly)) {
            QTextStream out(&file);
            out << page()->mainFrame()->toHtml();

            // Close file
            file.commit();
            emit fileExporter(iFileName);
        }
#endif
    } else {
        QImage image(this->size(), QImage::Format_ARGB32);
        QPainter painter(&image);
        this->render(&painter);
        painter.end();
        image.save(iFileName);

        emit fileExporter(iFileName);
    }
}

void SKGWebView::onExport()
{
    _SKGTRACEINFUNC(10);
    QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), QStringLiteral("application/pdf text/html application/vnd.oasis.opendocument.text image/png image/jpeg image/gif image/tiff"), this);
    if (fileName.isEmpty()) {
        return;
    }

    exportInFile(fileName);
}

void SKGWebView::onPrintPreview()
{
    SKGTRACEINFUNC(10);
    QPointer<QPrintPreviewDialog> dialog = new QPrintPreviewDialog(this);
#ifdef SKG_WEBENGINE
    //TODO QWebEngine
    connect(dialog.data(), &QPrintPreviewDialog::paintRequested, page(), [&](QPrinter * printer) {
        page()->print(printer, [](bool) {});
    });
#else
    connect(dialog.data(), &QPrintPreviewDialog::paintRequested, this, &SKGWebView::print);
#endif

    dialog->exec();
}

void SKGWebView::onPrint()
{
    _SKGTRACEINFUNC(10);
    QPointer<QPrintDialog> dialog = new QPrintDialog(&m_printer, this);
    if (dialog->exec() == QDialog::Accepted) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#ifdef SKG_WEBENGINE
        page()->print(&m_printer, [](bool) {});
#else
        print(&m_printer);
#endif
        QApplication::restoreOverrideCursor();
    }
}
