/*
 * Cantata
 *
 * Copyright (c) 2011-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 detailexampleSong.
 *
 * 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 "albumdetailsdialog.h"
#include "audiocddevice.h"
#include "cdalbum.h"
#include "gui/coverdialog.h"
#include "models/devicesmodel.h"
#include "models/mpdlibrarymodel.h"
#include "models/musiclibraryitemsong.h"
#include "support/inputdialog.h"
#include "support/lineedit.h"
#include "support/messagebox.h"
#include "widgets/basicitemdelegate.h"
#include "widgets/icons.h"
#include <QMenu>
#include <QMouseEvent>
#include <QSpinBox>
#include <QStyledItemDelegate>
#include <algorithm>

enum Columns {
	COL_TRACK,
	COL_ARTIST,
	COL_TITLE
};

class EditorDelegate : public BasicItemDelegate {
public:
	EditorDelegate(QObject* parent = 0) : BasicItemDelegate(parent) {}

	QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
	{
		Q_UNUSED(option)
		if (COL_TRACK == index.column()) {
			QSpinBox* editor = new QSpinBox(parent);
			editor->setMinimum(0);
			editor->setMaximum(500);
			return editor;
		}
		else {
			parent->setProperty("cantata-delegate", true);
			return new LineEdit(parent);
		}
	}

	QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
	{
		return QStyledItemDelegate::sizeHint(option, index) + QSize(0, 4);
	}

	void setEditorData(QWidget* editor, const QModelIndex& index) const
	{
		if (COL_TRACK == index.column()) {
			static_cast<QSpinBox*>(editor)->setValue(index.model()->data(index, Qt::EditRole).toInt());
		}
		else {
			static_cast<LineEdit*>(editor)->setText(index.model()->data(index, Qt::EditRole).toString());
		}
	}
	void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
	{
		if (COL_TRACK == index.column()) {
			model->setData(index, static_cast<QSpinBox*>(editor)->value(), Qt::EditRole);
		}
		else {
			model->setData(index, static_cast<LineEdit*>(editor)->text().trimmed(), Qt::EditRole);
		}
	}

	void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
	{
		Q_UNUSED(index)
		editor->setGeometry(option.rect);
	}
};

static int iCount = 0;

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

AlbumDetailsDialog::AlbumDetailsDialog(QWidget* parent)
	: Dialog(parent, "AlbumDetailsDialog"), pressed(false)
{
	iCount++;
	setButtons(User1 | Ok | Cancel);
	setCaption(tr("Audio CD"));
	setAttribute(Qt::WA_DeleteOnClose);

	QWidget* mainWidet = new QWidget(this);
	setupUi(mainWidet);
	setMainWidget(mainWidet);

	QSet<QString> artists;
	QSet<QString> albumArtists;
	QSet<QString> composers;
	QSet<QString> albums;
	QSet<QString> genres;
	MpdLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres);

	QStringList strings = albumArtists.values();
	strings.sort();
	artist->clear();
	artist->insertItems(0, strings);

	strings = composers.values();
	strings.sort();
	composer->clear();
	composer->insertItems(0, strings);

	strings = albums.values();
	strings.sort();
	title->clear();
	title->insertItems(0, strings);

	strings = genres.values();
	strings.sort();
	genre->clear();
	genre->insertItems(0, strings);

	QMenu* toolsMenu = new QMenu(this);
	toolsMenu->addAction(tr("Apply \"Various Artists\" Workaround"), this, SLOT(applyVa()));
	toolsMenu->addAction(tr("Revert \"Various Artists\" Workaround"), this, SLOT(revertVa()));
	toolsMenu->addAction(tr("Capitalize"), this, SLOT(capitalise()));
	toolsMenu->addAction(tr("Adjust Track Numbers"), this, SLOT(adjustTrackNumbers()));
	setButtonMenu(User1, toolsMenu, InstantPopup);
	setButtonGuiItem(User1, GuiItem(tr("Tools"), "tools-wizard"));
	connect(singleArtist, SIGNAL(toggled(bool)), SLOT(hideArtistColumn(bool)));
	resize(600, 600);

	int size = fontMetrics().height() * 5;
	cover->setMinimumSize(size, size);
	cover->setMaximumSize(size, size);
	setCover();
	cover->installEventFilter(this);
	tracks->setItemDelegate(new EditorDelegate(tracks));
}

AlbumDetailsDialog::~AlbumDetailsDialog()
{
	iCount--;
}

void AlbumDetailsDialog::show(AudioCdDevice* dev)
{
	udi = dev->id();
	artist->setText(dev->albumArtist());
	composer->setText(dev->albumComposer());
	title->setText(dev->albumName());
	genre->setText(dev->albumGenre());
	disc->setValue(dev->albumDisc());
	year->setValue(dev->albumYear());
	tracks->clear();
	QSet<QString> artists;
	artists.insert(dev->albumArtist());

	QList<Song> songs;
	for (const MusicLibraryItem* i : dev->childItems()) {
		songs.append(static_cast<const MusicLibraryItemSong*>(i)->song());
	}
	std::sort(songs.begin(), songs.end());

	for (const Song& s : songs) {
		QTreeWidgetItem* item = new QTreeWidgetItem(tracks);
		update(item, s);
		artists.insert(s.artist);
		item->setFlags(item->flags() | Qt::ItemIsEditable);
	}

	singleArtist->setChecked(1 == artists.count());
	coverImage = dev->cover();
	if (!coverImage.img.isNull()) {
		cover->setPixmap(QPixmap::fromImage(coverImage.img.scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
	}
	Dialog::show();
}

void AlbumDetailsDialog::slotButtonClicked(int button)
{
	switch (button) {
	case Ok: {
		Device* dev = DevicesModel::self()->device(udi);
		if (dev && Device::AudioCd == dev->devType()) {
			CdAlbum cdAlbum = getAlbum();
			for (int i = 0; i < tracks->topLevelItemCount(); ++i) {
				cdAlbum.tracks.append(toSong(tracks->topLevelItem(i), cdAlbum));
			}
			static_cast<AudioCdDevice*>(dev)->setDetails(cdAlbum);
			static_cast<AudioCdDevice*>(dev)->setCover(coverImage);
		}
		accept();
		break;
	}
	case Cancel:
		reject();
		break;
	default:
		break;
	}

	if (Ok == button) {
		accept();
	}

	Dialog::slotButtonClicked(button);
}

void AlbumDetailsDialog::hideArtistColumn(bool hide)
{
	tracks->header()->setSectionHidden(COL_ARTIST, hide);
}

void AlbumDetailsDialog::applyVa()
{
	if (MessageBox::No == MessageBox::questionYesNo(this, tr("Apply \"Various Artists\" workaround?") + QLatin1String("\n\n") + tr("This will set 'Album artist' and 'Artist' to "
	                                                                                                                               "\"Various Artists\", and set 'Title' to "
	                                                                                                                               "\"TrackArtist - TrackTitle\""),
	                                                tr("Apply \"Various Artists\" Workaround"), StdGuiItem::apply(), StdGuiItem::cancel())) {
		return;
	}

	CdAlbum album = getAlbum();
	for (int i = 0; i < tracks->topLevelItemCount(); ++i) {
		QTreeWidgetItem* itm = tracks->topLevelItem(i);
		Song s = toSong(itm, album);
		if (s.fixVariousArtists()) {
			update(itm, s);
		}
	}
}

void AlbumDetailsDialog::revertVa()
{
	if (MessageBox::No == MessageBox::questionYesNo(this, tr("Revert \"Various Artists\" workaround?") + QLatin1String("\n\n") + tr("Where the 'Album artist' is the same as 'Artist' "
	                                                                                                                                "and the 'Title' is of the format \"TrackArtist - TrackTitle\", "
	                                                                                                                                "'Artist' will be taken from 'Title' and 'Title' itself will be "
	                                                                                                                                "set to just the title. e.g. \n"
	                                                                                                                                "If 'Title' is \"Wibble - Wobble\", then 'Artist' will be set to "
	                                                                                                                                "\"Wibble\" and 'Title' will be set to \"Wobble\""),
	                                                tr("Revert \"Various Artists\" Workaround"), GuiItem(tr("Revert")), StdGuiItem::cancel())) {
		return;
	}

	CdAlbum album = getAlbum();
	for (int i = 0; i < tracks->topLevelItemCount(); ++i) {
		QTreeWidgetItem* itm = tracks->topLevelItem(i);
		Song s = toSong(itm, album);
		if (s.revertVariousArtists()) {
			update(itm, s);
		}
	}
}

void AlbumDetailsDialog::capitalise()
{
	if (MessageBox::No == MessageBox::questionYesNo(this, tr("Capitalize the first letter of 'Title', 'Artist', 'Album artist', and 'Album'?"), tr("Capitalize"), GuiItem(tr("Capitalize")), StdGuiItem::cancel())) {
		return;
	}

	CdAlbum album = getAlbum();
	for (int i = 0; i < tracks->topLevelItemCount(); ++i) {
		QTreeWidgetItem* itm = tracks->topLevelItem(i);
		Song s = toSong(itm, album);
		if (s.capitalise()) {
			update(itm, s);
		}
	}
}

void AlbumDetailsDialog::adjustTrackNumbers()
{
	bool ok = false;
	int adj = InputDialog::getInteger(tr("Adjust Track Numbers"), tr("Adjust track number by:"), 0, -500, 500, 1, 10, &ok, this);

	if (!ok || 0 == adj) {
		return;
	}

	CdAlbum album = getAlbum();
	for (int i = 0; i < tracks->topLevelItemCount(); ++i) {
		QTreeWidgetItem* itm = tracks->topLevelItem(i);
		Song s = toSong(itm, album);
		s.track += adj;
		update(itm, s);
	}
}

enum Roles {
	Role_Id = Qt::UserRole,
	Role_File,
	Role_Time
};

Song AlbumDetailsDialog::toSong(QTreeWidgetItem* i, const CdAlbum& album)
{
	Song s;
	s.albumartist = album.artist;
	s.album = album.name;
	s.genres[0] = album.genre;
	s.year = album.year;
	s.disc = album.disc;
	s.artist = singleArtist->isChecked() ? s.albumartist : i->text(COL_ARTIST);
	if (!album.composer.isEmpty()) {
		s.setComposer(album.composer);
	}
	s.title = i->text(COL_TITLE);
	s.track = i->text(COL_TRACK).toInt();
	s.id = i->data(0, Role_Id).toInt();
	s.file = i->data(0, Role_File).toString();
	s.time = i->data(0, Role_Time).toInt();
	s.fillEmptyFields();
	return s;
}

CdAlbum AlbumDetailsDialog::getAlbum() const
{
	CdAlbum cdAlbum;
	cdAlbum.artist = artist->text().trimmed();
	cdAlbum.composer = composer->text().trimmed();
	cdAlbum.name = title->text().trimmed();
	cdAlbum.disc = disc->value();
	cdAlbum.year = year->value();
	cdAlbum.genre = genre->text().trimmed();
	if (cdAlbum.artist.isEmpty()) {
		cdAlbum.artist = Song::unknown();
	}
	if (cdAlbum.name.isEmpty()) {
		cdAlbum.name = Song::unknown();
	}
	return cdAlbum;
}

void AlbumDetailsDialog::update(QTreeWidgetItem* i, const Song& s)
{
	i->setText(COL_TRACK, QString::number(s.track));
	i->setText(COL_ARTIST, s.artist);
	i->setText(COL_TITLE, s.title);
	i->setData(0, Role_Id, s.id);
	i->setData(0, Role_File, s.file);
	i->setData(0, Role_Time, s.time);
}

void AlbumDetailsDialog::setCover()
{
	int iconSize = cover->size().width() <= 128 ? 128 : 256;
	cover->setPixmap(Icons::self()->albumIcon(iconSize).pixmap(iconSize, iconSize).scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}

bool AlbumDetailsDialog::eventFilter(QObject* object, QEvent* event)
{
	switch (event->type()) {
	case QEvent::MouseButtonPress:
		if (Qt::LeftButton == static_cast<QMouseEvent*>(event)->button()) {
			pressed = true;
		}
		break;
	case QEvent::MouseButtonRelease:
		if (pressed && Qt::LeftButton == static_cast<QMouseEvent*>(event)->button()) {
			if (0 == CoverDialog::instanceCount()) {
				CoverDialog* dlg = new CoverDialog(this);
				connect(dlg, SIGNAL(selectedCover(QImage, QString)), this, SLOT(coverSelected(QImage, QString)));
				Song s;
				s.file = AudioCdDevice::coverUrl(udi);
				s.artist = artist->text().trimmed();
				s.album = title->text().trimmed();
				s.type = Song::Cdda;
				dlg->show(s, coverImage);
			}
		}
		pressed = false;
		break;
	default:
		break;
	}
	return QObject::eventFilter(object, event);
}

void AlbumDetailsDialog::coverSelected(const QImage& img, const QString& fileName)
{
	coverImage = Covers::Image(img, fileName);
	if (!coverImage.img.isNull()) {
		cover->setPixmap(QPixmap::fromImage(coverImage.img.scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
	}
}

#include "moc_albumdetailsdialog.cpp"
