/*
 * Copyright (C) 2006 George Staikos <staikos@kde.org>
 * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
 * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <qwebpage.h>
#include <qwebview.h>
#include <qwebframe.h>
#include <qwebsettings.h>

#include <QtGui>
#include <QDebug>



class InfoWidget :public QProgressBar {
    Q_OBJECT
public:
    InfoWidget(QWidget *parent)
        : QProgressBar(parent), m_progress(0)
    {
        setMinimum(0);
        setMaximum(100);
    }
    QSize sizeHint() const
    {
        QSize size(100, 20);
        return size;
    }
public slots:
    void startLoad()
    {
        setValue(m_progress);
        show();
    }
    void changeLoad(int change)
    {
        m_progress = change;
        setValue(change);
    }
    void endLoad()
    {
        QTimer::singleShot(1000, this, SLOT(hide()));
        m_progress = 0;
    }

protected:
    int m_progress;
};

class HoverLabel : public QWidget {
    Q_OBJECT
public:
    HoverLabel(QWidget *parent=0)
        : QWidget(parent),
          m_animating(false),
          m_percent(0)
    {
        m_timer.setInterval(1000/30);
        m_hideTimer.setInterval(500);
        m_hideTimer.setSingleShot(true);
        connect(&m_timer, SIGNAL(timeout()),
                this, SLOT(update()));
        connect(&m_hideTimer, SIGNAL(timeout()),
                this, SLOT(hide()));
    }

public slots:
    void setHoverLink(const QString &link) {
        m_link = link;
        if (m_link.isEmpty()) {
            m_hideTimer.start();
        } else {
            m_hideTimer.stop();
            m_oldSize = m_newSize;
            m_newSize = sizeForFont();
            resetAnimation();
            updateSize();
            show();
            repaint();
        }
    }
    QSize sizeForFont() const {
        QFont f = font();
        QFontMetrics fm(f);
        return QSize(fm.width(m_link) + 10, fm.height() + 6);
    }
    QSize sizeHint() const {
        if (!m_animating)
            return sizeForFont();
        else
            return (m_newSize.width() > m_oldSize.width()) ? m_newSize : m_oldSize;
    }
    void updateSize() {
        QRect r = geometry();
        QSize newSize = sizeHint();
        r = QRect(r.x(), r.y(), newSize.width(), newSize.height());
        setGeometry(r);
    }
    void resetAnimation() {
        m_animating = true;
        m_percent = 0;
        if (!m_timer.isActive())
            m_timer.start();
    }
protected:
    void paintEvent(QPaintEvent *e) {
        QPainter p(this);
        p.setClipRect(e->rect());
        p.setPen(QPen(Qt::black, 1));
        QLinearGradient gradient(rect().topLeft(), rect().bottomLeft());
        gradient.setColorAt(0, QColor(255, 255, 255, 220));
        gradient.setColorAt(1, QColor(193, 193, 193, 220));
        p.setBrush(QBrush(gradient));
        QSize size;
        {
            //draw a nicely rounded corner rectangle. to avoid unwanted
            // borders we move the coordinates outsize the our clip region
            size = interpolate(m_oldSize, m_newSize, m_percent);
            QRect r(-1, 0, size.width(), size.height()+2);
            const int roundness = 20;
            QPainterPath path;
            path.moveTo(r.x(), r.y());
            path.lineTo(r.topRight().x()-roundness, r.topRight().y());
            path.cubicTo(r.topRight().x(), r.topRight().y(),
                         r.topRight().x(), r.topRight().y(),
                         r.topRight().x(), r.topRight().y() + roundness);
            path.lineTo(r.bottomRight());
            path.lineTo(r.bottomLeft());
            path.closeSubpath();
            p.setRenderHint(QPainter::Antialiasing);
            p.drawPath(path);
        }
        if (m_animating) {
            if (qFuzzyCompare(m_percent, 1)) {
                m_animating = false;
                m_percent = 0;
                m_timer.stop();
            } else {
                m_percent += 0.1;
                if (m_percent >= 0.99) {
                    m_percent = 1;
                }
            }
        }

        QString txt;
        QFontMetrics fm(fontMetrics());
        txt = fm.elidedText(m_link, Qt::ElideRight, size.width()-5);
        p.drawText(5, height()-6, txt);
    }

private:
    QSize interpolate(const QSize &src, const QSize &dst, qreal percent) {
        int widthDiff  = int((dst.width() - src.width())  * percent);
        int heightDiff = int((dst.height() - src.height()) * percent);
        return QSize(src.width()  + widthDiff,
                     src.height() + heightDiff);
    }
    QString m_link;
    bool    m_animating;
    QTimer  m_timer;
    QTimer  m_hideTimer;
    QSize   m_oldSize;
    QSize   m_newSize;
    qreal   m_percent;
};

class SearchEdit;

class ClearButton : public QPushButton {
    Q_OBJECT
public:
    ClearButton(QWidget *w)
        : QPushButton(w)
    {
        setMinimumSize(24, 24);
        setFixedSize(24, 24);
        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    }
    void paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event);
        QPainter painter(this);
        int height = parentWidget()->geometry().height();
        int width = height; //parentWidget()->geometry().width();

        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.setPen(Qt::lightGray);
        painter.setBrush(isDown() ?
                         QColor(140, 140, 190) :
                         underMouse() ? QColor(220, 220, 255) : QColor(200, 200, 230)
            );
        painter.drawEllipse(4, 4, width - 8, height - 8);
        painter.setPen(Qt::white);
        int border = 8;
        painter.drawLine(border, border, width - border, height - border);
        painter.drawLine(border, height - border, width - border, border);
    }
};

class SearchEdit : public QLineEdit
{
    Q_OBJECT
public:
    SearchEdit(const QString &str, QWidget *parent = 0)
        : QLineEdit(str, parent)
    {
        setMinimumSize(QSize(400, 24));
        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

        setStyleSheet(":enabled { padding-right: 27px }");
        clearButton = new ClearButton(this);
        clearButton->setGeometry(QRect(geometry().right() - 27,
                                       geometry().top() - 2,
                                       geometry().right(), geometry().bottom()));
        clearButton->setVisible(true);
#ifndef QT_NO_CURSOR
        clearButton->setCursor(Qt::ArrowCursor);
#endif
#ifndef QT_NO_TOOLTIP
        clearButton->setToolTip("Clear");
#endif
        connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
    }
    ~SearchEdit() { }
protected:
    virtual void paintEvent(QPaintEvent *e) {
        QLineEdit::paintEvent(e);
        if(text().isEmpty())
            clearButton->setVisible(false);
        else
            clearButton->setVisible(true);
    }
    virtual void resizeEvent(QResizeEvent *) {
        clearButton->setParent(this);
        clearButton->setGeometry(QRect(width()-27,
                                       0,
                                       24, 24));
    }
    virtual void moveEvent(QMoveEvent *) {
        clearButton->setParent(this);
        clearButton->setGeometry(QRect(width()-27, 1,
                                       24, 24));
    }

    QPushButton *clearButton;
};

class WebPage : public QWebPage
{
public:
    inline WebPage(QWidget *parent) : QWebPage(parent) {}

    virtual QWebPage *createWindow();
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(const QUrl &url = QUrl())
    {
        view = new QWebView(this);
        view->setPage(new WebPage(view));
        InfoWidget *info = new InfoWidget(view);
        info->setGeometry(20, 20, info->sizeHint().width(),
                          info->sizeHint().height());
        connect(view, SIGNAL(loadStarted()),
                info, SLOT(startLoad()));
        connect(view, SIGNAL(loadProgressChanged(int)),
                info, SLOT(changeLoad(int)));
        connect(view, SIGNAL(loadFinished()),
                info, SLOT(endLoad()));
        connect(view, SIGNAL(loadFinished()),
                this, SLOT(loadFinished()));
        connect(view, SIGNAL(titleChanged(const QString&)),
                this, SLOT(setWindowTitle(const QString&)));
        connect(view->page(), SIGNAL(hoveringOverLink(const QString&, const QString&)),
                this, SLOT(showLinkHover(const QString&, const QString&)));


        setCentralWidget(view);

        QToolBar *bar = addToolBar("Navigation");
        urlEdit = new SearchEdit(url.toString());
        urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy());
        connect(urlEdit, SIGNAL(returnPressed()),
                SLOT(changeLocation()));
        bar->addAction(view->action(QWebPage::GoBack));
        bar->addAction(view->action(QWebPage::Stop));
        bar->addAction(view->action(QWebPage::GoForward));
        bar->addSeparator();
        bar->addAction(view->action(QWebPage::Cut));
        bar->addAction(view->action(QWebPage::Copy));
        bar->addAction(view->action(QWebPage::Paste));
        bar->addSeparator();
        bar->addAction(view->action(QWebPage::Undo));
        bar->addAction(view->action(QWebPage::Redo));

        addToolBarBreak();
        bar = addToolBar("Location");
        bar->addWidget(new QLabel(tr("Location:")));
        bar->addWidget(urlEdit);

        hoverLabel = new HoverLabel(this);
        hoverLabel->hide();

        if (url.isValid())
            view->load(url);

        info->raise();
    }
    inline QWebPage *webPage() const { return view->page(); }
protected slots:
    void changeLocation()
    {
        QUrl url(urlEdit->text());
        view->load(url);
    }
    void loadFinished()
    {
        urlEdit->setText(view->url().toString());
    }
    void showLinkHover(const QString &link, const QString &toolTip)
    {
        //statusBar()->showMessage(link);
        hoverLabel->setHoverLink(link);
#ifndef QT_NO_TOOLTIP
        if (!toolTip.isEmpty())
            QToolTip::showText(QCursor::pos(), toolTip);
#endif
    }
protected:
    void resizeEvent(QResizeEvent *) {
        QSize hoverSize = hoverLabel->sizeHint();
        hoverLabel->setGeometry(0, height()-hoverSize.height(),
                                300, hoverSize.height());
    }
private:
    QWebView *view;
    QLineEdit *urlEdit;
    HoverLabel *hoverLabel;
};

QWebPage *WebPage::createWindow()
{
    MainWindow *mw = new MainWindow;
    return mw->webPage();
}

#include "main.moc"

int main(int argc, char **argv)
{
    QString url = QString("%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html"));
    QApplication app(argc, argv);

    QWebSettings::defaultSettings()->setAttribute(QWebSettings::PluginsEnabled);
    QWebSettings::defaultSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled);

    const QStringList args = app.arguments();
    if (args.count() > 1)
        url = args.at(1);

    QUrl qurl(url);
    if (qurl.scheme().isEmpty())
        qurl = QUrl::fromLocalFile(QFileInfo(url).absoluteFilePath());
    MainWindow window(qurl);
    window.show();

    return app.exec();
}
