/*
 * Fooyin
 * Copyright © 2024, Luke Taylor <luket@pm.me>
 *
 * Fooyin 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 3 of the License, or
 * (at your option) any later version.
 *
 * Fooyin 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 Fooyin.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "artworkdialog.h"

#include "artwork/artworkdelegate.h"
#include "artwork/artworksaveutils.h"
#include "artworkfinder.h"
#include "artworkmodel.h"
#include "internalguisettings.h"

#include <core/library/musiclibrary.h>
#include <gui/coverrepository.h>
#include <gui/guisettings.h>
#include <gui/widgets/expandedtreeview.h>
#include <utils/fileutils.h>
#include <utils/settings/settingsmanager.h>

#include <QComboBox>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QPushButton>

using namespace Qt::StringLiterals;

namespace Fooyin {
ArtworkDialog::ArtworkDialog(std::shared_ptr<NetworkAccessManager> networkManager, MusicLibrary* library,
                             SettingsManager* settings, CoverRepository* coverRepository, TrackList tracks,
                             Track::Cover type, QWidget* parent)
    : QDialog{parent}
    , m_networkManager{std::move(networkManager)}
    , m_library{library}
    , m_settings{settings}
    , m_coverRepository{coverRepository}
    , m_tracks{std::move(tracks)}
    , m_type{type}
    , m_artworkFinder{new ArtworkFinder(m_networkManager, m_settings, this)}
    , m_view{new ExpandedTreeView(this)}
    , m_model{new ArtworkModel(m_settings, this)}
    , m_saveMethods{m_settings->value<Settings::Gui::Internal::ArtworkSaveMethods>().value<ArtworkSaveMethods>()}
    , m_status{new QLabel(this)}
    , m_artist{new QLineEdit(this)}
    , m_album{new QLineEdit(this)}
    , m_searchType{new QComboBox(this)}
    , m_startSearch{new QPushButton(tr("Search"), this)}
{
    setWindowTitle(tr("Artwork Finder"));

    m_artist->setPlaceholderText(tr("Artist"));
    m_album->setPlaceholderText(tr("Album"));

    if(!m_tracks.empty()) {
        const Track& track = m_tracks.front();
        m_artist->setText(m_parser.evaluate(m_settings->value<Settings::Gui::Internal::ArtworkArtistField>(), track));
        m_album->setText(m_parser.evaluate(m_settings->value<Settings::Gui::Internal::ArtworkAlbumField>(), track));
    }

    m_view->setModel(m_model);
    m_view->setItemDelegate(new ArtworkDelegate(this));

    m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
    m_view->setSelectionMode(QAbstractItemView::SingleSelection);
    m_view->setViewMode(ExpandedTreeView::ViewMode::Icon);
    m_view->setUniformRowHeights(false);
    m_view->setHeaderHidden(true);

    QObject::connect(m_artworkFinder, &ArtworkFinder::coverFound, m_model, &ArtworkModel::addPendingCover);
    QObject::connect(m_artworkFinder, &ArtworkFinder::coverLoaded, m_model, &ArtworkModel::loadCover);
    QObject::connect(m_artworkFinder, &ArtworkFinder::coverLoadError, m_model, &ArtworkModel::removeCover);
    QObject::connect(m_artworkFinder, &ArtworkFinder::coverLoadProgress, m_model, &ArtworkModel::updateCoverProgress);
    QObject::connect(m_artworkFinder, &ArtworkFinder::searchFinished, this, &ArtworkDialog::searchFinished);

    QObject::connect(m_startSearch, &QPushButton::clicked, this, &ArtworkDialog::searchArtwork);

    auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
    QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);

    if(auto* okButton = buttonBox->button(QDialogButtonBox::Ok)) {
        okButton->setDisabled(true);
        QObject::connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this,
                         [okButton](const QItemSelection& selected, const QItemSelection& /*deselected*/) {
                             okButton->setDisabled(selected.empty());
                         });
    }

    static const std::map<Track::Cover, QString> artworkOptions = {
        {Track::Cover::Front, u"Front"_s} /*, {Track::Cover::Front, u"Back"_s}*/, {Track::Cover::Artist, u"Artist"_s}};

    for(const auto& [option, title] : artworkOptions) {
        m_searchType->addItem(title, QVariant::fromValue(option));
    }

    if(artworkOptions.contains(m_type)) {
        m_searchType->setCurrentText(artworkOptions.at(m_type));
    }

    auto* paramsLayout = new QGridLayout();

    paramsLayout->addWidget(new QLabel(tr("Artist") + ":"_L1, this), 0, 0);
    paramsLayout->addWidget(m_artist, 0, 1);
    paramsLayout->addWidget(new QLabel(tr("Album") + ":"_L1, this), 1, 0);
    paramsLayout->addWidget(m_album, 1, 1);
    paramsLayout->addWidget(new QLabel(tr("Type") + ":"_L1, this), 2, 0);
    paramsLayout->addWidget(m_searchType, 2, 1);
    paramsLayout->addWidget(m_startSearch, 0, 3, 3, 1);

    m_startSearch->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);

    auto* layout = new QGridLayout(this);

    int row{0};
    layout->addLayout(paramsLayout, row++, 0, 1, 2);
    layout->addWidget(m_view, row++, 0, 1, 2);
    layout->addWidget(m_status, row, 0);
    layout->addWidget(buttonBox, row++, 1);

    QObject::connect(m_searchType, &QComboBox::currentIndexChanged, this,
                     [this]() { m_type = m_searchType->currentData().value<Track::Cover>(); });

    searchArtwork();
}

void ArtworkDialog::accept()
{
    const auto selection = m_view->selectionModel()->selectedIndexes();
    if(selection.empty()) {
        return;
    }

    auto result = selection.front().data(ArtworkItem::Result).value<ArtworkResult>();
    if(result.image.isNull()) {
        QDialog::accept();
        return;
    }

    const auto& method    = m_saveMethods.value(m_type);
    const auto saveResult = prepareArtworkForSave(result, method.format, method.quality);

    if(method.method == ArtworkSaveMethod::Embedded) {
        TrackCoverData coverData;
        coverData.tracks = m_tracks;
        coverData.coverData.emplace(m_type, CoverImage{.mimeType = saveResult.mimeType, .data = saveResult.image});
        m_library->writeTrackCovers(coverData).finished.then(
            this, [this, tracks = m_tracks](const WriteResult& /*result*/) {
                for(const Track& track : tracks) {
                    m_coverRepository->removeFromCache(track, *m_settings);
                }
            });
    }
    else {
        const QString path
            = m_parser.evaluate(u"%1/%2.%3"_s.arg(method.dir, method.filename, saveResult.suffix), m_tracks.front());
        const QString cleanPath = QDir::cleanPath(path);

        QFile file{cleanPath};
        if(file.open(QIODevice::WriteOnly) && file.write(saveResult.image) == saveResult.image.size()) {
            for(const Track& track : m_tracks) {
                m_coverRepository->removeFromCache(track, *m_settings);
            }
        }
    }

    QDialog::accept();
}

QSize ArtworkDialog::sizeHint() const
{
    return {800, 800};
}

void ArtworkDialog::keyPressEvent(QKeyEvent* event)
{
    if(m_artist->hasFocus() || m_album->hasFocus()) {
        m_startSearch->click();
    }
    else {
        QDialog::keyPressEvent(event);
    }
}

void ArtworkDialog::searchArtwork()
{
    const Track& track = m_tracks.front();

    const QString artist = m_artist->text();
    const QString album  = m_album->text();
    const QString title  = m_parser.evaluate(m_settings->value<Settings::Gui::Internal::ArtworkTitleField>(), track);

    m_model->clear();

    m_artworkFinder->findArtwork(m_type, artist, album, title);
    m_status->setText(tr("Searching…"));
}

void ArtworkDialog::searchFinished()
{
    if(m_model->rowCount({}) == 0) {
        m_status->setText(tr("No artwork found"));
    }
    else {
        m_status->clear();
    }
}
} // namespace Fooyin
