/* ContextMenu.cpp */

/* 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 "ContextMenu.h"
#include "Gui/Utils/Icons.h"
#include "Gui/Utils/GuiUtils.h"
#include "Gui/Utils/PreferenceAction.h"

#include "Utils/Algorithm.h"
#include "Utils/Language/Language.h"

#include <QTimer>
#include <QKeySequence>

namespace Gui
{
	struct ContextMenu::Private
	{
		QAction* actionApply = nullptr;
		QAction* actionDefault = nullptr;
		QAction* actionDelete = nullptr;
		QAction* actionEdit = nullptr;
		QAction* actionNew = nullptr;
		QAction* actionOpen = nullptr;
		QAction* actionRename = nullptr;
		QAction* actionSave = nullptr;
		QAction* actionSaveAs = nullptr;
		QAction* actionUndo = nullptr;
		QAction* preferencePseparator = nullptr;

		QList<QAction*> actions;
		QTimer* timer;

		bool hasSpecialActions {false};
		bool hasPreferenceActions {false};

		explicit Private(QObject* parent) :
			actionApply {new QAction(parent)},
			actionDefault {new QAction(parent)},
			actionDelete {new QAction(parent)},
			actionEdit {new QAction(parent)},
			actionNew {new QAction(parent)},
			actionOpen {new QAction(parent)},
			actionRename {new QAction(parent)},
			actionSave {new QAction(parent)},
			actionSaveAs {new QAction(parent)},
			actionUndo {new QAction(parent)},
			timer {new QTimer(parent)} {}
	};

	ContextMenu::ContextMenu(QWidget* parent) :
		Gui::WidgetTemplate<QMenu>(parent),
		m {Pimpl::make<Private>(this)}
	{
		m->actions << addSeparator()
		           << m->actionNew
		           << m->actionOpen
		           << m->actionEdit
		           << m->actionApply
		           << m->actionSave
		           << m->actionSaveAs
		           << m->actionRename
		           << addSeparator()
		           << m->actionUndo
		           << m->actionDefault
		           << addSeparator()
		           << m->actionDelete
		           << addSeparator();

		addActions(m->actions);

		for(auto* action: qAsConst(m->actions))
		{
			action->setVisible(false);
		}

		connect(m->actionApply, &QAction::triggered, this, &ContextMenu::sigApply);
		connect(m->actionDefault, &QAction::triggered, this, &ContextMenu::sigDefault);
		connect(m->actionDelete, &QAction::triggered, this, &ContextMenu::sigDelete);
		connect(m->actionEdit, &QAction::triggered, this, &ContextMenu::sigEdit);
		connect(m->actionNew, &QAction::triggered, this, &ContextMenu::sigNew);
		connect(m->actionOpen, &QAction::triggered, this, &ContextMenu::sigOpen);
		connect(m->actionRename, &QAction::triggered, this, &ContextMenu::sigRename);
		connect(m->actionSave, &QAction::triggered, this, &ContextMenu::sigSave);
		connect(m->actionSaveAs, &QAction::triggered, this, &ContextMenu::sigSaveAs);
		connect(m->actionUndo, &QAction::triggered, this, &ContextMenu::sigUndo);
	}

	ContextMenu::~ContextMenu() = default;

	void ContextMenu::languageChanged()
	{
		m->actionApply->setText(Lang::get(Lang::Apply));
		m->actionNew->setText(Lang::get(Lang::New));
		m->actionEdit->setText(Lang::get(Lang::Edit));
		m->actionOpen->setText(Lang::get(Lang::Open));
		m->actionSave->setText(Lang::get(Lang::Save));
		m->actionSaveAs->setText(Lang::get(Lang::SaveAs).triplePt());
		m->actionRename->setText(Lang::get(Lang::Rename));
		m->actionUndo->setText(Lang::get(Lang::Undo));
		m->actionDefault->setText(Lang::get(Lang::Default));
		m->actionDelete->setText(Lang::get(Lang::Delete));

		m->actionOpen->setShortcut(QKeySequence::Open);
		m->actionNew->setShortcut(QKeySequence::New);
		m->actionUndo->setShortcut(QKeySequence::Undo);
		m->actionSave->setShortcut(QKeySequence::Save);
		m->actionSaveAs->setShortcut(QKeySequence::SaveAs);
		m->actionRename->setShortcut(QKeySequence("F2"));
		m->actionDelete->setShortcut(QKeySequence::Delete);
	}

	void ContextMenu::skinChanged()
	{
		m->actionDefault->setIcon(Gui::icon(Gui::Undo));
		m->actionDelete->setIcon(Gui::icon(Gui::Delete));
		m->actionEdit->setIcon(Gui::icon(Gui::Edit));
		m->actionNew->setIcon(Gui::icon(Gui::New));
		m->actionOpen->setIcon(Gui::icon(Gui::Open));
		m->actionRename->setIcon(Gui::icon(Gui::Edit));
		m->actionSave->setIcon(Gui::icon(Gui::Save));
		m->actionSaveAs->setIcon(Gui::icon(Gui::SaveAs));
		m->actionUndo->setIcon(Gui::icon(Gui::Undo));
	}

	void ContextMenu::registerAction(QAction* action)
	{
		m->actions << action;

		if(!m->hasSpecialActions)
		{
			auto* sep = addSeparator();
			insertAction(m->preferencePseparator, sep);
			m->hasSpecialActions = true;
		}

		if(m->preferencePseparator)
		{
			insertAction(m->preferencePseparator, action);
		}

		else
		{
			addAction(action);
		}
	}

	void ContextMenu::showActions(ContextMenuEntries entries)
	{
		m->actionApply->setVisible(entries & ContextMenu::EntryApply);
		m->actionNew->setVisible(entries & ContextMenu::EntryNew);
		m->actionEdit->setVisible(entries & ContextMenu::EntryEdit);
		m->actionOpen->setVisible(entries & ContextMenu::EntryOpen);
		m->actionUndo->setVisible(entries & ContextMenu::EntryUndo);
		m->actionDefault->setVisible(entries & ContextMenu::EntryDefault);
		m->actionSave->setVisible(entries & ContextMenu::EntrySave);
		m->actionSaveAs->setVisible(entries & ContextMenu::EntrySaveAs);
		m->actionRename->setVisible(entries & ContextMenu::EntryRename);
		m->actionDelete->setVisible(entries & ContextMenu::EntryDelete);
	}

	void ContextMenu::showAction(const ContextMenu::Entry entry, const bool visible)
	{
		if(visible)
		{
			showActions(entries() | entry);
		}

		else
		{
			showActions(entries() & ~entry);
		}
	}

	bool ContextMenu::hasActions()
	{
		return ::Util::Algorithm::contains(m->actions, [](auto* a) {
			return a->isVisible();
		});
	}

	ContextMenuEntries ContextMenu::entries() const
	{
		auto entries = static_cast<ContextMenuEntries>(ContextMenu::EntryNone);

		if(m->actionApply->isVisible())
		{
			entries |= ContextMenu::EntryApply;
		}
		if(m->actionNew->isVisible())
		{
			entries |= ContextMenu::EntryNew;
		}
		if(m->actionEdit->isVisible())
		{
			entries |= ContextMenu::EntryEdit;
		}
		if(m->actionDelete->isVisible())
		{
			entries |= ContextMenu::EntryDelete;
		}
		if(m->actionOpen->isVisible())
		{
			entries |= ContextMenu::EntryOpen;
		}
		if(m->actionRename->isVisible())
		{
			entries |= ContextMenu::EntryRename;
		}
		if(m->actionSave->isVisible())
		{
			entries |= ContextMenu::EntrySave;
		}
		if(m->actionSaveAs->isVisible())
		{
			entries |= ContextMenu::EntrySaveAs;
		}
		if(m->actionUndo->isVisible())
		{
			entries |= ContextMenu::EntryUndo;
		}
		if(m->actionDefault->isVisible())
		{
			entries |= ContextMenu::EntryDefault;
		}

		return entries;
	}

	void ContextMenu::showAll()
	{
		for(auto* action: qAsConst(m->actions))
		{
			action->setVisible(true);
		}
	}

	void ContextMenu::addPreferenceAction(Gui::PreferenceAction* action)
	{
		QList<QAction*> actions;

		if(!m->hasPreferenceActions)
		{
			m->preferencePseparator = addSeparator();
			actions << m->preferencePseparator;
			m->hasPreferenceActions = true;
		}

		actions << action;

		addActions(actions);
	}

	void ContextMenu::showEvent(QShowEvent* e)
	{
		for(auto* action: qAsConst(m->actions))
		{
			action->setDisabled(true);
		}

		QTimer::singleShot(200, this, &ContextMenu::timedOut); // NOLINT(*-magic-numbers)

		WidgetTemplate<QMenu>::showEvent(e);
	}

	void ContextMenu::timedOut()
	{
		for(auto* action: qAsConst(m->actions))
		{
			action->setDisabled(false);
		}
	}
}

