/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebEngine module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qquickwebenginefaviconprovider_p_p.h"

#include "favicon_manager.h"
#include "qquickwebengineview_p.h"
#include "qquickwebengineview_p_p.h"
#include "web_contents_adapter.h"

#include <QtGui/QIcon>
#include <QtGui/QPixmap>

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QtGui/qiconengine.h>
#include <QtGui/private/qicon_p.h>
#endif

QT_BEGIN_NAMESPACE

using QtWebEngineCore::FaviconInfo;
using QtWebEngineCore::FaviconManager;

static inline unsigned area(const QSize &size)
{
    return size.width() * size.height();
}

QString QQuickWebEngineFaviconProvider::identifier()
{
    return QStringLiteral("favicon");
}

QUrl QQuickWebEngineFaviconProvider::faviconProviderUrl(const QUrl &url)
{
    if (url.isEmpty())
        return url;

    QUrl providerUrl;
    providerUrl.setScheme(QStringLiteral("image"));
    providerUrl.setHost(identifier());
    providerUrl.setPath(QStringLiteral("/%1").arg(url.toString(QUrl::RemoveQuery | QUrl::RemoveFragment)));
    if (url.hasQuery())
        providerUrl.setQuery(url.query(QUrl::FullyDecoded));
    if (url.hasFragment())
        providerUrl.setFragment(url.fragment(QUrl::FullyDecoded));

    return providerUrl;
}

QQuickWebEngineFaviconProvider::QQuickWebEngineFaviconProvider()
    : QQuickImageProvider(QQuickImageProvider::Pixmap)
    , m_latestView(0)
{
}

QQuickWebEngineFaviconProvider::~QQuickWebEngineFaviconProvider()
{
    qDeleteAll(m_iconUrlMap);
}

QUrl QQuickWebEngineFaviconProvider::attach(QQuickWebEngineView *view, const QUrl &iconUrl)
{
    if (iconUrl.isEmpty())
        return QUrl();

    m_latestView = view;

    if (!m_iconUrlMap.contains(view))
        m_iconUrlMap.insert(view, new QList<QUrl>());

    QList<QUrl> *iconUrls = m_iconUrlMap[view];
    if (!iconUrls->contains(iconUrl))
        iconUrls->append(iconUrl);

    return faviconProviderUrl(iconUrl);
}

void QQuickWebEngineFaviconProvider::detach(QQuickWebEngineView *view)
{
    QList<QUrl> *iconUrls = m_iconUrlMap.take(view);
    delete iconUrls;
}

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
static QPixmap getUnscaledPixmap(QIcon icon, const QSize &size)
{
    QPixmap pixmap = icon.data_ptr()->engine->pixmap(size, QIcon::Normal, QIcon::Off);
    pixmap.setDevicePixelRatio(1.0);
    return pixmap;
}
#else
static QPixmap getUnscaledPixmap(const QIcon &icon, const QSize &size)
{
    return icon.pixmap(size, 1.0);
}
#endif

QPixmap QQuickWebEngineFaviconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
    Q_UNUSED(size);
    Q_UNUSED(requestedSize);

    QUrl iconUrl(id);
    QQuickWebEngineView *view = viewForIconUrl(iconUrl);

    if (!view || iconUrl.isEmpty())
        return QPixmap();

    FaviconManager *faviconManager = view->d_ptr->adapter->faviconManager();

    Q_ASSERT(faviconManager);
    const FaviconInfo &faviconInfo = faviconManager->getFaviconInfo(iconUrl);
    const QIcon &icon = faviconManager->getIcon(faviconInfo.candidate ? QUrl() : iconUrl);

    Q_ASSERT(!icon.isNull());
    const QSize &bestSize = faviconInfo.size;

    // If source size is not specified, use the best quality
    if (!requestedSize.isValid()) {
        if (size)
            *size = bestSize;

        return getUnscaledPixmap(icon, bestSize).copy();
    }

    const QSize &fitSize = findFitSize(icon.availableSizes(), requestedSize, bestSize);
    const QPixmap &iconPixmap = getUnscaledPixmap(icon, fitSize);

    if (size)
        *size = iconPixmap.size();

    return iconPixmap.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation).copy();
}

QQuickWebEngineView *QQuickWebEngineFaviconProvider::viewForIconUrl(const QUrl &iconUrl) const
{
    // The most common use case is that the requested iconUrl belongs to the
    // latest WebEngineView which was raised an iconChanged signal.
    if (m_latestView) {
        QList<QUrl> *iconUrls = m_iconUrlMap[m_latestView];
        if (iconUrls && iconUrls->contains(iconUrl))
            return m_latestView;
    }

    for (auto it = m_iconUrlMap.cbegin(), end = m_iconUrlMap.cend(); it != end; ++it) {
        if (it.value()->contains(iconUrl))
            return it.key();
    }

    return 0;
}

QSize QQuickWebEngineFaviconProvider::findFitSize(const QList<QSize> &availableSizes,
                                                  const QSize &requestedSize,
                                                  const QSize &bestSize) const
{
    Q_ASSERT(availableSizes.count());
    if (availableSizes.count() == 1 || area(requestedSize) >= area(bestSize))
        return bestSize;

    QSize fitSize = bestSize;
    for (const QSize &size : availableSizes) {
        if (area(size) == area(requestedSize))
            return size;

        if (area(requestedSize) < area(size) && area(size) < area(fitSize))
            fitSize = size;
    }

    return fitSize;
}

QT_END_NAMESPACE
