/*
 * Cantata
 *
 * Copyright (c) 2011-2014 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 "coverdialog.h"
#include "support/messagebox.h"
#include "support/localize.h"
#include "widgets/listview.h"
#include "network/networkaccessmanager.h"
#include "settings.h"
#include "mpd/mpdconnection.h"
#include "support/utils.h"
#include "support/spinner.h"
#include "widgets/messageoverlay.h"
#include "support/icon.h"
#include "widgets/icons.h"
#include "config.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QPixmap>
#include <QFont>
#include <QFontMetrics>
#include <QPainter>
#include <QStyle>
#include <QApplication>
#include <QProgressBar>
#include <QScrollArea>
#include <QDesktopWidget>
#include <QWheelEvent>
#include <QScrollBar>
#include <QMenu>
#include <QAction>
#ifdef ENABLE_KDE_SUPPORT
#include <KDE/KFileDialog>
#else
#include <QFileDialog>
#endif
#include <QFileInfo>
#include <QUrl>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#include <QMimeData>
#include <QJsonDocument>
#else
#include "qjson/parser.h"
#endif
#include <QTemporaryFile>
#include <QDir>
#include <QXmlStreamReader>
#include <QDomDocument>
#include <QDomElement>
#include <QSslSocket>

#include <QDebug>
#define DBUG if (Covers::debugEnabled()) qWarning() << "CoverDialog" << __FUNCTION__

static int iCount=0;
static const int constMaxTempFiles=20;

static QImage cropImage(QImage img, bool isArtist)
{
    if (isArtist && img.width()!=img.height()) {
        int size=qMin(img.width(), img.height());
        return img.copy((img.width()-size)/2, 0, size, size);
    }
    return img;
}

int CoverDialog::instanceCount()
{
    return iCount;
}

// Only really want square-ish covers!
bool canUse(int w, int h)
{
    return w>90 && h>90 && (w==h || (h<=(w*1.1) && w<=(h*1.1)));
}

enum Providers {
    Prov_LastFm   = 0x0001,
    Prov_Google   = 0x0002,
    Prov_DiscoGs  = 0x0004,
    Prov_CoverArt = 0x0008,
    Prov_Deezer   = 0x0010,
    Prov_Spotify  = 0x0020,
    Prov_ITunes   = 0x0040,

    Prov_All      = 0x007F
};

class CoverItem : public QListWidgetItem
{
public:
    CoverItem(const QString &u, const QString &tu, const QImage &img, const QString &text, QListWidget *parent, int w=-1, int h=-1, int sz=-1)
        : QListWidgetItem(parent)
        , imgUrl(u)
        , thmbUrl(tu)
        , list(parent) {
        setSizeHint(parent->gridSize());
        setTextAlignment(Qt::AlignHCenter | Qt::AlignTop);
        setToolTip(u);
        if (!img.isNull()) {
            setImage(img);
        }
        if (sz>0) {
            setText(i18nc("name\nwidth x height (file size)", "%1\n%2 x %3 (%4)", text, w, h, Utils::formatByteSize(sz)));
        } else if (w>0 && h>0) {
            setText(i18nc("name\nwidth x height", "%1\n%2 x %3", text, w, h));
        } else {
            setText(text);
        }
    }
    const QString & url() const { return imgUrl; }
    const QString & thumbUrl() const { return thmbUrl; }
    virtual bool isLocal() const { return false; }
    virtual bool isExisting() const { return false; }

protected:
    void setImage(const QImage &img) {
        int size=list && list->parentWidget() && qobject_cast<CoverDialog *>(list->parentWidget())
                    ? static_cast<CoverDialog *>(list->parentWidget())->imageSize() : 100;
        QPixmap pix=QPixmap::fromImage(img.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation));
        if (pix.width()<size || pix.height()<size) {
            QPixmap newPix(size, size);
            newPix.fill(Qt::transparent);
            QPainter p(&newPix);
            p.drawPixmap((size-pix.width())/2, (size-pix.height())/2, pix.width(), pix.height(), pix);
            p.end();
            pix=newPix;
        }
        setIcon(pix);
    }

protected:
    QString imgUrl;
    QString thmbUrl;
    QListWidget *list;
};

class ExistingCover : public CoverItem
{
public:
    ExistingCover(const Covers::Image &i, QListWidget *parent)
        : CoverItem(i.fileName, QString(), i.img, i18n("Current Cover"), parent, i.img.width(), i.img.height(), QFileInfo(i.fileName).size())
        , img(i.img) {
        QFont f(font());
        f.setBold((true));
        setFont(f);
    }
    bool isExisting() const { return true; }
    const QImage & image() const { return img; }
private:
    QImage img;
};

class LocalCover : public CoverItem
{
public:
    LocalCover(const QString &u, const QImage &i, QListWidget *parent)
        : CoverItem(u, QString(), i, Utils::getFile(u), parent, i.width(), i.height(), QFileInfo(u).size())
        , img(i) { }
    bool isLocal() const { return true; }
    const QImage & image() const { return img; }
private:
    QImage img;
};

class LastFmCover : public CoverItem
{
public:
    LastFmCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("Last.fm"), parent) { }
};

class GoogleCover : public CoverItem
{
public:
    GoogleCover(const QString &u, const QString &tu, const QImage &img, int w, int h, int size, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("Google"), parent, w, h, size*1024) { }
};

class DiscogsCover : public CoverItem
{
public:
    DiscogsCover(const QString &u, const QString &tu, const QImage &img, int w, int h, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("Discogs"), parent, w, h) { }
};

class CoverArtArchiveCover : public CoverItem
{
public:
    CoverArtArchiveCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent)
        : CoverItem(u, tu, img, i18n("CoverArt Archive"), parent)  { }
};

class DeezerCover : public CoverItem
{
public:
    DeezerCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("Deezer"), parent)  { }
};

class SpotifyCover : public CoverItem
{
public:
    SpotifyCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("Spotify"), parent, 640, 640)  { }
};

class ITunesCover : public CoverItem
{
public:
    ITunesCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent)
        : CoverItem(u, tu, img, QLatin1String("iTunes"), parent, 600, 600)  { }
};

CoverPreview::CoverPreview(QWidget *p)
    : Dialog(p)
    , zoom(1.0)
    , imgW(0),
      imgH(0)
{
    setButtons(Close);
    setWindowTitle(i18n("Image"));
    QWidget *mw=new QWidget(this);
    QVBoxLayout *layout=new QVBoxLayout(mw);
    loadingLabel=new QLabel(i18n("Downloading..."), mw);
    pbar=new QProgressBar(mw);
    imageLabel=new QLabel;
    pbar->setRange(0, 100);
    imageLabel->setBackgroundRole(QPalette::Base);
    imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    imageLabel->setScaledContents(true);
    scrollArea = new QScrollArea(mw);
    scrollArea->setBackgroundRole(QPalette::Dark);
    scrollArea->setWidget(imageLabel);

    layout->addWidget(loadingLabel);
    layout->addWidget(pbar);
    layout->addWidget(scrollArea);
    layout->setMargin(0);
    setMainWidget(mw);
}

void CoverPreview::showImage(const QImage &img, const QString &u)
{
    if (u==url) {
        zoom=1.0;
        url=u;
        loadingLabel->hide();
        pbar->hide();
        imageLabel->setPixmap(QPixmap::fromImage(img));
        imageLabel->adjustSize();
        scrollArea->show();
        QApplication::processEvents();
        adjustSize();
        QStyleOptionFrameV3 opt;
        opt.init(scrollArea);
        int fw=style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, scrollArea);
        if (fw<0) {
            fw=2;
        }
        fw*=2;
        QRect desktop = qApp->desktop()->screenGeometry(this);
        int maxWidth=desktop.width()*0.75;
        int maxHeight=desktop.height()*0.75;
        int lrPad=width()-mainWidget()->width();
        int tbPad=height()-mainWidget()->height();
        imgW=img.width();
        imgH=img.height();
        resize(lrPad+qMax(100, qMin(maxWidth, imgW+fw)), tbPad+qMax(100, qMin(maxHeight, imgH+fw)));
        setWindowTitle(i18nc("Image (width x height zoom%)", "Image (%1 x %2 %3%)", imgW, imgH, zoom*100));
        show();
    }
}

void CoverPreview::downloading(const QString &u)
{
    url=u;
    if (!url.isEmpty()) {
        loadingLabel->show();
        scrollArea->hide();
        pbar->show();
        pbar->setValue(0);
        int spacing=Utils::layoutSpacing(this);
        QApplication::processEvents();
        adjustSize();
        resize(qMin(200, loadingLabel->width()+32), loadingLabel->height()+pbar->height()+(3*spacing));
        show();
    }
}

void CoverPreview::progress(qint64 rx, qint64 total)
{
    pbar->setValue((int)(((rx*100.0)/(total*1.0))+0.5));
}

void CoverPreview::scaleImage(int adjust)
{
    double newZoom=zoom+(adjust*0.25);

    if (newZoom<0.25 || newZoom>4.0 || (fabs(newZoom-zoom)<0.01)) {
        return;
    }
    zoom=newZoom;
    imageLabel->resize(zoom * imageLabel->pixmap()->size());
    setWindowTitle(i18nc("Image (width x height zoom%)", "Image (%1 x %2 %3%)", imgW, imgH, zoom*100));
}

void CoverPreview::wheelEvent(QWheelEvent *event)
{
    if (scrollArea->isVisible() && QApplication::keyboardModifiers() & Qt::ControlModifier) {
        const int numDegrees = event->delta() / 8;
        const int numSteps = numDegrees / 15;
        if (0!=numSteps) {
            scaleImage(numSteps);
        }
        event->accept();
        return;
    }
    Dialog::wheelEvent(event);
}

CoverDialog::CoverDialog(QWidget *parent)
    : Dialog(parent, "CoverDialog")
    , existing(0)
    , currentQueryProviders(0)
    , preview(0)
    , saving(false)
    , isArtist(false)
    , spinner(0)
    , msgOverlay(0)
    , page(0)
    , menu(0)
    , showAction(0)
    , removeAction(0)
{
    Configuration cfg(objectName());
    enabledProviders=cfg.get("enabledProviders", (int)Prov_All);

    iCount++;
    QWidget *mainWidet = new QWidget(this);
    setupUi(mainWidet);
    setMainWidget(mainWidet);
    setAttribute(Qt::WA_DeleteOnClose);
    setButtons(Cancel|Ok);
    enableButton(Ok, false);
    connect(list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(showImage(QListWidgetItem*)));
    connect(list, SIGNAL(itemSelectionChanged()), SLOT(checkStatus()));
    connect(search, SIGNAL(clicked()), SLOT(sendQuery()));
    connect(query, SIGNAL(returnPressed()), SLOT(sendQuery()));
    connect(addFileButton, SIGNAL(clicked()), SLOT(addLocalFile()));
    connect(list, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(menuRequested(const QPoint&)));

    QFont f(list->font());
    QFontMetrics origFm(f);
    iSize=origFm.height()*7;
    iSize=((iSize/10)*10)+((iSize%10) ? 10 : 0);
    f.setPointSizeF(f.pointSizeF()*0.75);
    QFontMetrics fm(f);
    list->setFont(f);
    list->setAcceptDrops(false);
    list->setContextMenuPolicy(Qt::CustomContextMenu);
    list->setDragDropMode(QAbstractItemView::NoDragDrop);
    list->setDragEnabled(false);
    list->setDropIndicatorShown(false);
    list->setMovement(QListView::Static);
    list->setGridSize(QSize(imageSize()+10, imageSize()+10+(2.25*fm.height())));
    list->setIconSize(QSize(imageSize(), imageSize()));
    int spacing=style()->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Vertical);
    if (spacing<0) {
        spacing=4;
    }
    list->setSpacing(spacing);
    list->setViewMode(QListView::IconMode);
    list->setResizeMode(QListView::Adjust);
    list->setMinimumSize((list->gridSize().width()*3)+style()->pixelMetric(QStyle::PM_ScrollBarExtent)+spacing, list->gridSize().height()*2);
    list->setSortingEnabled(false);

    addFileButton->setIcon(Icon("document-open"));
    addFileButton->setAutoRaise(true);

    configureButton->setIcon(Icons::self()->configureIcon);
    configureButton->setAutoRaise(true);

    QMenu *configMenu=new QMenu(configureButton);
    addProvider(configMenu, QLatin1String("Last.fm"), Prov_LastFm, enabledProviders);
    addProvider(configMenu, i18n("CoverArt Archive"), Prov_CoverArt, enabledProviders);
    addProvider(configMenu, QLatin1String("Google"), Prov_Google, enabledProviders);
    addProvider(configMenu, QLatin1String("Discogs"), Prov_DiscoGs, enabledProviders);
    addProvider(configMenu, QLatin1String("Deezer"), Prov_Deezer, enabledProviders);
    addProvider(configMenu, QLatin1String("Spotify"), Prov_Spotify, enabledProviders);
    addProvider(configMenu, QLatin1String("iTunes"), Prov_ITunes, enabledProviders);
    configureButton->setMenu(configMenu);
    configureButton->setPopupMode(QToolButton::InstantPopup);
    setAcceptDrops(true);
}

CoverDialog::~CoverDialog()
{
    Configuration cfg(objectName());
    cfg.set("enabledProviders", enabledProviders);

    iCount--;
    cancelQuery();
    clearTempFiles();
}

void CoverDialog::show(const Song &s, const Covers::Image &current)
{
    song=s;
    isArtist=song.isArtistImageRequest();
    Covers::Image img=current.img.isNull() ? Covers::locateImage(song) : current;

    if (img.validFileName() && !QFileInfo(img.fileName).isWritable()) {
        MessageBox::error(parentWidget(),
                          (isArtist
                            ? i18n("An image already exists for this artist, and the file is not writeable.")
                            : i18n("A cover already exists for this album, and the file is not writeable."))+
                          QString("<br/><br/><a href=\"%1\">%1</a>").arg(img.fileName));
        deleteLater();
        return;
    }
    if (isArtist) {
        setCaption(i18n("'%1' Artist Image", song.albumartist));
    } else {
        setCaption(i18nc("'Artist - Album' Album Cover", "'%1 - %2' Album Cover", song.albumArtist(), song.album));
    }
    if (!img.img.isNull()) {
        existing=new ExistingCover(isArtist ? Covers::Image(cropImage(img.img, true), img.fileName) : img, list);
        list->addItem(existing);
    }
    query->setText(isArtist ? song.albumArtist() : QString(song.albumArtist()+QLatin1String(" ")+song.album));
    Dialog::show();
    sendQuery();
}

static const char * constHostProperty="host";
static const char * constLargeProperty="large";
static const char * constThumbProperty="thumb";
static const char * constWidthProperty="w";
static const char * constHeightProperty="h";
static const char * constSizeProperty="sz";
static const char * constTypeProperty="type";
static const char * constLastFmHost="ws.audioscrobbler.com";
static const char * constGoogleHost="images.google.com";
static const char * constDiscogsHost="api.discogs.com";
static const char * constCoverArtArchiveHost="coverartarchive.org";
static const char * constSpotifyHost="ws.spotify.com";
static const char * constITunesHost="itunes.apple.com";
static const char * constDeezerHost="api.deezer.com";

void CoverDialog::queryJobFinished()
{
    NetworkJob *reply=qobject_cast<NetworkJob *>(sender());
    if (!reply) {
        return;
    }
    reply->deleteLater();
    if (!currentQuery.contains(reply)) {
        return;
    }

    DBUG << reply->origUrl().toString() << reply->ok();
    currentQuery.remove(reply);
    if (reply->ok()) {
        QString host=reply->property(constHostProperty).toString();
        QByteArray resp=reply->readAll();
        if (constLastFmHost==host) {
            parseLastFmQueryResponse(resp);
        } else if (constGoogleHost==host) {
            parseGoogleQueryResponse(resp);
        } else if (constDiscogsHost==host) {
            parseDiscogsQueryResponse(resp);
        } else if (constCoverArtArchiveHost==host) {
            parseCoverArtArchiveQueryResponse(resp);
        } else if (constSpotifyHost==host) {
            parseSpotifyQueryResponse(resp);
        } else if (constITunesHost==host) {
            parseITunesQueryResponse(resp);
        } else if (constDeezerHost==host) {
            parseDeezerQueryResponse(resp);
        }
    }
    if (currentQuery.isEmpty()) {
        setSearching(false);
    }
}

void CoverDialog::insertItem(CoverItem *item)
{
    list->addItem(item);
    if (item->isLocal()) {
        list->scrollToItem(item);
        list->setItemSelected(item, true);
    }
}

void CoverDialog::downloadJobFinished()
{
    NetworkJob *reply=qobject_cast<NetworkJob *>(sender());
    if (!reply) {
        return;
    }
    reply->deleteLater();
    if (!currentQuery.contains(reply)) {
        return;
    }

    DownloadType dlType=(DownloadType)reply->property(constTypeProperty).toInt();

    if (DL_LargeSave==dlType) {
        saving=false;
    }

    DBUG << reply->origUrl().toString() << reply->ok();
    currentQuery.remove(reply);
    if (reply->ok()) {
        QString host=reply->property(constHostProperty).toString();
        QString url=reply->url().toString();
        QByteArray data=reply->readAll();
        const char *format=Covers::imageFormat(data);
        QImage img=QImage::fromData(data, format);
        if (!img.isNull()) {
            bool isLarge=reply->property(constThumbProperty).toString().isEmpty();
            QTemporaryFile *temp=0;

            if (isLarge || (reply->property(constThumbProperty).toString()==reply->property(constLargeProperty).toString())) {
                temp=new QTemporaryFile(QDir::tempPath()+"/cantata_XXXXXX."+(format ? QString(QLatin1String(format)).toLower() : "png"));

                if (temp->open()) {
                    if (!format) {
                        img.save(temp, "PNG");
                    } else {
                        temp->write(data);
                    }
                    if (tempFiles.size()>=constMaxTempFiles) {
                        QTemporaryFile *last=tempFiles.takeLast();
                        last->remove();
                        delete last;
                    }
                    temp->close();
                    temp->setProperty(constLargeProperty, reply->property(constLargeProperty));
                    tempFiles.prepend(temp);
                } else {
                    delete temp;
                    temp=0;
                }
            }
            if (isLarge) {
                if (DL_LargePreview==dlType) {
                    previewDialog()->showImage(cropImage(img, isArtist), reply->property(constLargeProperty).toString());
                } else if (DL_LargeSave==dlType) {
                    if (!temp) {
                        MessageBox::error(this, i18n("Failed to set cover!\n\nCould not download to temporary file!"));
                    } else if (saveCover(temp->fileName(), img)) {
                        accept();
                    }
                }
            } else {
                CoverItem *item=0;
                img=cropImage(img, isArtist);
                if (constLastFmHost==host) {
                    item=new LastFmCover(reply->property(constLargeProperty).toString(), url, img, list);
                } else if (constGoogleHost==host) {
                    item=new GoogleCover(reply->property(constLargeProperty).toString(), url, img, reply->property(constWidthProperty).toInt(),
                                         reply->property(constHeightProperty).toInt(), reply->property(constSizeProperty).toInt(), list);
                } else if (constDiscogsHost==host) {
                    item=new DiscogsCover(reply->property(constLargeProperty).toString(), url, img, reply->property(constWidthProperty).toInt(),
                                          reply->property(constHeightProperty).toInt(), list);
                } else if (constCoverArtArchiveHost==host) {
                    item=new CoverArtArchiveCover(reply->property(constLargeProperty).toString(), url, img, list);
                } else if (constSpotifyHost==host) {
                    item=new SpotifyCover(reply->property(constLargeProperty).toString(), url, img, list);
                } else if (constITunesHost==host) {
                    item=new ITunesCover(reply->property(constLargeProperty).toString(), url, img, list);
                } else if (constDeezerHost==host) {
                    item=new DeezerCover(reply->property(constLargeProperty).toString(), url, img, list);
                }
                if (item) {
                    insertItem(item);
                }
            }
        }
    } else if (reply->property(constThumbProperty).toString().isEmpty()) {
        if (preview && preview->aboutToShow(reply->property(constLargeProperty).toString())) {
            preview->hide();
        }
        MessageBox::error(this, i18n("Failed to download image!"));
    }
    if (currentQuery.isEmpty()) {
        setSearching(false);
    }
}

void CoverDialog::showImage(QListWidgetItem *item)
{
    if (saving) {
        return;
    }

    CoverItem *cover=static_cast<CoverItem *>(item);

    if (cover->isExisting()) {
        previewDialog()->downloading(cover->url());
        previewDialog()->showImage(static_cast<ExistingCover *>(cover)->image(), cover->url());
    } else if (cover->isLocal()) {
        previewDialog()->downloading(cover->url());
        previewDialog()->showImage(static_cast<LocalCover *>(cover)->image(), cover->url());
    } else {
        previewDialog()->downloading(cover->url());
        NetworkJob *j=downloadImage(cover->url(), DL_LargePreview);
        if (j) {
            j->setProperty(constLargeProperty, cover->url());
            connect(j, SIGNAL(downloadProgress(qint64, qint64)), preview, SLOT(progress(qint64, qint64)));
        }
    }
}

CoverPreview *CoverDialog::previewDialog() {
    if (!preview) {
        preview=new CoverPreview(this);
    }
    return preview;
}

void CoverDialog::sendQuery()
{
    if (saving) {
        return;
    }

    QString fixedQuery(query->text().trimmed());
    fixedQuery.remove(QChar('?'));

    if (fixedQuery.isEmpty()) {
        return;
    }

    if (currentQueryString==fixedQuery && enabledProviders==currentQueryProviders) {
        page++;
    } else {
        page=0;
    }

    if (0==page) {
        QList<CoverItem *> keep;

        while (list->count()) {
            CoverItem *item=static_cast<CoverItem *>(list->takeItem(0));
            if (item->isExisting() || item->isLocal()) {
                keep.append(item);
            } else {
                currentUrls.remove(item->url());
                currentUrls.remove(item->thumbUrl());
                delete item;
            }
        }

        foreach (CoverItem *item, keep) {
            list->addItem(item);
        }

        cancelQuery();
    }

    currentQueryString=fixedQuery;
    if (enabledProviders&Prov_LastFm) {
        sendLastFmQuery(fixedQuery, page);
    }
    if (enabledProviders&Prov_Google) {
        sendGoogleQuery(fixedQuery, page);
    }
    if (enabledProviders&Prov_DiscoGs) {
        sendDiscoGsQuery(fixedQuery, page);
    }
    if (page==0) {
        if (enabledProviders&Prov_Spotify) {
            sendSpotifyQuery(fixedQuery);
        }
        if (enabledProviders&Prov_ITunes) {
            sendITunesQuery(fixedQuery);
        }
        if (enabledProviders&Prov_Deezer) {
            sendDeezerQuery(fixedQuery);
        }
    }
    currentQueryProviders=enabledProviders;
    setSearching(!currentQuery.isEmpty());
}

void CoverDialog::sendLastFmQuery(const QString &fixedQuery, int page)
{
    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("https");
    url.setHost(constLastFmHost);
    url.setPath("/2.0/");
    query.addQueryItem("api_key", Covers::constLastFmApiKey);
    query.addQueryItem("limit", QString::number(20));
    query.addQueryItem("page", QString::number(page));
    query.addQueryItem(isArtist ? "artist" : "album", fixedQuery);
    query.addQueryItem("method", isArtist ? "artist.search" : "album.search");
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::sendGoogleQuery(const QString &fixedQuery, int page)
{
    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("http");
    url.setHost(constGoogleHost);
    url.setPath("/images");
    query.addQueryItem("q", fixedQuery);
    query.addQueryItem("gbv", QChar('1'));
    query.addQueryItem("filter", QChar('1'));
    query.addQueryItem("start", QString::number(20 * page));
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::sendDiscoGsQuery(const QString &fixedQuery, int page)
{
    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("http");
    url.setHost(constDiscogsHost);
    url.setPath("/search");
    query.addQueryItem("page", QString::number(page + 1));
    query.addQueryItem("per_page", QString::number(20));
    query.addQueryItem("type", isArtist ? "artist" : "release");
    query.addQueryItem("q", fixedQuery);
    query.addQueryItem("f", "json");
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::sendSpotifyQuery(const QString &fixedQuery)
{
    if (!QSslSocket::supportsSsl()) {
        return;
    }
    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("http");
    url.setHost(constSpotifyHost);
    url.setPath(isArtist ? "/search/1/artist.json" : "/search/1/album.json");
    query.addQueryItem("q", fixedQuery);
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::sendITunesQuery(const QString &fixedQuery)
{
    if (isArtist) { // TODO???
        return;
    }

    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("http");
    url.setHost(constITunesHost);
    url.setPath("/search");
    query.addQueryItem("term", fixedQuery);
    query.addQueryItem("limit", QString::number(10));
    query.addQueryItem("media", "music");
    query.addQueryItem("entity", "album");
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::sendDeezerQuery(const QString &fixedQuery)
{
    QUrl url;
    #if QT_VERSION < 0x050000
    QUrl &query=url;
    #else
    QUrlQuery query;
    #endif
    url.setScheme("http");
    url.setHost(constDeezerHost);
    url.setPath(isArtist ? "/search/artist" : "/search/album");
    query.addQueryItem("q", fixedQuery);
    query.addQueryItem("nb_items", QString::number(10));
    query.addQueryItem("output", "json");
    #if QT_VERSION >= 0x050000
    url.setQuery(query);
    #endif
    sendQueryRequest(url);
}

void CoverDialog::checkStatus()
{
    QList<QListWidgetItem*> items=list->selectedItems();
    enableButtonOk(1==items.size() && !static_cast<CoverItem *>(items.at(0))->isExisting());
}

void CoverDialog::cancelQuery()
{
    foreach (NetworkJob *job, currentQuery) {
        job->cancelAndDelete();
    }
    currentQuery.clear();
    setSearching(false);
}

void CoverDialog::addLocalFile()
{
    #ifdef ENABLE_KDE_SUPPORT
    QString fileName=KFileDialog::getOpenFileName(KUrl(), "image/jpeg image/png", this, i18n("Load Local Cover"));
    #else
    QString fileName=QFileDialog::getOpenFileName(this, i18n("Load Local Cover"), QDir::homePath(), i18n("Images (*.png *.jpg)"));
    #endif

    if (!fileName.isEmpty()) {
        if (currentLocalCovers.contains(fileName)) {
            MessageBox::error(this, i18n("File is already in list!"));
        } else {
            QImage img(fileName);
            if (img.isNull()) {
                MessageBox::error(this, i18n("Failed to read image!"));
            } else {
                currentLocalCovers.insert(fileName);
                insertItem(new LocalCover(fileName, img, list));
            }
        }
    }
}

void CoverDialog::menuRequested(const QPoint &pos)
{
    if (!menu) {
        menu=new QMenu(list);
        showAction=menu->addAction(Icon("zoom-original"), i18n("Display"));
        removeAction=menu->addAction(Icon("list-remove"), i18n("Remove"));
        connect(showAction, SIGNAL(triggered()), SLOT(showImage()));
        connect(removeAction, SIGNAL(triggered()), SLOT(removeImages()));
    }

    QList<QListWidgetItem*> items=list->selectedItems();
    showAction->setEnabled(1==items.count());
    removeAction->setEnabled(!items.isEmpty());
    if (removeAction->isEnabled()) {
        foreach (QListWidgetItem *i, items) {
            if (static_cast<CoverItem *>(i)->isExisting()) {
                removeAction->setEnabled(false);
            }
        }
    }
    menu->popup(list->mapToGlobal(pos));
}

void CoverDialog::showImage()
{
    QList<QListWidgetItem*> items=list->selectedItems();
    if (1==items.count()) {
        showImage(items.at(0));
    }
}

void CoverDialog::removeImages()
{
    QList<QListWidgetItem*> items=list->selectedItems();
    foreach (QListWidgetItem *i, items) {
        delete i;
    }
}

void CoverDialog::updateProviders()
{
    QAction *s=qobject_cast<QAction *>(sender());
    enabledProviders=0;

    if (s) {
        if (Prov_LastFm==s->data().toUInt() && !s->isChecked()) {
            providers[Prov_CoverArt]->setChecked(false);
        } else if (Prov_CoverArt==s->data().toUInt() && s->isChecked()) {
            providers[Prov_LastFm]->setChecked(true);
        }
    }

    foreach (const QAction *act, configureButton->menu()->actions()) {
        if (act->isChecked()) {
            enabledProviders|=act->data().toUInt();
        }
    }
    DBUG << enabledProviders;
}

void CoverDialog::clearTempFiles()
{
    foreach (QTemporaryFile *file, tempFiles) {
        file->remove();
        delete file;
    }
}

void CoverDialog::sendQueryRequest(const QUrl &url, const QString &host)
{
    DBUG << url.toString();
    NetworkJob *j=NetworkAccessManager::self()->get(QNetworkRequest(url));
    j->setProperty(constHostProperty, host.isEmpty() ? url.host() : host);
    j->setProperty(constTypeProperty, (int)DL_Query);
    connect(j, SIGNAL(finished()), this, SLOT(queryJobFinished()));
    currentQuery.insert(j);
}

NetworkJob * CoverDialog::downloadImage(const QString &url, DownloadType dlType)
{
    DBUG << url << dlType;
    if (DL_Thumbnail==dlType) {
        if (currentUrls.contains(url)) {
            return 0;
        }
        currentUrls.insert(url);
    } else {
        foreach (QTemporaryFile *tmp, tempFiles) {
            if (tmp->property(constLargeProperty).toString()==url) {
                QImage img;
                if (img.load(tmp->fileName())) {
                    if (DL_LargePreview==dlType) {
                        previewDialog()->downloading(url);
                        previewDialog()->showImage(img, url);
                    } else if (DL_LargeSave==dlType) {
                        if (saveCover(tmp->fileName(), img)) {
                            accept();
                        }
                    }
                    return 0;
                }
                tmp->remove();
                delete tmp;
                tempFiles.removeAll(tmp);
                break;
            }
        }
    }

    if (DL_LargeSave==dlType) {
        saving=true;
    }

    NetworkJob *j=NetworkAccessManager::self()->get(QNetworkRequest(QUrl(url)));
    connect(j, SIGNAL(finished()), this, SLOT(downloadJobFinished()));
    currentQuery.insert(j);
    j->setProperty(constTypeProperty, (int)dlType);
    if (saving) {
        previewDialog()->downloading(url);
        connect(j, SIGNAL(downloadProgress(qint64, qint64)), preview, SLOT(progress(qint64, qint64)));
    }
    return j;
}

void CoverDialog::downloadThumbnail(const QString &thumbUrl, const QString &largeUrl, const QString &host, int w, int h, int sz)
{
    if (thumbUrl.isEmpty() || largeUrl.isEmpty()) {
        return;
    }

    NetworkJob *j=downloadImage(thumbUrl, DL_Thumbnail);
    if (j) {
        j->setProperty(constThumbProperty, thumbUrl);
        j->setProperty(constLargeProperty, largeUrl);
        j->setProperty(constHostProperty, host);
        if (w>0) {
            j->setProperty(constWidthProperty, w);
        }
        if (h>0) {
            j->setProperty(constHeightProperty, h);
        }
        if (sz>0) {
            j->setProperty(constSizeProperty, sz);
        }
    }
}

typedef QMap<QString, QString> SizeMap;
void CoverDialog::parseLastFmQueryResponse(const QByteArray &resp)
{
    bool inSection=false;
    QXmlStreamReader doc(resp);
    SizeMap urls;
    QList<SizeMap> entries;
    QStringList musibBrainzIds;

    while (!doc.atEnd()) {
        doc.readNext();

        if (doc.isStartElement()) {
            if (!inSection && QLatin1String(isArtist ? "artist" : "album")==doc.name()) {
                inSection=true;
                urls.clear();
            } else if (inSection && QLatin1String("image")==doc.name()) {
                QString size=doc.attributes().value("size").toString();
                QString url=doc.readElementText();
                if (!size.isEmpty() && !url.isEmpty()) {
                    urls.insert(size, url);
                }
            } else if (inSection && QLatin1String("mbid")==doc.name()) {
                QString id=doc.readElementText();
                if (id.length()>4) {
                    musibBrainzIds.append(id);
                }
            }
        } else if (doc.isEndElement() && inSection && QLatin1String(isArtist ? "artist" : "album")==doc.name()) {
            if (!urls.isEmpty()) {
                entries.append(urls);
                urls.clear();
            }
            inSection=false;
        }
    }

    QStringList largeUrls=QStringList() << "extralarge" << "large";
    QStringList thumbUrls=QStringList() << "large" << "medium" << "small";

    foreach (const SizeMap &urls, entries) {
        QString largeUrl;
        QString thumbUrl;

        foreach (const QString &u, largeUrls) {
            if (urls.contains(u)) {
                largeUrl=urls[u];
                break;
            }
        }

        foreach (const QString &u, thumbUrls) {
            if (urls.contains(u)) {
                thumbUrl=urls[u];
                break;
            }
        }
        downloadThumbnail(thumbUrl, largeUrl, constLastFmHost);
    }

    if (enabledProviders&Prov_CoverArt) {
        foreach (const QString &id, musibBrainzIds) {
            QUrl coverartUrl;
            coverartUrl.setScheme("http");
            coverartUrl.setHost(constCoverArtArchiveHost);
            coverartUrl.setPath("/release/"+id);
            sendQueryRequest(coverartUrl);
        }
    }
}

void CoverDialog::parseGoogleQueryResponse(const QByteArray &resp)
{
    // Code based on Audex CDDA Extractor
    QRegExp rx("<a\\shref=\"(\\/imgres\\?imgurl=[^\"]+)\">[\\s\\n]*<img[^>]+src=\"([^\"]+)\"");
    rx.setCaseSensitivity(Qt::CaseInsensitive);
    rx.setMinimal(true);

    int pos = 0;
    QString xml(QString::fromUtf8(resp));
    QString html = xml.replace(QLatin1String("&amp;"), QLatin1String("&"));

    while (-1!=(pos=rx.indexIn(html, pos))) {
        #if QT_VERSION < 0x050000
        QUrl url("http://www.google.com"+rx.cap(1));
        #else
        QUrl u("http://www.google.com"+rx.cap(1));
        QUrlQuery url(u);
        #endif
        int width=url.queryItemValue("w").toInt();
        int height=url.queryItemValue("h").toInt();
        if (canUse(width, height)) {
            downloadThumbnail(rx.cap(2), url.queryItemValue("imgurl"), constGoogleHost, width, height, url.queryItemValue("sz").toInt());
        }
        pos += rx.matchedLength();
    }
}

void CoverDialog::parseDiscogsQueryResponse(const QByteArray &resp)
{
    #if QT_VERSION >= 0x050000
    QJsonParseError jsonParseError;
    QVariantMap parsed=QJsonDocument::fromJson(resp, &jsonParseError).toVariant().toMap();
    bool ok=QJsonParseError::NoError==jsonParseError.error;
    #else
    bool ok=false;
    QVariantMap parsed=QJson::Parser().parse(resp, &ok).toMap();
    #endif

    if (ok && parsed.contains("resp")) {
        QVariantMap response=parsed["resp"].toMap();
        if (response.contains("search")) {
            QVariantMap search=response["search"].toMap();
            if (search.contains("searchresults")) {
                QVariantMap searchresults=search["searchresults"].toMap();
                if (searchresults.contains("results")) {
                    QVariantList results=searchresults["results"].toList();
                    foreach (const QVariant &r, results) {
                        QVariantMap rm=r.toMap();
                        if (!isArtist && rm.contains("uri")) {
                            QStringList parts=rm["uri"].toString().split("/", QString::SkipEmptyParts);
                            if (!parts.isEmpty()) {
                                QUrl discogsUrl;
                                #if QT_VERSION < 0x050000
                                QUrl &discogsQuery=discogsUrl;
                                #else
                                QUrlQuery discogsQuery;
                                #endif
                                discogsUrl.setScheme("http");
                                discogsUrl.setHost(constDiscogsHost);
                                discogsUrl.setPath("/release/"+parts.last());
                                discogsQuery.addQueryItem("f", "json");
                                #if QT_VERSION >= 0x050000
                                discogsUrl.setQuery(discogsQuery);
                                #endif
                                sendQueryRequest(discogsUrl);
                            }
                        } else if (isArtist && rm.contains("thumb")) {
                            QString thumbUrl=rm["thumb"].toString();
                            if (thumbUrl.contains("/image/A-150-")) {
                                downloadThumbnail(thumbUrl, QString(thumbUrl).replace("image/A-150-", "/image/A-"), constDiscogsHost);
                            }
                        }
                    }
                }
            }
        } else if (response.contains("release")) {
            QVariantMap release=response["release"].toMap();
            if (release.contains("images")) {
                QVariantList images=release["images"].toList();
                foreach (const QVariant &i, images) {
                    QVariantMap im=i.toMap();
                    if (im.contains("uri") && im.contains("uri150") && im.contains("width") && im.contains("height") &&
                        canUse(im["width"].toString().toInt(), im["height"].toString().toInt())) {
                        downloadThumbnail(im["uri150"].toString(), im["uri"].toString(), constDiscogsHost,
                                          im["width"].toString().toInt(), im["height"].toString().toInt());
                    }
                }
            }
        }
    }
}

void CoverDialog::parseCoverArtArchiveQueryResponse(const QByteArray &resp)
{
    #if QT_VERSION >= 0x050000
    QJsonParseError jsonParseError;
    QVariantMap parsed=QJsonDocument::fromJson(resp, &jsonParseError).toVariant().toMap();
    bool ok=QJsonParseError::NoError==jsonParseError.error;
    #else
    bool ok=false;
    QVariantMap parsed=QJson::Parser().parse(resp, &ok).toMap();
    #endif

    if (ok && parsed.contains("images")) {
        QVariantList images=parsed["images"].toList();
        foreach (const QVariant &i, images) {
            QVariantMap im=i.toMap();
            if (im.contains("front") && im["front"].toBool() && im.contains("image") && im.contains("thumbnails")) {
                QVariantMap thumb=im["thumbnails"].toMap();
                QString largeUrl=im["image"].toString();
                QString thumbUrl;
                if (thumb.contains("small")) {
                    thumbUrl=thumb["small"].toString();
                } else if (thumb.contains("large")) {
                    thumbUrl=thumb["large"].toString();
                }
                downloadThumbnail(thumbUrl, largeUrl, constCoverArtArchiveHost);
            }
        }
    }
}

void CoverDialog::parseSpotifyQueryResponse(const QByteArray &resp)
{
    #if QT_VERSION >= 0x050000
    QJsonParseError jsonParseError;
    QVariantMap parsed=QJsonDocument::fromJson(resp, &jsonParseError).toVariant().toMap();
    bool ok=QJsonParseError::NoError==jsonParseError.error;
    #else
    bool ok=false;
    QVariantMap parsed=QJson::Parser().parse(resp, &ok).toMap();
    #endif

    if (ok) {
        if (parsed.contains("info")) { // Initial query response...
            const QString key=QLatin1String(isArtist ? "artists" : "albums");
            const QString href=QLatin1String("spotify:")+QLatin1String(isArtist ? "artist:" : "album:");
            const QString baseUrl=QLatin1String("https://embed.spotify.com/oembed/?url=http://open.spotify.com/")+QLatin1String(isArtist ? "artist/" : "album/");
            if (parsed.contains(key)) {
                QVariantList results=parsed[key].toList();
                foreach (const QVariant &res, results) {
                    QVariantMap item=res.toMap();
                    if (item.contains("href")) {
                        QString url=item["href"].toString();
                        if (url.contains(href)) {
                            url=baseUrl+url.remove(href);
                            sendQueryRequest(QUrl(url), constSpotifyHost);
                        }
                    }
                }
            }
        } else if (parsed.contains("provider_url") && parsed.contains("thumbnail_url")) { // Response to query above
            QString thumbUrl=parsed["thumbnail_url"].toString();
            downloadThumbnail(thumbUrl, QString(thumbUrl).replace("/cover/", "/640/"), constSpotifyHost);
        }
    }
}

void CoverDialog::parseITunesQueryResponse(const QByteArray &resp)
{
    #if QT_VERSION >= 0x050000
    QJsonParseError jsonParseError;
    QVariantMap parsed=QJsonDocument::fromJson(resp, &jsonParseError).toVariant().toMap();
    bool ok=QJsonParseError::NoError==jsonParseError.error;
    #else
    bool ok=false;
    QVariantMap parsed=QJson::Parser().parse(resp, &ok).toMap();
    #endif

    if (ok && parsed.contains("results")) {
        QVariantList results=parsed["results"].toList();
        foreach (const QVariant &res, results) {
            QVariantMap item=res.toMap();
            if (item.contains("artworkUrl100")) {
                QString thumbUrl=item["artworkUrl100"].toString();
                downloadThumbnail(thumbUrl, QString(thumbUrl).replace("100x100", "600x600"), constITunesHost);
            }
        }
    }
}

void CoverDialog::parseDeezerQueryResponse(const QByteArray &resp)
{
    const QString key=QLatin1String(isArtist ? "picture" : "cover");
    #if QT_VERSION >= 0x050000
    QJsonParseError jsonParseError;
    QVariantMap parsed=QJsonDocument::fromJson(resp, &jsonParseError).toVariant().toMap();
    bool ok=QJsonParseError::NoError==jsonParseError.error;
    #else
    bool ok=false;
    QVariantMap parsed=QJson::Parser().parse(resp, &ok).toMap();
    #endif

    if (ok && parsed.contains("data")) {
        QVariantList results=parsed["data"].toList();
        foreach (const QVariant &res, results) {
            QVariantMap item=res.toMap();
            if (item.contains(key)) {
                QString thumbUrl=item[key].toString();
                downloadThumbnail(thumbUrl, thumbUrl+"&size=big", constDeezerHost);
            }
        }
    }
}

void CoverDialog::slotButtonClicked(int button)
{
    switch (button) {
    case Ok: {
        QList<QListWidgetItem*> items=list->selectedItems();
        if (1==items.size()) {
            CoverItem *cover=static_cast<CoverItem *>(items.at(0));
            if (cover->isLocal()) {
                if (saveCover(cover->url(), static_cast<LocalCover *>(cover)->image())) {
                    accept();
                }
            } else if (!cover->isExisting()) {
                NetworkJob *j=downloadImage(cover->url(), DL_LargeSave);
                if (j) {
                    j->setProperty(constLargeProperty, cover->url());
                }
            }
        }
        break;
    }
    case Cancel:
        reject();
        // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!!
        Dialog::slotButtonClicked(button);
        break;
    default:
        break;
    }
}

bool CoverDialog::saveCover(const QString &src, const QImage &img)
{
    QString filePath=song.filePath();
    if (song.isCdda()) {
        QString dir = Utils::cacheDir(Covers::constCddaCoverDir, true);
        if (!dir.isEmpty()) {
            QString destName=dir+filePath.mid(Song::constCddaProtocol.length())+src.mid(src.length()-4);
            if (QFile::exists(destName)) {
                QFile::remove(destName);
            }
            if (QFile::copy(src, destName)) {
                emit selectedCover(img, destName);
                return true;
            }
        }
        MessageBox::error(this, i18n("Failed to set cover!\n\nCould not make copy!"));
        return false;
    }
    QString existingBackup;

    if (existing && !existing->url().isEmpty() && !existing->url().startsWith(Covers::constCoverInTagPrefix)) {
        static const QLatin1String constBakExt(".bak");
        existingBackup=existing->url()+constBakExt;
        if (!QFile::rename(existing->url(), existingBackup)) {
            MessageBox::error(this, i18n("Failed to set cover!\n\nCould not backup original!"));
            return false;
        }
    }

    QString destName;
    QString mpdDir;
    QString dirName;
    bool saveInMpd=Settings::self()->storeCoversInMpdDir();

    if (saveInMpd) {
        bool haveAbsPath=filePath.startsWith('/');
        mpdDir=MPDConnection::self()->getDetails().dir;
        if (haveAbsPath || !mpdDir.isEmpty()) {
            dirName=filePath.endsWith('/') ? (haveAbsPath ? QString() : mpdDir)+filePath
                                            : Utils::getDir((haveAbsPath ? QString() : mpdDir)+filePath);
        }
    }

    if (isArtist) {
        if (saveInMpd && !mpdDir.isEmpty() && dirName.startsWith(mpdDir) && 2==dirName.mid(mpdDir.length()).split('/', QString::SkipEmptyParts).count()) {
            QDir d(dirName);
            d.cdUp();
            destName=d.absolutePath()+'/'+Covers::artistFileName(song)+src.mid(src.length()-4);
        } else {
            destName=Utils::cacheDir(Covers::constCoverDir, true)+Covers::encodeName(song.albumartist)+src.mid(src.length()-4);
        }
    } else {
        if (saveInMpd) {
            destName=dirName+Covers::albumFileName(song)+src.mid(src.length()-4);
        } else { // Save to cache dir...
            QString dir(Utils::cacheDir(Covers::constCoverDir+Covers::encodeName(song.albumArtist()), true));
            destName=dir+Covers::encodeName(song.album)+src.mid(src.length()-4);
        }
    }

    if (!destName.startsWith("http://") && QFile::copy(src, destName)) {
        Utils::setFilePerms(destName);
        if (!existingBackup.isEmpty() && QFile::exists(existingBackup)) {
            QFile::remove(existingBackup);
        }
        Covers::self()->updateCover(song, img, destName);
        return true;
    } else {
        if (existing && !existingBackup.isEmpty()) {
            QFile::rename(existingBackup, existing->url());
        }
        MessageBox::error(this, i18n("Failed to set cover!\n\nCould not copy file to '%1'!", destName));
        return false;
    }
}

static bool hasMimeType(QDropEvent *event)
{
    #if QT_VERSION < 0x050000
    return event->provides("text/uri-list");
    #else
    return event->mimeData()->formats().contains(QLatin1String("text/uri-list"));
    #endif
}

void CoverDialog::dragEnterEvent(QDragEnterEvent *event)
{
    if (hasMimeType(event)) {
        event->acceptProposedAction();
    }
}

void CoverDialog::dropEvent(QDropEvent *event)
{
    if (hasMimeType(event) && Qt::CopyAction==event->dropAction()) {
        event->acceptProposedAction();

        QList<QUrl> urls(event->mimeData()->urls());
        foreach (const QUrl &url, urls) {
            if (url.scheme().isEmpty() || "file"==url.scheme()) {
                QString path=url.path();
                if (!currentLocalCovers.contains(path) && (path.endsWith(".jpg", Qt::CaseInsensitive) || path.endsWith(".png", Qt::CaseInsensitive))) {
                    QImage img(path);
                    if (!img.isNull()) {
                        currentLocalCovers.insert(path);
                        insertItem(new LocalCover(path, img, list));
                    }
                }
            }
        }
    }
}

void CoverDialog::setSearching(bool s)
{
    if (!spinner && !s) {
        return;
    }
    if (!spinner) {
        spinner=new Spinner(this);
        spinner->setWidget(list);
    }
    if (!msgOverlay) {
        msgOverlay=new MessageOverlay(this);
        msgOverlay->setWidget(list);
        connect(msgOverlay, SIGNAL(cancel()), SLOT(cancelQuery()));
    }
    if (s) {
        spinner->start();
        msgOverlay->setText(i18n("Searching..."));
    } else {
        spinner->stop();
        msgOverlay->setText(QString());
    }
}

void CoverDialog::addProvider(QMenu *mnu, const QString &name, int bit, int value)
{
    QAction *act=mnu->addAction(name);
    act->setData(bit);
    act->setCheckable(true);
    act->setChecked(value&bit);
    connect(act, SIGNAL(toggled(bool)), this, SLOT(updateProviders()));
    providers.insert(bit, act);
}
