/*
 * Cantata
 *
 * Copyright (c) 2011-2013 Craig Drummond <craig.p.drummond@gmail.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 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "coverwidget.h"
#include "covers.h"
#include "config.h"
#include "localize.h"
#include "icons.h"
#include <QPixmap>
#include <QPainter>
#include <QBuffer>
#include <QEvent>
#include <QResizeEvent>
#include <QFile>
#include <QTimer>
#include <QVariant>
#include <QCoreApplication>
#include <QFile>

static QString encode(const QImage &img)
{
    QByteArray bytes;
    QBuffer buffer(&bytes);
    buffer.open(QIODevice::WriteOnly);
    QImage copy(img);

    if (copy.size().width()>Covers::constMaxSize.width() || copy.size().height()>Covers::constMaxSize.height()) {
        copy=copy.scaled(Covers::constMaxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    copy.save(&buffer, "PNG");
    return QString("<img src=\"data:image/png;base64,%1\">").arg(QString(buffer.data().toBase64()));
}

#ifndef Q_OS_WIN
static void themes(const QString &theme, QStringList &iconThemes)
{
    if (iconThemes.contains(theme)) {
        return;
    }
    QString lower=theme.toLower();
    iconThemes << theme;
    if (lower!=theme) {
        iconThemes << lower;
    }
    QStringList paths=QIcon::themeSearchPaths();
    QString key("Inherits=");
    foreach (const QString &p, paths) {
        QString index(p+"/"+theme+"/index.theme");
        QFile f(index);
        if (f.open(QIODevice::ReadOnly|QIODevice::Text)) {
            while (!f.atEnd()) {
                QString line=f.readLine().trimmed().simplified();
                if (line.startsWith(key)) {
                    QStringList inherited=line.mid(key.length()).split(",", QString::SkipEmptyParts);
                    foreach (const QString &i, inherited) {
                        themes(i, iconThemes);
                    }
                    return;
                }
            }
        }
    }
}

void CoverWidget::initIconThemes()
{
    if (iconThemes.isEmpty()) {
        themes(Icon::currentTheme(), iconThemes);
    }
}

QString CoverWidget::findIcon(const QStringList &names)
{
    initIconThemes();
    QList<int> sizes=QList<int>() << 256 << 128 << 64 << 48 << 32 << 24 << 22;
    QStringList paths=QIcon::themeSearchPaths();
    QStringList sections=QStringList() << "categories" << "devices";
    foreach (const QString &p, paths) {;
        foreach (const QString &n, names) {
            foreach (const QString &theme, iconThemes) {
                QMap<int, QString> files;
                foreach (int s, sizes) {
                    foreach (const QString &sect, sections) {
                        QString f(p+"/"+theme+"/"+QString::number(s)+"x"+QString::number(s)+"/"+sect+"/"+n+".png");
                        if (QFile::exists(f)) {
                            files.insert(s, f);
                            break;
                        }
                        f=QString(p+"/"+theme+"/"+QString::number(s)+"x"+QString::number(s)+"/"+sect+"/"+n+".svg");
                        if (QFile::exists(f)) {
                            files.insert(s, f);
                            break;
                        }
                        f=QString(p+"/"+theme+"/"+sect+"/"+QString::number(s)+"/"+n+".svg");
                        if (QFile::exists(f)) {
                            files.insert(s, f);
                            break;
                        }
                    }
                }

                if (!files.isEmpty()) {
                    foreach (int s, sizes) {
                        if (files.contains(s)) {
                            return files[s];
                        }
                    }
                }
            }
        }
    }
    return QString();
}
#endif

const int CoverWidget::constBorder=1;

CoverWidget::CoverWidget(QWidget *parent)
    : QLabel(parent)
    , empty(true)
    , valid(false)
    , pressed(false)
{
    connect(Covers::self(), SIGNAL(cover(const Song &, const QImage &, const QString &)), SLOT(coverRetrieved(const Song &, const QImage &, const QString &)));
    connect(Covers::self(), SIGNAL(coverUpdated(const Song &, const QImage &, const QString &)), SLOT(coverRetrieved(const Song &, const QImage &, const QString &)));
    installEventFilter(this);
    QTimer::singleShot(0, this, SLOT(init())); // Need to do this after constructed, so that size is set....
    setStyleSheet(QString("QLabel {border: %1px solid transparent} QToolTip {background-color:#111111; color: #DDDDDD}").arg(constBorder));
}

CoverWidget::~CoverWidget()
{
}

const QPixmap & CoverWidget::stdPixmap(bool stream)
{
    QPixmap &pix=stream ? noStreamCover : noCover;

    if (pix.isNull()) {
        QSize s=size()-QSize(constBorder*2, constBorder*2);
        int iconSize=s.width()<=128 ? 128 : 256;
        pix = (stream ? Icons::self()->streamIcon : Icons::self()->albumIcon).pixmap(iconSize, iconSize).scaled(s, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

        #ifndef Q_OS_WIN
        QString &file=stream ? noStreamCoverFileName : noCoverFileName;
        if (stream &file.isEmpty()) {
            QString iconFile=QString(INSTALL_PREFIX"/share/")+QCoreApplication::applicationName()+"/icons/stream.png";
            if (QFile::exists(iconFile)) {
                file=iconFile;
            }
        }
        if (file.isEmpty()) {
            file=findIcon(stream ? QStringList() << "applications-internet" : QStringList() << "media-optical" << "media-optical-audio");
        }
        #endif
    }
    return pix;
}

void CoverWidget::update(const QImage &i)
{
    setPixmap(QPixmap::fromImage(i).scaled(size()-QSize(constBorder*2, constBorder*2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    img=i;
    empty=false;
}

void CoverWidget::update(const QPixmap &p)
{
    QSize pixSize=size()-QSize(constBorder*2, constBorder*2);
    setPixmap(p.size()==pixSize ? p : p.scaled(pixSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    img=p.toImage();
    empty=true;
}

void CoverWidget::update(const Song &s)
{
    if (s.albumArtist()!=current.albumArtist() || s.album!=current.album || s.isStream()!=current.isStream()) {
        current=s;
        bool isStream=current.isStream() && !current.isCantataStream() && !current.isCdda();
        if (!s.albumArtist().isEmpty() && !s.album.isEmpty() && !isStream) {
            Covers::Image cImg=Covers::self()->requestImage(s);
            valid=!cImg.img.isNull();
            if (valid) {
                update(cImg.img);
                coverFileName=cImg.fileName;
                emit coverImage(cImg.img);
                emit coverFile(cImg.fileName);
                emit albumCover(cImg.img);
            } else {
                // We ned to set the image here, so that TrayItem gets the correct 'noCover' image
                // ...but if Covers does eventually download a cover, we dont want valid->noCover->valid
                img=stdPixmap(false).toImage();
            }
        } else {
            valid=true;
            update(stdPixmap(isStream));
            coverFileName=isStream ? noStreamCoverFileName : noCoverFileName;
            img=isStream ? noStreamCover.toImage() : noCover.toImage();
            emit coverImage(img);
            emit coverFile(coverFileName);
            emit albumCover(QImage());
        }
    }
}

void CoverWidget::init()
{
    update(stdPixmap(false));
}

void CoverWidget::coverRetrieved(const Song &s, const QImage &img, const QString &file)
{
    if (!s.isArtistImageRequest() && s.albumArtist()==current.albumArtist() && s.album==current.album) {
        valid=!img.isNull();
        if (valid) {
            update(img);
            coverFileName=file;
            emit coverImage(img);
            emit coverFile(file);
            emit albumCover(img);
        } else {
            bool stream=current.isStream() && !current.isCdda();
            update(stdPixmap(stream));
            coverFileName=stream ? noStreamCoverFileName : noCoverFileName;
            emit coverImage(QImage());
            emit coverFile(coverFileName);
            emit albumCover(QImage());
        }
    }
}

bool CoverWidget::eventFilter(QObject *object, QEvent *event)
{
    switch(event->type()) {
    case QEvent::ToolTip: {
        if (current.isEmpty() || (current.isStream() && !current.isCantataStream() && !current.isCdda())) {
            setToolTip(QString());
            break;
        }
        QString toolTip=QLatin1String("<table>");

        toolTip+=i18n("<tr><td align=\"right\"><b>Artist:</b></td><td>%1</td></tr>"
                      "<tr><td align=\"right\"><b>Album:</b></td><td>%2</td></tr>"
                      "<tr><td align=\"right\"><b>Year:</b></td><td>%3</td></tr>", current.artist, current.album, current.year);
        toolTip+="</table>";
        if (!img.isNull()) {
            if (img.size().width()>Covers::constMaxSize.width() || img.size().height()>Covers::constMaxSize.height() ||
                coverFileName.isEmpty() || !QFile::exists(coverFileName)) {
                toolTip+=QString("<br/>%1").arg(encode(img));
            } else {
                toolTip+=QString("<br/><img src=\"%1\"/>").arg(coverFileName);
            }
        }
        setToolTip(toolTip);
        break;
    }
    case QEvent::MouseButtonPress:
        if (Qt::LeftButton==static_cast<QMouseEvent *>(event)->button()) {
            pressed=true;
        }
        break;
    case QEvent::MouseButtonRelease:
        if (pressed && Qt::LeftButton==static_cast<QMouseEvent *>(event)->button()) {
            emit clicked();
        }
        pressed=false;
        break;
    default:
        break;
    }
    return QObject::eventFilter(object, event);
}

void CoverWidget::resizeEvent(QResizeEvent *e)
{
    int sz=qMax(e->size().width(), e->size().height());
    resize(sz, sz);
}

// This commented out section draws a rounded cover - but all other covers are square, so not
// sure if I want this or not...
// static QPainterPath buildPath(const QRectF &r, double radius)
// {
//     QPainterPath path;
//     double diameter(radius*2);
//
//     path.moveTo(r.x()+r.width(), r.y()+r.height()-radius);
//     path.arcTo(r.x()+r.width()-diameter, r.y(), diameter, diameter, 0, 90);
//     path.arcTo(r.x(), r.y(), diameter, diameter, 90, 90);
//     path.arcTo(r.x(), r.y()+r.height()-diameter, diameter, diameter, 180, 90);
//     path.arcTo(r.x()+r.width()-diameter, r.y()+r.height()-diameter, diameter, diameter, 270, 90);
//     return path;
// }

// void CoverWidget::paintEvent(QPaintEvent *e)
// {
//     QLabel::paintEvent(e);
//     QPainter painter(this);
//     painter.setRenderHint(QPainter::Antialiasing);
//     painter.setBrushOrigin(QPoint(constBorder, constBorder));
//     painter.fillPath(buildPath(QRectF(0.5, 0.5, bgnd.width()-1, bgnd.height()-1), width()>128 ? 5.0 : 4.0), bgnd);
// }
