#include <QDir>
#include <QApplication>
#include <QProgressDialog>

#include "../ui_qt.h"

#ifndef CXX_BUILD
extern "C" {
#endif

#ifdef HAVE_CONFIG_H
#include "../../../config.h"
#endif

#include <string/stdstring.h>
#include <streams/file_stream.h>
#include <queues/task_queue.h>
#include <file/archive_file.h>

#include "../../../config.def.h"
#include "../../../configuration.h"
#include "../../../tasks/tasks_internal.h"
#include "../../../verbosity.h"
#include "../../../version.h"

#ifndef CXX_BUILD
}
#endif

#undef TEMP_EXTENSION
#undef USER_AGENT
#define USER_AGENT "RetroArch-WIMP/" PACKAGE_VERSION
#define PARTIAL_EXTENSION ".partial"
#define TEMP_EXTENSION ".tmp"
#define THUMBNAILPACK_URL_HEADER "http://thumbnailpacks.libretro.com/"
#define THUMBNAILPACK_EXTENSION ".zip"
#define THUMBNAIL_URL_HEADER "https://thumbnails.libretro.com/"
#define THUMBNAIL_IMAGE_EXTENSION ".png"

static void cb_extract_thumbnail_pack(retro_task_t *task,
      void *task_data, void *user_data, const char *err)
{
   decompress_task_data_t *dec = (decompress_task_data_t*)task_data;
   MainWindow *mainwindow      = (MainWindow*)user_data;

   if (err)
      RARCH_ERR("%s", err);

   if (dec)
   {
      if (filestream_exists(dec->source_file))
         filestream_delete(dec->source_file);

      free(dec->source_file);
      free(dec);
   }

   mainwindow->onThumbnailPackExtractFinished(string_is_empty(err));
}

void MainWindow::onThumbnailPackDownloadNetworkError(QNetworkReply::NetworkError code)
{
   QByteArray errorStringArray;
   QNetworkReply *reply        = m_thumbnailPackDownloadReply.data();
   const char *errorStringData = NULL;

   m_thumbnailPackDownloadProgressDialog->cancel();

   if (!reply)
      return;

   errorStringArray = reply->errorString().toUtf8();
   errorStringData  = errorStringArray.constData();

   RARCH_ERR("[Qt]: Network error code %d received: %s\n",
         code, errorStringData);

#if 0
   /* Deleting the reply here seems to cause a strange 
    * heap-use-after-free crash. */
   reply->disconnect();
   reply->abort();
   reply->deleteLater();
#endif
}

void MainWindow::onThumbnailPackDownloadNetworkSslErrors(const QList<QSslError> &errors)
{
   QNetworkReply *reply = m_thumbnailPackDownloadReply.data();
   int i;

   if (!reply)
      return;

   for (i = 0; i < errors.count(); i++)
   {
      const QSslError &error = errors.at(i);
      QString string         = 
           QString("Ignoring SSL error code ") 
         + QString::number(error.error()) 
         + ": " 
         + error.errorString();
      QByteArray stringArray = string.toUtf8();
      const char *stringData = stringArray.constData();
      RARCH_ERR("[Qt]: %s\n", stringData);
   }

   /* Ignore all SSL errors for now, like self-signed, expired etc. */
   reply->ignoreSslErrors();
}

void MainWindow::onThumbnailPackDownloadCanceled()
{
   m_thumbnailPackDownloadProgressDialog->cancel();
}

void MainWindow::onThumbnailPackDownloadFinished()
{
   QString system;
   QNetworkReply *reply = m_thumbnailPackDownloadReply.data();
   QNetworkReply::NetworkError error;
   int code;

   m_thumbnailPackDownloadProgressDialog->cancel();

   /* At least on Linux, the progress dialog will refuse 
    * to hide itself and will stay on screen in a corrupted 
    * way if we happen to show an error message in this function. 
    * processEvents() will sometimes fix it, other times not... 
    * seems random. */
   qApp->processEvents();

   if (!reply)
      return;

   system = reply->property("system").toString();

   error  = reply->error();
   code   = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

   if (m_thumbnailPackDownloadFile.isOpen())
      m_thumbnailPackDownloadFile.close();

   if (code != 200)
   {
      QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();

      if (!redirectUrl.isEmpty())
      {
         QByteArray redirectUrlArray = redirectUrl.toString().toUtf8();
         const char *redirectUrlData = redirectUrlArray.constData();

         RARCH_LOG("[Qt]: Thumbnail pack download got redirect with HTTP code %d: %s\n", code, redirectUrlData);

         reply->disconnect();
         reply->abort();
         reply->deleteLater();

         downloadAllThumbnails(system, redirectUrl);

         return;
      }

      emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": HTTP Code " + QString::number(code));

      m_thumbnailPackDownloadFile.remove();

      RARCH_ERR("[Qt]: Thumbnail pack download failed with HTTP status code: %d\n", code);

      reply->disconnect();
      reply->abort();
      reply->deleteLater();

      return;
   }

   if (error == QNetworkReply::NoError)
   {
      int index = m_thumbnailPackDownloadFile.fileName().lastIndexOf(PARTIAL_EXTENSION);
      QString newFileName = m_thumbnailPackDownloadFile.fileName().left(index);
      QFile newFile(newFileName);

      /* rename() requires the old file to be deleted first if it exists */
      if (newFile.exists() && !newFile.remove())
         RARCH_ERR("[Qt]: Thumbnail pack download finished, but old file could not be deleted.\n");
      else
      {
         if (m_thumbnailPackDownloadFile.rename(newFileName))
         {
            settings_t *settings = config_get_ptr();

            if (settings)
            {
               RARCH_LOG("[Qt]: Thumbnail pack download finished successfully.\n");
               emit extractArchiveDeferred(newFileName, settings->paths.directory_thumbnails, TEMP_EXTENSION, cb_extract_thumbnail_pack);
            }
         }
         else
         {
            RARCH_ERR("[Qt]: Thumbnail pack download finished, but temp file could not be renamed.\n");
            emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE));
         }
      }
   }
   else
   {
      QByteArray errorArray = reply->errorString().toUtf8();
      const char *errorData = errorArray.constData();

      m_thumbnailPackDownloadFile.remove();

      RARCH_ERR("[Qt]: Thumbnail pack download ended prematurely: %s\n", errorData);
      emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": Code " + QString::number(code) + ": " + errorData);
   }

   reply->disconnect();
   reply->close();
}

void MainWindow::onThumbnailPackDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
   QNetworkReply *reply = m_thumbnailPackDownloadReply.data();
   int progress = (bytesReceived / (float)bytesTotal) * 100.0f;

   if (!reply)
      return;

   m_thumbnailPackDownloadProgressDialog->setValue(progress);
}

void MainWindow::onThumbnailPackDownloadReadyRead()
{
   QNetworkReply *reply = m_thumbnailPackDownloadReply.data();

   if (reply)
      m_thumbnailPackDownloadFile.write(reply->readAll());
}

void MainWindow::downloadAllThumbnails(QString system, QUrl url)
{
   QString urlString;
   QNetworkRequest request;
   QByteArray urlArray;
   QNetworkReply *reply = NULL;
   settings_t *settings = config_get_ptr();
   const char *urlData  = NULL;

   if (!settings)
      return;

   urlString            = 
      QString(THUMBNAILPACK_URL_HEADER) 
      + system 
      + THUMBNAILPACK_EXTENSION;

   if (url.isEmpty())
      url               = urlString;

   request.setUrl(url);

   urlArray             = url.toString().toUtf8();
   urlData              = urlArray.constData();

   if (m_thumbnailPackDownloadFile.isOpen())
   {
      RARCH_ERR("[Qt]: File is already open.\n");
      return;
   }
   else
   {
      QDir dir;
      const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
      QString dirString               = QString(path_dir_thumbnails);
      QString fileName                = 
         dirString 
         + "/" 
         + system 
         + THUMBNAILPACK_EXTENSION 
         + PARTIAL_EXTENSION;
      QByteArray fileNameArray        = fileName.toUtf8();
      const char *fileNameData        = fileNameArray.constData();

      dir.mkpath(dirString);

      m_thumbnailPackDownloadFile.setFileName(fileName);

      if (!m_thumbnailPackDownloadFile.open(QIODevice::WriteOnly))
      {
         m_thumbnailPackDownloadProgressDialog->cancel();
         showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
         RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData);
         return;
      }
   }

   RARCH_LOG("[Qt]: Starting thumbnail pack download...\n");
   RARCH_LOG("[Qt]: Downloading URL %s\n", urlData);

   request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT);

   m_thumbnailPackDownloadProgressDialog->setWindowModality(Qt::NonModal);
   m_thumbnailPackDownloadProgressDialog->setMinimumDuration(0);
   m_thumbnailPackDownloadProgressDialog->setRange(0, 100);
   m_thumbnailPackDownloadProgressDialog->setAutoClose(true);
   m_thumbnailPackDownloadProgressDialog->setAutoReset(true);
   m_thumbnailPackDownloadProgressDialog->setValue(0);
   m_thumbnailPackDownloadProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_DOWNLOADING)) + "...");
   m_thumbnailPackDownloadProgressDialog->setCancelButtonText(tr("Cancel"));
   m_thumbnailPackDownloadProgressDialog->show();

   m_thumbnailPackDownloadReply = m_networkManager->get(request);

   reply = m_thumbnailPackDownloadReply.data();
   reply->setProperty("system", system);

   /* Make sure any previous connection is removed first */
   disconnect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));
   connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));

   connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onThumbnailPackDownloadNetworkError(QNetworkReply::NetworkError)));
   connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(onThumbnailPackDownloadNetworkSslErrors(const QList<QSslError>&)));
   connect(reply, SIGNAL(finished()), this, SLOT(onThumbnailPackDownloadFinished()));
   connect(reply, SIGNAL(readyRead()), this, SLOT(onThumbnailPackDownloadReadyRead()));
   connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onThumbnailPackDownloadProgress(qint64, qint64)));
}

void MainWindow::onThumbnailPackExtractFinished(bool success)
{
   m_updateProgressDialog->cancel();

   if (!success)
   {
      RARCH_ERR("[Qt]: Thumbnail pack extraction failed.\n");
      emit showErrorMessageDeferred(msg_hash_to_str(MSG_DECOMPRESSION_FAILED));
      return;
   }

   RARCH_LOG("[Qt]: Thumbnail pack extracted successfully.\n");

   emit showInfoMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_PACK_DOWNLOADED_SUCCESSFULLY));

   QNetworkReply *reply = m_thumbnailPackDownloadReply.data();

   m_playlistModel->reloadSystemThumbnails(reply->property("system").toString());
   reply->deleteLater();
   updateVisibleItems();

   /* Reload thumbnail image */
   emit itemChanged();
}

void MainWindow::onThumbnailDownloadNetworkError(QNetworkReply::NetworkError code)
{
   QByteArray errorStringArray;
   QNetworkReply *reply        = m_thumbnailDownloadReply.data();
   const char *errorStringData = NULL;

   m_thumbnailDownloadProgressDialog->cancel();

   if (!reply)
      return;

   errorStringArray = reply->errorString().toUtf8();
   errorStringData  = errorStringArray.constData();

   RARCH_ERR("[Qt]: Network error code %d received: %s\n",
         code, errorStringData);

#if 0
   /* Deleting the reply here seems to cause a strange 
    * heap-use-after-free crash. */
   reply->disconnect();
   reply->abort();
   reply->deleteLater();
#endif
}

void MainWindow::onThumbnailDownloadNetworkSslErrors(
      const QList<QSslError> &errors)
{
   QNetworkReply *reply = m_thumbnailDownloadReply.data();
   int i;

   if (!reply)
      return;

   for (i = 0; i < errors.count(); i++)
   {
      const QSslError &error = errors.at(i);
      QString         string = 
           QString("Ignoring SSL error code ") 
         + QString::number(error.error()) 
         + ": " 
         + error.errorString();
      QByteArray stringArray = string.toUtf8();
      const char *stringData = stringArray.constData();
      RARCH_ERR("[Qt]: %s\n", stringData);
   }

   /* Ignore all SSL errors for now, like self-signed, expired etc. */
   reply->ignoreSslErrors();
}

void MainWindow::onThumbnailDownloadCanceled()
{
   m_thumbnailDownloadProgressDialog->cancel();
}

void MainWindow::onThumbnailDownloadFinished()
{
   int code;
   QString system;
   QString title;
   QString downloadType;
   QNetworkReply::NetworkError error;
   QNetworkReply *reply = m_thumbnailDownloadReply.data();

   m_thumbnailDownloadProgressDialog->cancel();

   /* At least on Linux, the progress dialog will refuse 
    * to hide itself and will stay on screen in a corrupted 
    * way if we happen to show an error message in this 
    * function. processEvents() will sometimes fix it, 
    * other times not... seems random. */
   qApp->processEvents();

   if (!reply)
      return;

   system       = reply->property("system").toString();
   title        = reply->property("title").toString();
   downloadType = reply->property("download_type").toString();

   error        = reply->error();
   code         = reply->attribute(
         QNetworkRequest::HttpStatusCodeAttribute).toInt();

   if (m_thumbnailDownloadFile.isOpen())
      m_thumbnailDownloadFile.close();

   if (code != 200)
   {
      QUrl redirectUrl               = 
         reply->attribute(
               QNetworkRequest::RedirectionTargetAttribute).toUrl();

      if (!redirectUrl.isEmpty())
      {
         QByteArray redirectUrlArray = redirectUrl.toString().toUtf8();
         const char *redirectUrlData = redirectUrlArray.constData();

         m_pendingThumbnailDownloadTypes.prepend(downloadType);

         RARCH_LOG("[Qt]: Thumbnail download got redirect with HTTP code %d: %s\n", code, redirectUrlData);

         reply->disconnect();
         reply->abort();
         reply->deleteLater();

         downloadThumbnail(system, title, redirectUrl);

         return;
      }

      m_thumbnailDownloadFile.remove();

      RARCH_ERR("[Qt]: Thumbnail download failed with HTTP status code: %d\n", code);

      reply->disconnect();
      reply->abort();
      reply->deleteLater();

      if (!m_pendingThumbnailDownloadTypes.isEmpty())
         downloadThumbnail(system, title);

      return;
   }

   if (error == QNetworkReply::NoError)
   {
      int index           = m_thumbnailDownloadFile.fileName().
         lastIndexOf(PARTIAL_EXTENSION);
      QString newFileName = m_thumbnailDownloadFile.fileName().left(index);
      QFile newFile(newFileName);

      /* rename() requires the old file to be deleted first if it exists */
      if (newFile.exists() && !newFile.remove())
         RARCH_ERR("[Qt]: Thumbnail download finished, but old file could not be deleted.\n");
      else
      {
         if (m_thumbnailDownloadFile.rename(newFileName))
         {
            RARCH_LOG("[Qt]: Thumbnail download finished successfully.\n");
            /* reload thumbnail image */
            m_playlistModel->reloadThumbnailPath(m_thumbnailDownloadFile.fileName());
            updateVisibleItems();
            emit itemChanged();
         }
         else
         {
            RARCH_ERR("[Qt]: Thumbnail download finished, but temp file could not be renamed.\n");
            emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE));
         }
      }
   }
   else
   {
      QByteArray errorArray = reply->errorString().toUtf8();
      const char *errorData = errorArray.constData();

      m_thumbnailDownloadFile.remove();

      RARCH_ERR("[Qt]: Thumbnail download ended prematurely: %s\n", errorData);
      emit showErrorMessageDeferred(
              QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) 
            + ": Code " 
            + QString::number(code) 
            + ": " 
            + errorData);
   }

   reply->disconnect();
   reply->close();
   reply->deleteLater();

   if (!m_pendingThumbnailDownloadTypes.isEmpty())
      emit gotThumbnailDownload(system, title);
}

void MainWindow::onDownloadThumbnail(QString system, QString title)
{
   downloadThumbnail(system, title);
}

void MainWindow::onThumbnailDownloadProgress(
      qint64 bytesReceived, qint64 bytesTotal)
{
   QNetworkReply *reply = m_thumbnailDownloadReply.data();
   int         progress = (bytesReceived / (float)bytesTotal) * 100.0f;

   if (!reply)
      return;

   m_thumbnailDownloadProgressDialog->setValue(progress);
}

void MainWindow::onThumbnailDownloadReadyRead()
{
   QNetworkReply *reply = m_thumbnailDownloadReply.data();

   if (reply)
      m_thumbnailDownloadFile.write(reply->readAll());
}

void MainWindow::downloadThumbnail(QString system, QString title, QUrl url)
{
   QString urlString;
   QNetworkRequest request;
   QByteArray urlArray;
   QString downloadType;
   QNetworkReply *reply     = NULL;
   const char *urlData      = NULL;
   settings_t *settings     = config_get_ptr();

   if (!settings || m_pendingThumbnailDownloadTypes.isEmpty())
      return;

   title                    = getScrubbedString(title);
   downloadType             = m_pendingThumbnailDownloadTypes.takeFirst();
   urlString                = QString(THUMBNAIL_URL_HEADER) 
      + system + "/"
      + downloadType + "/" 
      + title 
      + THUMBNAIL_IMAGE_EXTENSION;

   if (url.isEmpty())
      url = urlString;

   request.setUrl(url);

   urlArray                 = url.toString().toUtf8();
   urlData                  = urlArray.constData();

   if (m_thumbnailDownloadFile.isOpen())
   {
      RARCH_ERR("[Qt]: File is already open.\n");
      return;
   }
   else
   {
      QDir dir;
      const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
      QString               dirString = QString(path_dir_thumbnails) + "/" + system + "/" + downloadType;
      QString fileName                = dirString 
         + "/" 
         + title 
         + THUMBNAIL_IMAGE_EXTENSION 
         + PARTIAL_EXTENSION;
      QByteArray fileNameArray        = fileName.toUtf8();
      const char *fileNameData        = fileNameArray.constData();

      dir.mkpath(dirString);

      m_thumbnailDownloadFile.setFileName(fileName);

      if (!m_thumbnailDownloadFile.open(QIODevice::WriteOnly))
      {
         m_thumbnailDownloadProgressDialog->cancel();
         showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
         RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData);

         if (m_thumbnailDownloadReply)
         {
            m_thumbnailDownloadReply->disconnect();
            m_thumbnailDownloadReply->abort();
            m_thumbnailDownloadReply->deleteLater();
         }

         if (m_pendingThumbnailDownloadTypes.isEmpty())
            m_thumbnailDownloadProgressDialog->cancel();
         else
            downloadThumbnail(system, title);

         return;
      }
   }

   RARCH_LOG("[Qt]: Starting thumbnail download...\n");
   RARCH_LOG("[Qt]: Downloading URL %s\n", urlData);

   request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT);

   m_thumbnailDownloadProgressDialog->setWindowModality(Qt::NonModal);
   m_thumbnailDownloadProgressDialog->setMinimumDuration(0);
   m_thumbnailDownloadProgressDialog->setRange(0, 100);
   m_thumbnailDownloadProgressDialog->setAutoClose(true);
   m_thumbnailDownloadProgressDialog->setAutoReset(true);
   m_thumbnailDownloadProgressDialog->setValue(0);
   m_thumbnailDownloadProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_DOWNLOADING)) + "...");
   m_thumbnailDownloadProgressDialog->setCancelButtonText(tr("Cancel"));
   m_thumbnailDownloadProgressDialog->show();

   m_thumbnailDownloadReply = m_networkManager->get(request);

   reply = m_thumbnailDownloadReply.data();
   reply->setProperty("system", system);
   reply->setProperty("title", title);
   reply->setProperty("download_type", downloadType);

   /* Make sure any previous connection is removed first */
   disconnect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));
   connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));

   connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onThumbnailDownloadNetworkError(QNetworkReply::NetworkError)));
   connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(onThumbnailDownloadNetworkSslErrors(const QList<QSslError>&)));
   connect(reply, SIGNAL(finished()), this, SLOT(onThumbnailDownloadFinished()));
   connect(reply, SIGNAL(readyRead()), this, SLOT(onThumbnailDownloadReadyRead()));
   connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onThumbnailDownloadProgress(qint64, qint64)));
}

void MainWindow::onPlaylistThumbnailDownloadNetworkError(QNetworkReply::NetworkError /*code*/)
{
}

void MainWindow::onPlaylistThumbnailDownloadNetworkSslErrors(const QList<QSslError> &errors)
{
   QNetworkReply *reply = m_playlistThumbnailDownloadReply.data();
   int i;

   if (!reply)
      return;

   for (i = 0; i < errors.count(); i++)
   {
      const QSslError &error = errors.at(i);
      QString string         = 
           QString("Ignoring SSL error code ") 
         + QString::number(error.error()) 
         + ": " 
         + error.errorString();
      QByteArray stringArray = string.toUtf8();
      const char *stringData = stringArray.constData();
      RARCH_ERR("[Qt]: %s\n", stringData);
   }

   /* Ignore all SSL errors for now, like self-signed, expired etc. */
   reply->ignoreSslErrors();
}

void MainWindow::onPlaylistThumbnailDownloadCanceled()
{
   m_playlistThumbnailDownloadProgressDialog->cancel();
   m_playlistThumbnailDownloadWasCanceled = true;
   RARCH_LOG("[Qt]: Playlist thumbnail download was canceled.\n");
}

void MainWindow::onPlaylistThumbnailDownloadFinished()
{
   int code;
   QString playlistPath;
   QNetworkReply::NetworkError error;
   QNetworkReply *reply = m_playlistThumbnailDownloadReply.data();

   if (!reply)
      return;

   playlistPath         = reply->property("playlist").toString();

   error                = reply->error();
   code                 = reply->attribute(
         QNetworkRequest::HttpStatusCodeAttribute).toInt();

   if (m_playlistThumbnailDownloadFile.isOpen())
      m_playlistThumbnailDownloadFile.close();

   if (code != 200)
   {
      QUrl redirectUrl  = reply->attribute(
            QNetworkRequest::RedirectionTargetAttribute).toUrl();

      if (!redirectUrl.isEmpty())
      {
         QByteArray redirectUrlArray = redirectUrl.toString().toUtf8();
         reply->disconnect();
         reply->abort();
         reply->deleteLater();

         downloadNextPlaylistThumbnail(reply->property("system").toString(), reply->property("title").toString(), reply->property("type").toString(), redirectUrl);

         return;
      }

      m_playlistThumbnailDownloadFile.remove();
      m_failedThumbnails++;
   }

   if (error == QNetworkReply::NoError)
   {
      int index = m_playlistThumbnailDownloadFile.fileName().lastIndexOf(PARTIAL_EXTENSION);
      QString newFileName = m_playlistThumbnailDownloadFile.fileName().left(index);
      QFile newFile(newFileName);

      /* rename() requires the old file to be deleted first if it exists */
      if (newFile.exists() && !newFile.remove())
      {
         m_failedThumbnails++;
         RARCH_ERR("[Qt]: Thumbnail download finished, but old file could not be deleted.\n");
      }
      else
      {
         /* Thumbnail download finished succesfully? */
         if (m_playlistThumbnailDownloadFile.rename(newFileName))
            m_downloadedThumbnails++;
         else
            m_failedThumbnails++;
      }
   }
   else
   {
      m_playlistThumbnailDownloadFile.remove();
      m_failedThumbnails++;
   }

   m_playlistModel->reloadThumbnailPath(m_playlistThumbnailDownloadFile.fileName());

   if (!m_playlistThumbnailDownloadWasCanceled && m_pendingPlaylistThumbnails.count() > 0)
   {
      QHash<QString, QString> nextThumbnail = m_pendingPlaylistThumbnails.takeAt(0);
      ViewType viewType = getCurrentViewType();

      updateVisibleItems();
      downloadNextPlaylistThumbnail(nextThumbnail.value("db_name"), nextThumbnail.value("label_noext"), nextThumbnail.value("type"));
   }
   else
   {
      RARCH_LOG("[Qt]: Playlist thumbnails finished downloading.\n");
      /* Update thumbnail */
      emit itemChanged();
   }

   reply->disconnect();
   reply->close();
   reply->deleteLater();
}

void MainWindow::onPlaylistThumbnailDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
#if 0
   QNetworkReply *reply = m_playlistThumbnailDownloadReply.data();
   int progress         = (bytesReceived / (float)bytesTotal) * 100.0f;
   if (!reply)
      return;
#endif
}

void MainWindow::onPlaylistThumbnailDownloadReadyRead()
{
   QNetworkReply *reply = m_playlistThumbnailDownloadReply.data();

   if (reply)
      m_playlistThumbnailDownloadFile.write(reply->readAll());
}

void MainWindow::downloadNextPlaylistThumbnail(
      QString system, QString title, QString type, QUrl url)
{
   QString urlString;
   QNetworkRequest request;
   QNetworkReply *reply = NULL;
   settings_t *settings = config_get_ptr();

   if (!settings)
      return;

   title                = getScrubbedString(title);

   urlString            = 
        QString(THUMBNAIL_URL_HEADER) 
      + system  + "/"
      + type 
      + "/" 
      + title 
      + THUMBNAIL_IMAGE_EXTENSION;

   if (url.isEmpty())
      url               = urlString;

   request.setUrl(url);

   if (m_playlistThumbnailDownloadFile.isOpen())
   {
      RARCH_ERR("[Qt]: File is already open.\n");
      return;
   }
   else
   {
      QString dirString = QString(settings->paths.directory_thumbnails);
      QString fileName = dirString + "/" + system + "/" + type + "/" + title + THUMBNAIL_IMAGE_EXTENSION + PARTIAL_EXTENSION;
      QByteArray fileNameArray = fileName.toUtf8();
      QDir dir;
      const char *fileNameData = fileNameArray.constData();

      dir.mkpath(dirString + "/" + system + "/" + THUMBNAIL_BOXART);
      dir.mkpath(dirString + "/" + system + "/" + THUMBNAIL_SCREENSHOT);
      dir.mkpath(dirString + "/" + system + "/" + THUMBNAIL_TITLE);

      m_playlistThumbnailDownloadFile.setFileName(fileName);

      if (!m_playlistThumbnailDownloadFile.open(QIODevice::WriteOnly))
      {
         m_failedThumbnails++;

         RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData);

         if (m_pendingPlaylistThumbnails.count() > 0)
         {
            QHash<QString, QString> nextThumbnail = m_pendingPlaylistThumbnails.takeAt(0);
            downloadNextPlaylistThumbnail(nextThumbnail.value("db_name"), nextThumbnail.value("label_noext"), nextThumbnail.value("type"));
         }
         else
            m_playlistThumbnailDownloadProgressDialog->cancel();

         return;
      }
   }

   request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT);

   m_playlistThumbnailDownloadReply = m_networkManager->get(request);

   reply = m_playlistThumbnailDownloadReply.data();
   reply->setProperty("system", system);
   reply->setProperty("title", title);
   reply->setProperty("type", type);

   /* Make sure any previous connection is removed first */
   disconnect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));
   connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));

   connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onPlaylistThumbnailDownloadNetworkError(QNetworkReply::NetworkError)));
   connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(onPlaylistThumbnailDownloadNetworkSslErrors(const QList<QSslError>&)));
   connect(reply, SIGNAL(finished()), this, SLOT(onPlaylistThumbnailDownloadFinished()));
   connect(reply, SIGNAL(readyRead()), this, SLOT(onPlaylistThumbnailDownloadReadyRead()));
   connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onPlaylistThumbnailDownloadProgress(qint64, qint64)));

   m_playlistThumbnailDownloadProgressDialog->setValue(m_playlistThumbnailDownloadProgressDialog->maximum() - m_pendingPlaylistThumbnails.count());

   {
      QString labelText  = QString(msg_hash_to_str(MSG_DOWNLOADING)) + "...\n";
      QString labelText2 = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_PLAYLIST_THUMBNAIL_PROGRESS)).arg(m_downloadedThumbnails).arg(m_failedThumbnails);

      labelText.append(labelText2);

      m_playlistThumbnailDownloadProgressDialog->setLabelText(labelText);
   }
}

void MainWindow::downloadPlaylistThumbnails(QString playlistPath)
{
   int i, count;
   QString system, title, type;
   QFile playlistFile(playlistPath);
   settings_t *settings = config_get_ptr();

   if (!settings || !playlistFile.exists())
      return;

   m_pendingPlaylistThumbnails.clear();
   m_downloadedThumbnails                 = 0;
   m_failedThumbnails                     = 0;
   m_playlistThumbnailDownloadWasCanceled = false;

   count = m_playlistModel->rowCount();

   if (count == 0)
      return;

   for (i = 0; i < count; i++)
   {
      QHash<QString, QString> hash;
      QHash<QString, QString> hash2;
      QHash<QString, QString> hash3;
      const QHash<QString, QString> &itemHash = m_playlistModel->index(i, 0).data(PlaylistModel::HASH).value< QHash<QString, QString> >();

      hash["db_name"]     = itemHash.value("db_name");
      hash["label_noext"] = itemHash.value("label_noext");
      hash["type"]        = THUMBNAIL_BOXART;

      hash2               = hash;
      hash3               = hash;

      hash2["type"]       = THUMBNAIL_SCREENSHOT;
      hash3["type"]       = THUMBNAIL_TITLE;

      m_pendingPlaylistThumbnails.append(hash);
      m_pendingPlaylistThumbnails.append(hash2);
      m_pendingPlaylistThumbnails.append(hash3);
   }

   m_playlistThumbnailDownloadProgressDialog->setWindowModality(Qt::NonModal);
   m_playlistThumbnailDownloadProgressDialog->setMinimumDuration(0);
   m_playlistThumbnailDownloadProgressDialog->setRange(0, m_pendingPlaylistThumbnails.count());
   m_playlistThumbnailDownloadProgressDialog->setAutoClose(true);
   m_playlistThumbnailDownloadProgressDialog->setAutoReset(true);
   m_playlistThumbnailDownloadProgressDialog->setValue(0);
   m_playlistThumbnailDownloadProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_DOWNLOADING)) + "...");
   m_playlistThumbnailDownloadProgressDialog->setCancelButtonText(tr("Cancel"));
   m_playlistThumbnailDownloadProgressDialog->show();

   {
      QHash<QString, QString> firstThumbnail = m_pendingPlaylistThumbnails.takeAt(0);

      /* Start downloading the first thumbnail, 
       * the rest will download as each one finishes. */
      downloadNextPlaylistThumbnail(firstThumbnail.value("db_name"), firstThumbnail.value("label_noext"), firstThumbnail.value("type"));
   }
}