/*
 * 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 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 "tableview.h"
#include "basicitemdelegate.h"
#include "models/roles.h"
#include "mpd-interface/song.h"
#include "ratingwidget.h"
#include "stretchheaderview.h"
#include "support/configuration.h"
#include <QAction>
#include <QActionGroup>
#include <QMenu>
#include <QPainter>
#include <QStyleOption>

class TableViewItemDelegate : public BasicItemDelegate {
public:
	TableViewItemDelegate(QObject* p, int rc) : BasicItemDelegate(p), ratingCol(rc), ratingPainter(nullptr) {}
	~TableViewItemDelegate() override { delete ratingPainter; }
	void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
	{
		if (!index.isValid()) {
			return;
		}

		QStyleOptionViewItem v4((QStyleOptionViewItem&)option);
		if (QStyleOptionViewItem::Beginning == v4.viewItemPosition) {
			v4.icon = index.data(Cantata::Role_Decoration).value<QIcon>();
			if (!v4.icon.isNull()) {
				v4.features |= QStyleOptionViewItem::HasDecoration;
				v4.decorationSize = v4.icon.actualSize(option.decorationSize, QIcon::Normal, QIcon::Off);
			}
		}

		BasicItemDelegate::paint(painter, v4, index);

		if (index.column() == ratingCol) {
			Song song = index.data(Cantata::Role_SongWithRating).value<Song>();
			if (song.rating > 0 && song.rating <= Song::Rating_Max) {
				const QRect& r = option.rect;
				if (!ratingPainter) {
					ratingPainter = new RatingPainter(r.height() - 4);
					ratingPainter->setColor(option.palette.color(QPalette::Active, QPalette::Text));
				}
				painter->save();
				painter->setOpacity(painter->opacity() * 0.75);
				painter->setClipRect(r);
				const QSize& ratingSize = ratingPainter->size();
				QRect ratingRect(r.x() + (r.width() - ratingSize.width()) / 2, r.y() + (r.height() - ratingSize.height()) / 2,
				                 ratingSize.width(), ratingSize.height());
				ratingPainter->paint(painter, ratingRect, song.rating);
				painter->restore();
			}
		}
	}

	int ratingCol;
	mutable RatingPainter* ratingPainter;
};

TableView::TableView(const QString& cfgGroup, QWidget* parent, bool menuAlwaysAllowed)
	: TreeView(parent, menuAlwaysAllowed), menu(nullptr), configGroup(cfgGroup), menuIsForCol(-1)
{
	setContextMenuPolicy(Qt::CustomContextMenu);
	setAcceptDrops(true);
	setDragDropOverwriteMode(false);
	setDragDropMode(QAbstractItemView::DragDrop);
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	setDropIndicatorShown(true);
	setUniformRowHeights(true);
	setAttribute(Qt::WA_MouseTracking, true);
	StretchHeaderView* hdr = new StretchHeaderView(Qt::Horizontal, this);
	setHeader(hdr);
	connect(hdr, SIGNAL(StretchEnabledChanged(bool)), SLOT(stretchToggled(bool)));
}

void TableView::setModel(QAbstractItemModel* m)
{
	if (dynamic_cast<TableViewItemDelegate*>(itemDelegate())) {
		itemDelegate()->deleteLater();
	}
	bool ok = false;
	int col = m ? m->data(QModelIndex(), Cantata::Role_RatingCol).toInt(&ok) : -1;
	if (ok && col >= 0) {
		setItemDelegate(new TableViewItemDelegate(this, col));
	}
	TreeView::setModel(m);
}

static const QLatin1String constHeaderKey("tableHeader");

void TableView::initHeader()
{
	if (!model()) {
		return;
	}

	StretchHeaderView* hdr = qobject_cast<StretchHeaderView*>(header());
	QList<int> hideable;
	QList<int> initiallyHidden;
	if (!menu) {
		hdr->SetStretchEnabled(true);
		stretchToggled(true);
		hdr->setContextMenuPolicy(Qt::CustomContextMenu);
		for (int i = 0; i < model()->columnCount(); ++i) {
			hdr->SetColumnWidth(i, model()->headerData(i, Qt::Horizontal, Cantata::Role_Width).toDouble());
			if (model()->headerData(i, Qt::Horizontal, Cantata::Role_Hideable).toBool()) {
				hideable.append(i);
			}
			if (model()->headerData(i, Qt::Horizontal, Cantata::Role_InitiallyHidden).toBool()) {
				initiallyHidden.append(i);
			}
		}
		hdr->setSectionsMovable(true);
		connect(hdr, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
	}

	//Restore state
	Configuration config(configGroup);
	QByteArray state = config.get(constHeaderKey, QByteArray());
	if (state.isEmpty()) {
		for (int i : initiallyHidden) {
			hdr->HideSection(i);
		}
	}
	else {
		hdr->RestoreState(state);
	}

	if (!menu) {
		menu = new QMenu(this);
		QAction* stretch = new QAction(tr("Stretch Columns To Fit Window"), this);
		stretch->setCheckable(true);
		stretch->setChecked(hdr->is_stretch_enabled());
		connect(stretch, SIGNAL(toggled(bool)), hdr, SLOT(SetStretchEnabled(bool)));
		menu->addAction(stretch);

		QMenu* alignmentMenu = new QMenu(menu);
		QActionGroup* alignGroup = new QActionGroup(alignmentMenu);
		alignLeftAction = new QAction(tr("Left"), alignGroup);
		alignCenterAction = new QAction(tr("Center"), alignGroup);
		alignRightAction = new QAction(tr("Right"), alignGroup);

		alignLeftAction->setCheckable(true);
		alignCenterAction->setCheckable(true);
		alignRightAction->setCheckable(true);
		alignmentMenu->addActions(alignGroup->actions());
		connect(alignGroup, SIGNAL(triggered(QAction*)), SLOT(alignmentChanged()));
		alignAction = new QAction(tr("Alignment"), menu);
		alignAction->setMenu(alignmentMenu);
		menu->addAction(alignAction);

		menu->addSeparator();
		for (int col : hideable) {
			QAction* act = new QAction(model()->headerData(col, Qt::Horizontal, Cantata::Role_ContextMenuText).toString(), menu);
			act->setCheckable(true);
			act->setChecked(!hdr->isSectionHidden(col));
			menu->addAction(act);
			act->setData(col);
			connect(act, SIGNAL(toggled(bool)), this, SLOT(toggleHeaderItem(bool)));
		}
	}
}

void TableView::saveHeader()
{
	if (menu && model()) {
		Configuration(configGroup).set(constHeaderKey, qobject_cast<StretchHeaderView*>(header())->SaveState());
	}
}

void TableView::scrollTo(const QModelIndex& index, ScrollHint hint)
{
	QHeaderView* hdr = header();

	// scrollTo does not work if column hidden, so find first one that is not.
	if (hdr && hdr->isSectionHidden(index.column())) {
		for (int i = 0; i < model()->columnCount(); ++i) {
			if (!hdr->isSectionHidden(i)) {
				TreeView::scrollTo(model()->index(index.row(), i, index.parent()), hint);
				return;
			}
		}
	}
	TreeView::scrollTo(index, hint);
}

void TableView::showMenu(const QPoint& pos)
{
	menuIsForCol = header()->logicalIndexAt(pos);
	alignAction->setEnabled(-1 != menuIsForCol);
	if (-1 != menuIsForCol) {
		Qt::Alignment al = (Qt::AlignmentFlag)model()->headerData(menuIsForCol, Qt::Horizontal, Qt::TextAlignmentRole).toInt();
		if (al & Qt::AlignLeft) {
			alignLeftAction->setChecked(true);
		}
		else if (al & Qt::AlignHCenter) {
			alignCenterAction->setChecked(true);
		}
		else if (al & Qt::AlignRight) {
			alignRightAction->setChecked(true);
		}
	}
	menu->exec(mapToGlobal(pos));
}

void TableView::toggleHeaderItem(bool visible)
{
	QAction* act = qobject_cast<QAction*>(sender());

	if (act) {
		int index = act->data().toInt();
		if (-1 != index) {
			qobject_cast<StretchHeaderView*>(header())->SetSectionHidden(index, !visible);
		}
	}
}

void TableView::stretchToggled(bool e)
{
	setHorizontalScrollBarPolicy(e ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
}

void TableView::alignmentChanged()
{
	if (-1 != menuIsForCol) {
		int al = alignLeftAction->isChecked()
				? Qt::AlignLeft
				: alignRightAction->isChecked()
				? Qt::AlignRight
				: Qt::AlignHCenter;
		if (model()->setHeaderData(menuIsForCol, Qt::Horizontal, al, Qt::TextAlignmentRole)) {
			header()->reset();
		}
	}
}

#include "moc_tableview.cpp"
