
/* Copyright (C) 2011-2024 Michael Lugmair (Lucio Carreras)
 *
 * This file is part of sayonara player
 *
 * 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 3 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.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "LibraryHeaderView.h"
#include "Utils/Algorithm.h"
#include "Utils/Logger/Logger.h"

#include "Gui/Utils/GuiUtils.h"

#include <QAction>
#include <QFontMetrics>
#include <QPair>

namespace Library
{
	using ColumnActionPair = QPair<ColumnHeaderPtr, QAction*>;
	using ColumnActionPairList = QList<ColumnActionPair>;

	namespace
	{
		int columnWidth(const Library::ColumnHeaderPtr& columnHeader, QWidget* widget)
		{
			return std::max(columnHeader->defaultSize(),
			                Gui::Util::textWidth(widget, columnHeader->title() + "MMM"));
		}

		void restoreActions(const ColumnActionPairList& columns, const QHeaderView* headerView)
		{
			auto i = 0;
			for(const auto& [columnHeader, action]: columns)
			{
				action->setCheckable(columnHeader->isSwitchable());
				action->setChecked(!headerView->isSectionHidden(i));
				i++;
			}
		}

		double getScaleFactor(QHeaderView* header, const ColumnActionPairList& columns)
		{
			auto spaceNeeded = 0;
			auto freeSpace = header->width();

			for(auto i = 0; i < columns.size(); i++)
			{
				if(header->isSectionHidden(i))
				{
					continue;
				}

				const auto& columnHeader = columns[i].first;
				const auto size = columnWidth(columnHeader, header);

				if(columnHeader->isStretchable())
				{
					spaceNeeded += size;
				}

				else
				{
					freeSpace -= size;
				}
			}

			return std::max(freeSpace * 1.0 / spaceNeeded, 1.0);
		}
	}

	struct HeaderView::Private
	{
		ColumnActionPairList columns;
		QAction* actionResize;
		QAction* actionAutoResize;

		QByteArray initialState;
		bool isInitialized {false};

		explicit Private(HeaderView* parent) :
			actionResize {new QAction(parent)},
			actionAutoResize {new QAction(parent)}
		{
			actionAutoResize->setCheckable(parent);
		}
	};

	HeaderView::HeaderView(Qt::Orientation orientation, QWidget* parent) :
		Gui::HeaderView(orientation, parent)
	{
		m = Pimpl::make<Private>(this);

		connect(m->actionResize, &QAction::triggered, this, &HeaderView::actionResizeTriggered);
		connect(m->actionAutoResize, &QAction::triggered, this, &HeaderView::actionAutoResizeTriggered);
		connect(this, &QHeaderView::sectionDoubleClicked, this, [this](int /*logicalIndex*/) {
			resizeColumnsAutomatically();
		});
	}

	HeaderView::~HeaderView() = default;

	void HeaderView::init(const ColumnHeaderList& columnHeaderList, const QByteArray& state,
	                      Library::VariableSortorder sortOrder, bool autoResizeState)
	{
		m->initialState = state;

		for(auto i = 0; i < columnHeaderList.size(); i++)
		{
			const auto& columnHeader = columnHeaderList[i];

			// action
			auto* action = new QAction(columnHeader->title());
			action->setCheckable(columnHeader->isSwitchable());
			action->setChecked(!isSectionHidden(i));

			connect(action, &QAction::toggled, this, &HeaderView::actionTriggered);
			addAction(action);

			// sorting
			if(sortOrder == columnHeader->sortorder(Qt::AscendingOrder))
			{
				setSortIndicator(i, Qt::AscendingOrder);
			}

			else if(sortOrder == columnHeader->sortorder(Qt::DescendingOrder))
			{
				setSortIndicator(i, Qt::DescendingOrder);
			}

			m->columns << ColumnActionPair(columnHeader, action);
		}

		auto* sep = new QAction();
		sep->setSeparator(true);

		addAction(sep);
		addAction(m->actionResize);
		addAction(m->actionAutoResize);

		m->actionAutoResize->setChecked(autoResizeState);
	}

	void HeaderView::initializeView()
	{
		setMinimumSectionSize(25); // NOLINT(readability-magic-numbers)
		setSectionResizeMode(QHeaderView::ResizeMode::Interactive);
		setCascadingSectionResizes(false);
		setSectionsClickable(true);
		setSectionsMovable(true);
		setHighlightSections(true);
		setContextMenuPolicy(Qt::ActionsContextMenu);
		setTextElideMode(Qt::TextElideMode::ElideRight);
		setSectionResizeMode(QHeaderView::ResizeMode::Interactive);
		setStretchLastSection(true);
		setSortIndicatorShown(true);

		if(m->initialState.isEmpty())
		{
			resizeColumnsAutomatically();
		}

		else
		{
			restoreState(m->initialState);
			m->initialState.clear();

			restoreActions(m->columns, this);
		}

		m->isInitialized = true;
	}

	Library::VariableSortorder HeaderView::sortorder(int index, Qt::SortOrder sortOrder)
	{
		const auto& columnHeader = m->columns[index].first;
		return columnHeader->sortorder(sortOrder);
	}

	QString HeaderView::columnText(int index) const
	{
		return Util::between(index, m->columns)
		       ? m->columns[index].first->title()
		       : QString();
	}

	void HeaderView::actionTriggered(bool b)
	{
		auto* action = dynamic_cast<QAction*>(sender());
		const auto index = actions().indexOf(action);
		if(index >= 0)
		{
			setSectionHidden(index, !b);
		}

		actionResizeTriggered();
	}

	void HeaderView::actionResizeTriggered()
	{
		resizeColumnsAutomatically();
	}

	void HeaderView::actionAutoResizeTriggered(bool b)
	{
		emit sigAutoResizeToggled(b);
	}

	void HeaderView::resizeColumnsAutomatically()
	{
		const auto scaleFactor = getScaleFactor(this, m->columns);

		for(auto i = 0; i < m->columns.size(); i++)
		{
			const auto& columnHeader = m->columns[i].first;
			const auto size = columnWidth(columnHeader, this);

			if(!isSectionHidden(i))
			{
				const auto stretchedSize = columnHeader->isStretchable()
				                           ? static_cast<int>(size * scaleFactor)
				                           : size;

				resizeSection(i, stretchedSize);
			}
		}
	}

	void HeaderView::reloadColumnTexts()
	{
		for(auto i = 0; i < m->columns.size(); i++)
		{
			auto* action = m->columns[i].second;
			action->setText(columnText(i));
		}
	}

	void HeaderView::languageChanged()
	{
		m->actionResize->setText(tr("Resize columns"));
		m->actionAutoResize->setText(tr("Resize columns automatically"));

		reloadColumnTexts();
	}

	void HeaderView::showEvent(QShowEvent* e)
	{
		Gui::HeaderView::showEvent(e);

		if(!m->isInitialized)
		{
			initializeView();
		}
	}

	void HeaderView::resizeEvent(QResizeEvent* e)
	{
		Gui::HeaderView::resizeEvent(e);

		if(m && m->actionAutoResize->isChecked() && m->initialState.isEmpty())
		{
			resizeColumnsAutomatically();
		}
	}
}