/* ============================================================
 *
 * This file is a part of the rekonq project
 *
 * Copyright (C) 2007-2008 Trolltech ASA. All rights reserved
 * Copyright (C) 2008 by Andrea Diamantini <adjam7 at gmail dot com>
 *
 *
 * 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, 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.
 *
 * ============================================================ */

// Local Includes
#include "webview.h"
#include "webview.moc"

// Auto Includes
#include "rekonq.h"

// Local Includes
#include "application.h"
#include "mainwindow.h"
#include "mainview.h"
#include "cookiejar.h"
#include "networkaccessmanager.h"

// KDE Includes
#include <KStandardDirs>
#include <KDebug>

// Qt Includes
#include <QClipboard>
#include <QMenu>
#include <QMouseEvent>
#include <QWebHitTestResult>
#include <QBuffer>
// ---
#include <QUiLoader>

WebPage::WebPage(QObject *parent)
    : QWebPage(parent)
    , m_keyboardModifiers(Qt::NoModifier)
    , m_pressedButtons(Qt::NoButton)
    , m_openInNewTab(false)
{
    setNetworkAccessManager( Application::networkAccessManager() );
    connect(this, SIGNAL(unsupportedContent(QNetworkReply *)), this, SLOT(handleUnsupportedContent(QNetworkReply *)));
}


MainWindow *WebPage::mainWindow()
{
    QObject *w = this->parent();
    while (w) 
    {
        if (MainWindow *mw = qobject_cast<MainWindow*>(w))
            return mw;
        w = w->parent();
    }
    return Application::instance()->mainWindow();
}


bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
{
    // ctrl open in new tab and select
    // ctrl-alt open in new window
    if ( type == QWebPage::NavigationTypeLinkClicked && (m_keyboardModifiers & Qt::ControlModifier
            || m_pressedButtons == Qt::MidButton) ) 
    {
        WebView *webView = Application::instance()->newTab();
        webView->setFocus();
        webView->load(request);
        m_keyboardModifiers = Qt::NoModifier;
        m_pressedButtons = Qt::NoButton;
        return false;
    }
    if ( frame == mainFrame() ) 
    {
        m_loadingUrl = request.url();
        emit loadingUrl(m_loadingUrl);
    }
    return QWebPage::acceptNavigationRequest(frame, request, type);
}


QWebPage *WebPage::createWindow(QWebPage::WebWindowType type)
{
    Q_UNUSED(type);
    if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)
    {
        m_openInNewTab = true;
    }
    
    if (m_openInNewTab) 
    {
        m_openInNewTab = false;
        return mainWindow()->tabWidget()->newTab()->page();
    }

    MainWindow *mainWindow = Application::instance()->mainWindow();
    return mainWindow->currentTab()->page();
}


// QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList &paramNames, const QStringList &paramValues)
// {
//     kWarning() << "creating PLUGIN for rekonq ";
//     kWarning() << "classId = " << classId;
//     kWarning() << "url = " << url;
//     kWarning() << "Param Names = " << paramNames;
//     kWarning() << "Param Values = " << paramValues;
//     Q_UNUSED(url);
//     Q_UNUSED(paramNames);
//     Q_UNUSED(paramValues);
//     QUiLoader loader;
//     return loader.createWidget(classId, view());
// }


void WebPage::handleUnsupportedContent(QNetworkReply *reply)
{
    if (reply->error() == QNetworkReply::NoError) 
    {
        KUrl srcUrl = reply->url();
        QString path = ReKonfig::downloadDir() + QString("/") + srcUrl.fileName();
        KUrl destUrl = KUrl(path);
        Application::instance()->downloadUrl( srcUrl, destUrl );
        return;
    }

    QString myfilestr =  KStandardDirs::locate("data", "rekonq/htmls/notfound.html");
    QFile file( myfilestr );
    bool isOpened = file.open(QIODevice::ReadOnly);
    Q_ASSERT(isOpened);
    QString title = i18n("Error loading page: ") + reply->url().toString();
    QString html = QString(QLatin1String(file.readAll()))
                        .arg(title)
                        .arg(reply->errorString())
                        .arg(reply->url().toString());

    QBuffer imageBuffer;
    imageBuffer.open(QBuffer::ReadWrite);
    QIcon icon = view()->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, view());
    QPixmap pixmap = icon.pixmap(QSize(32,32));
    if (pixmap.save(&imageBuffer, "PNG")) 
    {
        html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"),
                     QString(QLatin1String(imageBuffer.buffer().toBase64())));
    }

    QList<QWebFrame*> frames;
    frames.append(mainFrame());
    while (!frames.isEmpty()) 
    {
        QWebFrame *frame = frames.takeFirst();
        if (frame->url() == reply->url()) 
        {
            frame->setHtml(html, reply->url());
            return;
        }
        QList<QWebFrame *> children = frame->childFrames();
        foreach(QWebFrame *frame, children)
            frames.append(frame);
    }
    if (m_loadingUrl == reply->url()) 
    {
        mainFrame()->setHtml(html, reply->url());
    }
}


// ------------------------------------------------------------------------------------------------------------------------------------------------------


WebView::WebView(QWidget* parent)
    : QWebView(parent)
    , m_progress(0)
    , m_page(new WebPage(this))
{
    setPage(m_page);
    connect(page(), SIGNAL(statusBarMessage(const QString&)), this, SLOT(setStatusBarText(const QString&)));
    connect(this, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int)));
    connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished()));
    connect(page(), SIGNAL(loadingUrl(const QUrl&)),  this, SIGNAL(urlChanged(const QUrl &)));
    connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(downloadRequested(const QNetworkRequest &)));
    page()->setForwardUnsupportedContent(true);
}


// TODO : improve and KDE-ize this menu
// 1. Add link to bookmarks
// 2. Add "save link as" action
void WebView::contextMenuEvent(QContextMenuEvent *event)
{
    QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos());
    if (!r.linkUrl().isEmpty()) 
    {
        KMenu menu(this);
        KAction *a = new KAction( KIcon("tab-new"), i18n("Open in New Tab"), this);
        connect( a, SIGNAL( triggered() ), this , SLOT( openLinkInNewTab() ) );
        menu.addAction(a);
        menu.addSeparator();
        menu.addAction( pageAction(QWebPage::DownloadLinkToDisk) );
        // Add link to bookmarks...
        menu.addSeparator();
        menu.addAction( pageAction(QWebPage::CopyLinkToClipboard) );
        if ( page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled) )
        {
            menu.addAction(pageAction(QWebPage::InspectElement));
        }
        menu.exec(mapToGlobal(event->pos()));
        return;
    }
    QWebView::contextMenuEvent(event);
}


void WebView::wheelEvent(QWheelEvent *event)
{
    if (QApplication::keyboardModifiers() & Qt::ControlModifier) 
    {
        int numDegrees = event->delta() / 8;
        int numSteps = numDegrees / 15;
        setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1);
        event->accept();
        return;
    }
    QWebView::wheelEvent(event);
}


void WebView::openLinkInNewTab()
{
    m_page->m_openInNewTab = true;
    pageAction(QWebPage::OpenLinkInNewWindow)->trigger();
}


void WebView::setProgress(int progress)
{
    m_progress = progress;
}


void WebView::loadFinished()
{
    if (m_progress != 100) 
    {
        kWarning() << "Recieved finished signal while progress is still:" << progress()
                   << "Url:" << url();
    }
    m_progress = 0;
}


void WebView::loadUrl(const KUrl &url)
{
    m_initialUrl = url;
    load(url);
}


QString WebView::lastStatusBarText() const
{
    return m_statusBarText;
}


KUrl WebView::url() const
{
    KUrl url = QWebView::url();
    if ( !url.isEmpty() )
    {
        return url;
    }
    return m_initialUrl;
}


void WebView::mousePressEvent(QMouseEvent *event)
{
    m_page->m_pressedButtons = event->buttons();
    m_page->m_keyboardModifiers = event->modifiers();
    QWebView::mousePressEvent(event);
}


void WebView::mouseReleaseEvent(QMouseEvent *event)
{
    QWebView::mouseReleaseEvent(event);
    if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) 
    {
        KUrl url( QApplication::clipboard()->text(QClipboard::Selection) );
        if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) 
        {
            setUrl(url);
        }
    }
}


void WebView::setStatusBarText(const QString &string)
{
    m_statusBarText = string;
}


void WebView::downloadRequested(const QNetworkRequest &request)
{
    KUrl srcUrl = request.url();
    QString path = ReKonfig::downloadDir() + QString("/") + srcUrl.fileName();
    KUrl destUrl = KUrl(path);
    Application::instance()->downloadUrl( srcUrl, destUrl );
}


void WebView::keyPressEvent(QKeyEvent *event)
{
    if ( (event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_Tab) )
    {
        emit ctrlTabPressed();
        return;
    }

    if( (event->modifiers() == Qt::ControlModifier + Qt::ShiftModifier) && (event->key() == Qt::Key_Backtab) )
    {
        emit shiftCtrlTabPressed();
        return;
    }

    QWebView::keyPressEvent( event );
}