/*
 * Cantata
 *
 * Copyright (c) 2017-2022 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 "config.h"
#include "mpdlibrarymodel.h"
#include "db/mpdlibrarydb.h"
#include "gui/covers.h"
#include "gui/settings.h"
#ifdef BUNDLED_KCATEGORIZEDVIEW
#include "kcategorizedview/kcategorizedsortfilterproxymodel.h"
#else
#include <KCategorizedSortFilterProxyModel>
#endif
#include "roles.h"
#include "support/configuration.h"
#include "support/globalstatic.h"
#include <QDebug>
#include <QTimer>

GLOBAL_STATIC(MpdLibraryModel, instance)
#define COVERS_DBUG \
	if (Covers::verboseDebugEnabled()) qWarning() << metaObject()->className() << QThread::currentThread()->objectName() << __FUNCTION__

MpdLibraryModel::MpdLibraryModel()
	: SqlLibraryModel(new MpdLibraryDb(nullptr), nullptr), showArtistImages(false)
{
	connect(Covers::self(), SIGNAL(cover(Song, QImage, QString)), this, SLOT(cover(Song, QImage, QString)));
	connect(Covers::self(), SIGNAL(coverUpdated(Song, QImage, QString)), this, SLOT(coverUpdated(Song, QImage, QString)));
	connect(Covers::self(), SIGNAL(artistImage(Song, QImage, QString)), this, SLOT(artistImage(Song, QImage, QString)));
	connect(Covers::self(), SIGNAL(composerImage(Song, QImage, QString)), this, SLOT(artistImage(Song, QImage, QString)));
	if (MPDConnection::self()->isConnected()) {
		static_cast<MpdLibraryDb*>(db)->connectionChanged(MPDConnection::self()->getDetails());
	}
}

QVariant MpdLibraryModel::data(const QModelIndex& index, int role) const
{
	if (!index.isValid()) {
		return QVariant();
	}

	switch (role) {
	case Cantata::Role_ListImage: {
		Item* item = static_cast<Item*>(index.internalPointer());
		return T_Album == item->getType() || (T_Artist == item->getType() && useArtistImages());
	}
	case Cantata::Role_GridCoverSong:
	case Cantata::Role_CoverSong: {
		QVariant v;
		Item* item = static_cast<Item*>(index.internalPointer());
		COVERS_DBUG << index.data().toString() << item->getType() << role;
		switch (item->getType()) {
		case T_Album:
			if (item->getSong().isEmpty()) {
				Song song = static_cast<MpdLibraryDb*>(db)->getCoverSong(T_Album == topLevel() ? static_cast<AlbumItem*>(item)->getArtistId() : item->getParent()->getId(), item->getId());
				if (song.isEmpty()) {
					COVERS_DBUG << "Failed to locate album cover song";
					return QVariant();
				}
				item->setSong(song);
				COVERS_DBUG << "Set album cover song" << song.albumArtist() << song.album << song.file;
				if (T_Genre == topLevel()) {
					song.addGenre(item->getParent()->getParent()->getId());
				}
			}
			v.setValue(item->getSong());
			break;
		case T_Artist:
			if (!showArtistImages && Cantata::Role_CoverSong == role) {
				COVERS_DBUG << "Not showing artist images";
				return QVariant();
			}
			if (item->getSong().isEmpty()) {
				Song song = static_cast<MpdLibraryDb*>(db)->getCoverSong(item->getId());
				if (song.isEmpty()) {
					COVERS_DBUG << "Failed to locate artist cover song";
					return QVariant();
				}
				COVERS_DBUG << "Set artist cover song" << song.albumArtist() << song.album << song.file;
				if (song.useComposer()) {
					song.setComposerImageRequest();
				}
				else {
					song.setArtistImageRequest();
				}
				if (T_Genre == topLevel()) {
					song.addGenre(item->getParent()->getId());
				}
				item->setSong(song);
			}
			v.setValue(item->getSong());
			break;
		default:
			break;
		}
		return v;
	}
	case KCategorizedSortFilterProxyModel::CategoryDisplayRole: {
		Item* item = static_cast<Item*>(index.internalPointer());
		if (T_Album == item->getType()) {
			int cat = static_cast<AlbumItem*>(item)->getCategory();
			return cat >= 0 && cat < categories.length() ? categories.at(cat) : "";
		}
		break;
	}
	case KCategorizedSortFilterProxyModel::CategorySortRole: {
		Item* item = static_cast<Item*>(index.internalPointer());
		if (T_Album == item->getType()) {
			return (long long)static_cast<AlbumItem*>(item)->getCategory();
		}
		break;
	}
	}
	return SqlLibraryModel::data(index, role);
}

void MpdLibraryModel::setUseArtistImages(bool u)
{
	if (u != showArtistImages) {
		showArtistImages = u;
		switch (topLevel()) {
		case T_Genre: {
			for (const Item* g : root->getChildren()) {
				const CollectionItem* genre = static_cast<const CollectionItem*>(g);
				if (genre->getChildCount()) {
					QModelIndex idx = index(genre->getRow(), 0, QModelIndex());
					emit dataChanged(index(0, 0, idx), index(genre->getChildCount() - 1, 0, idx));
				}
			}
			break;
		}
		case T_Artist:
			if (root->getChildCount()) {
				emit dataChanged(index(0, 0, QModelIndex()), index(root->getChildCount() - 1, 0, QModelIndex()));
			}
			break;
		default:
			break;
		}
	}
}

static QLatin1String constUseArtistImagesKey("artistImages");

void MpdLibraryModel::load(Configuration& config)
{
	showArtistImages = config.get(constUseArtistImagesKey, showArtistImages);
	SqlLibraryModel::load(config);
}

void MpdLibraryModel::save(Configuration& config)
{
	config.set(constUseArtistImagesKey, showArtistImages);
	SqlLibraryModel::save(config);
}

void MpdLibraryModel::listSongs()
{
	listingTotal = db->trackCount();
	listingCurrent = 0;
	if (listingTotal > 0) {
		QTimer::singleShot(0, this, SLOT(listNextChunk()));
	}
	else {
		emit songListing(QList<Song>(), 100.0);
	}
}

void MpdLibraryModel::cancelListing()
{
	listingTotal = 0;
}

static const int constMaxSongsInList = 1000;

void MpdLibraryModel::listNextChunk()
{
	if (listingTotal <= 0) {
		return;
	}

	QList<Song> songs = db->getTracks(listingCurrent, constMaxSongsInList);
	listingCurrent += songs.count();
	emit songListing(songs, (listingCurrent * 100.0) / (listingTotal * 1.0));
	if (songs.count() > 0) {
		if (listingCurrent == listingTotal) {
			emit songListing(QList<Song>(), 100.0);
		}
		else {
			QTimer::singleShot(0, this, SLOT(listNextChunk()));
		}
	}
}

void MpdLibraryModel::cover(const Song& song, const QImage& img, const QString& file)
{
	if (file.isEmpty() || img.isNull() || song.isFromOnlineService()) {
		return;
	}
	switch (topLevel()) {
	case T_Genre: {
		const Item* genre = root ? root->getChild(song.genres[0]) : nullptr;
		if (genre) {
			const Item* artist = static_cast<const CollectionItem*>(genre)->getChild(song.albumArtistOrComposer());
			if (artist) {
				const Item* album = static_cast<const CollectionItem*>(artist)->getChild(song.albumId());
				if (album) {
					QModelIndex idx = index(album->getRow(), 0, index(artist->getRow(), 0, index(genre->getRow(), 0, QModelIndex())));
					emit dataChanged(idx, idx);
				}
			}
		}
		break;
	}
	case T_Artist: {
		const Item* artist = root ? root->getChild(song.albumArtistOrComposer()) : nullptr;
		if (artist) {
			const Item* album = static_cast<const CollectionItem*>(artist)->getChild(song.albumId());
			if (album) {
				QModelIndex idx = index(album->getRow(), 0, index(artist->getRow(), 0, QModelIndex()));
				emit dataChanged(idx, idx);
			}
		}
		break;
	}
	case T_Album: {
		const Item* album = root ? root->getChild(song.albumArtistOrComposer() + song.albumId()) : nullptr;
		if (album) {
			QModelIndex idx = index(album->getRow(), 0, QModelIndex());
			emit dataChanged(idx, idx);
		}
		break;
	}
	default:
		break;
	}
}

void MpdLibraryModel::coverUpdated(const Song& song, const QImage& img, const QString& file)
{
	if (file.isEmpty() || img.isNull() || (T_Album == topLevel() && (song.isArtistImageRequest() || song.isComposerImageRequest()))) {
		return;
	}
	cover(song, img, file);
}

void MpdLibraryModel::artistImage(const Song& song, const QImage& img, const QString& file)
{
	if (file.isEmpty() || img.isNull() || T_Album == topLevel()) {
		return;
	}
	switch (topLevel()) {
	case T_Genre: {
		const Item* genre = root ? root->getChild(song.genres[0]) : nullptr;
		if (genre) {
			const Item* artist = static_cast<const CollectionItem*>(genre)->getChild(song.albumArtistOrComposer());
			if (artist) {
				QModelIndex idx = index(artist->getRow(), 0, index(genre->getRow(), 0, QModelIndex()));
				emit dataChanged(idx, idx);
			}
		}
		break;
	}
	case T_Artist: {
		const Item* artist = root ? root->getChild(song.albumArtistOrComposer()) : nullptr;
		if (artist) {
			QModelIndex idx = index(artist->getRow(), 0, QModelIndex());
			emit dataChanged(idx, idx);
		}
		break;
	}
	default:
		break;
	}
}

#include "moc_mpdlibrarymodel.cpp"
