/***************************** LICENSE START ***********************************

 Copyright 2013 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQIconProvider.h"

#include <QDebug>
#include <QImage>
#include <QImageReader>
#include <QPainter>

#include "Folder.h"
#include "IconClass.h"
#include "IconObject.h"
#include "MvQTheme.h"

#include <algorithm>

// This array defines the avaiable icon sizes
static int sizeArr[] = {12, 16, 24, 32, 48, 64, 96};

static MvQUnknownIcon unknownIcon(":/desktop/unknown.svg");
static MvQIcon linkIcon(":/desktop/link.svg");
static MvQIcon linkBrokenIcon(":/desktop/link_broken.svg");
static MvQIcon lockIcon(":/desktop/padlock.svg");
static MvQIcon warningIcon(":/desktop/warning.svg");
static MvQIcon bookmarkGroupIcon(":/desktop/bookmark_group.svg");
static MvQIcon embeddedIcon(":/desktop/embedded.svg");
static MvQIcon homeIcon(":/desktop/home.svg");

int MvQIconProvider::defaultSize_ = 32;
std::map<std::string, MvQIcon*> MvQIconProvider::icons_;
std::map<std::string, Folder*> MvQIconProvider::folders_;
std::vector<int> MvQIconProvider::sizes_(sizeArr, sizeArr + sizeof(sizeArr) / sizeof(sizeArr[0]));

//===========================================
//
// MvQIcon
//
//===========================================

MvQIcon::MvQIcon(QString path) :
    path_(path)
{
}

QPixmap MvQIcon::pixmap(int size)
{
    auto it = pixmaps_.find(size);
    if (it != pixmaps_.end())
        return it->second;
    else {
        QPixmap pix;
        QImageReader imgR(path_);
        if (imgR.canRead()) {
            imgR.setScaledSize(QSize(size, size));
            QImage img = imgR.read();
            pix = QPixmap::fromImage(img);
        }
        else {
            pix = unknownIcon.pixmap(size);
        }

        pixmaps_[size] = pix;
        return pix;
    }
    return {};
}

QPixmap MvQIcon::pixmap(IconObject* obj, int size, bool checkEmbedded)
{
    QPixmap pix = pixmap(size);

    if (obj->temporary()) {
        QImage img = pix.toImage();
        img.invertPixels();
        return QPixmap::fromImage(img);
    }

    if (obj->isLink()) {
        QImage img = pix.toImage();
        QPainter p(&img);
        int subSize = (size <= 32) ? (size / 2) : static_cast<int>((static_cast<float>(size * 0.4)));

        if (obj->isBrokenLink()) {
            greyOut(img);
            QRect r(0, 0, subSize, subSize);
            p.drawPixmap(QPoint(0, size - subSize),
                         linkBrokenIcon.pixmap(subSize), r);
        }
        else {
            QRect r(0, 0, subSize, subSize);
            p.drawPixmap(QPoint(0, size - subSize),
                         linkIcon.pixmap(subSize), r);
        }

        pix = QPixmap::fromImage(img);
    }
    if (obj->locked()) {
        QImage img = pix.toImage();
        QPainter p(&img);
        int subSize = (size <= 32) ? (size / 2) : static_cast<int>((static_cast<float>(size * 0.4)));

        QRect r(0, 0, subSize, subSize);
        p.drawPixmap(QPoint(size - subSize, size - subSize),
                     lockIcon.pixmap(subSize), r);

        pix = QPixmap::fromImage(img);
    }
    if (checkEmbedded && obj->isEmbedded()) {
        int subSize = size / 2;
        QPixmap emb(size + subSize / 2, size);
        emb.fill(Qt::transparent);
        QPainter p(&emb);

        QRect r(0, 0, size, size);
        p.drawPixmap(QPoint(0, 0), pix, r);
        r = QRect(0, 0, subSize, subSize);
        p.drawPixmap(QPoint(size - subSize / 2, 0),
                     embeddedIcon.pixmap(subSize), r);

        pix = emb;
    }

    return pix;
}


QPixmap MvQIcon::greyedOutPixmap(IconObject* obj, int size)
{
    QPixmap pix = pixmap(obj, size, false);
    QImage img = pix.toImage();
    greyOut(img);
    return QPixmap::fromImage(img);
}

void MvQIcon::greyOut(QImage& img)
{
    int w = img.width();
    int h = img.height();
    QRgb c = 0;
    int g = 0;
    int gBase = MvQTheme::colour("folderview", "faded_base").red();
    float gFactor = (255. - gBase) / 255.;

    for (int i = 0; i < w; i++)
        for (int j = 0; j < h; j++) {
            c = img.pixel(i, j);
            if (qAlpha(c) != 0) {
                // g=qGray(c);
                g = (qRed(c) + qGreen(c) + qBlue(c)) / 3;
                g = gBase + gFactor * static_cast<float>(g);
                img.setPixel(i, j, qRgb(g, g, g));
            }
        }
}

MvQUnknownIcon::MvQUnknownIcon(QString n) :
    MvQIcon(n)
{
}

// We must serve back a valid pixmap all the time!!! The MvQIcon pixmap() method
// calls this a fallback!!!
QPixmap MvQUnknownIcon::pixmap(int size)
{
    auto it = pixmaps_.find(size);
    if (it != pixmaps_.end())
        return it->second;
    else {
        QImageReader imgR(path_);
        if (imgR.canRead()) {
            return MvQIcon::pixmap(size);
        }
        else {
            QPixmap pix(size, size);
            pix.fill(QColor(210, 210, 210));

            QPainter painter(&pix);
            painter.setPen(Qt::black);
            QRect r(0, 0, size, size);
            QFont f;
            float reqFontSize = 4 + 72.0 * 0.75 * static_cast<float>(size) / 96.;  // estimate with 96 dpi
            f.setPointSize(reqFontSize);
            QFontMetrics fm(f);
            while (fm.height() > 3 * size / 4 && f.pointSize() > 8) {
                f.setPointSize(f.pointSize() - 1);
                fm = QFontMetrics(f);
            }
            painter.setFont(f);
            painter.drawText(r, Qt::AlignCenter, "?");
            pixmaps_[size] = pix;

            return pix;
        }
    }

    return {size, size};
}

//===========================================
//
// MvQIconProvider
//
//===========================================

MvQIconProvider::MvQIconProvider() = default;

MvQIcon* MvQIconProvider::icon(const IconClass& kind)
{
    auto it = icons_.find(kind.name());
    if (it != icons_.end())
        return it->second;

    auto* p = new MvQIcon(MvQTheme::icIconFile(kind.pixmap()));
    icons_[kind.name()] = p;
    return p;
}

MvQIcon* MvQIconProvider::icon(const std::string& kind)
{
    auto it = icons_.find(kind);
    if (it != icons_.end())
        return it->second;

    if (IconClass::isValid(kind)) {
        const IconClass& ic = IconClass::find(kind);
        auto* p = new MvQIcon(MvQTheme::icIconFile(ic.pixmap()));
        icons_[kind] = p;
        return p;
    }
    return &unknownIcon;
}

QPixmap MvQIconProvider::pixmap(const std::string& type, int size)
{
    return icon(type)->pixmap(size);
}

QPixmap MvQIconProvider::pixmap(const IconClass& kind, int size)
{
    return icon(kind)->pixmap(size);
}

QPixmap MvQIconProvider::pixmap(IconObject* obj, int size, bool checkEmbedded)
{
    return (obj) ? icon(obj->iconClass())->pixmap(obj, size, checkEmbedded) : unknownIcon.pixmap(size);
}

QPixmap MvQIconProvider::greyedOutPixmap(IconObject* obj, int size)
{
    return (obj) ? icon(obj->iconClass())->greyedOutPixmap(obj, size) : unknownIcon.pixmap(size);
}

QPixmap MvQIconProvider::bookmarkPixmap(std::string fullName, int size)
{
    if (fullName == "FOLDERGROUP")
        return bookmarkGroupIcon.pixmap(size);

    auto it = folders_.find(fullName);
    if (it != folders_.end()) {
        if (it->second)
            return pixmap(it->second, size);
        else {
            folders_.erase(it);
            return warningIcon.pixmap(size);
        }
    }
    else {
        Folder* folder = Folder::folder(fullName, false);
        folders_[fullName] = folder;
        if (folder)
            return pixmap(folder, size);
        else
            return warningIcon.pixmap(size);
    }

    return warningIcon.pixmap(size);
}

QPixmap MvQIconProvider::lockPixmap(int size)
{
    return lockIcon.pixmap(size);
}

QPixmap MvQIconProvider::warningPixmap(int size)
{
    return warningIcon.pixmap(size);
}

QPixmap MvQIconProvider::homePixmap(int size)
{
    return homeIcon.pixmap(size);
}

static MvQIconProvider iconProvider;
