/* VisualPlugin.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 "VisualPlugin.h"
#include "Components/Visualization/VisualColorStyleChooser.h"
#include "Components/PlayManager/PlayManager.h"
#include "Gui/Utils/GuiUtils.h"

struct VisualPlugin::Private
{
	VisualColorStyleChooser colorStyleChooser;
	PlayManager* playManager;
	GUI_StyleSettings* styleSettings = nullptr;
	QPushButton* btnConfig = nullptr;
	QPushButton* btnPrev = nullptr;
	QPushButton* btnNext = nullptr;
	QPushButton* btnClose = nullptr;
	ColorStyle currentColorStyle;
	int currentColorStyleIndex {-1};
	bool isActive {false};

	QTimer* timer = nullptr;

	explicit Private(PlayManager* playManager) :
		playManager(playManager) {}
};

VisualPlugin::VisualPlugin(PlayManager* playManager, QWidget* parent) :
	PlayerPlugin::Base(parent),
	m {Pimpl::make<Private>(playManager)} {}

VisualPlugin::~VisualPlugin() = default;

void VisualPlugin::initUi()
{
	connect(m->playManager, &PlayManager::sigPlaystateChanged, this, &VisualPlugin::playstateChanged);

	m->currentColorStyleIndex = std::max(0, loadStyleIndex());
	m->currentColorStyleIndex = std::min(m->colorStyleChooser.count() - 1, m->currentColorStyleIndex);
	if(m->currentColorStyleIndex >= 0)
	{
		m->currentColorStyle = extractStyle(m->colorStyleChooser.style(m->currentColorStyleIndex));
	}

	m->timer = new QTimer();
	m->timer->setInterval(30); // NOLINT(*-magic-numbers)

	connect(m->timer, &QTimer::timeout, this, &VisualPlugin::fadeoutTimerTimedOut);
}

void VisualPlugin::reloadStyle(const int index)
{
	if(isUiInitialized())
	{
		m->colorStyleChooser.reload();

		const auto rawColorStyle = m->colorStyleChooser.style(index);
		m->currentColorStyle = extractStyle(rawColorStyle);

		if(const auto success = applyStyle(m->currentColorStyle); success)
		{
			saveStyleIndex(index);
		}
	}
}

bool VisualPlugin::hasTitle() const { return false; }

void VisualPlugin::setButtonSizes()
{
	if(!m->btnConfig)
	{
		return;
	}

	const auto fm = fontMetrics();
	const auto charWidth = Gui::Util::textWidth(fm, "W");

	auto x = 10; // NOLINT(*-magic-numbers)
	const auto y = 5;

	const auto height = hasSmallButtons() ? fm.height() + 2 : fm.height() * 2;
	auto width = hasSmallButtons() ? charWidth + 4 : charWidth * 2;
	auto fontSize = hasSmallButtons() ? 6 : 8; // NOLINT(*-magic-numbers)

	auto font = m->btnConfig->font();
	font.setPointSize(fontSize);

	for(auto* button: {m->btnConfig, m->btnPrev, m->btnNext, m->btnClose})
	{
		button->setFont(font);
		button->setMaximumHeight(height);
		button->setMaximumWidth(width);
		button->setGeometry(x, y, width, height);

		const auto css = QString("padding: 0; "
		                         "margin: 0; "
		                         "max-height: %1; "
		                         "min-height: %1;").arg(height);
		button->setStyleSheet(css);

		x += width + 5; // NOLINT(*-magic-numbers)
	}
}

void VisualPlugin::setButtonsVisible(bool b)
{
	if(m->btnConfig)
	{
		m->btnConfig->setVisible(b);
		m->btnPrev->setVisible(b);
		m->btnNext->setVisible(b);
		m->btnClose->setVisible(b);
	}
}

void VisualPlugin::initButtons()
{
	if(m->btnConfig)
	{
		return;
	}

	auto* w = widget();
	m->btnConfig = new QPushButton(QString::fromUtf8("≡"), w);
	m->btnPrev = new QPushButton("<", w);
	m->btnNext = new QPushButton(">", w);
	m->btnClose = new QPushButton("x", w);

	m->btnClose->setFocusProxy(w);

	setButtonSizes();
	setButtonsVisible(false);

	connect(m->btnConfig, &QPushButton::clicked, this, &VisualPlugin::configClicked);
	connect(m->btnPrev, &QPushButton::clicked, this, &VisualPlugin::prevClicked);
	connect(m->btnNext, &QPushButton::clicked, this, &VisualPlugin::nextClicked);
	connect(m->btnClose, &QPushButton::clicked, this, &VisualPlugin::close);
	connect(m->btnClose, &QPushButton::clicked, parentWidget(), &QWidget::close);
}

void VisualPlugin::paintEvent(QPaintEvent* e)
{
	PlayerPlugin::Base::paintEvent(e);

	if(isUiInitialized() && isProcessing() && currentStyle().isValid())
	{
		paint();
		if(isFadeoutCompleted())
		{
			m->timer->stop();
		}
	}
}

void VisualPlugin::showStyleSettings()
{
	if(!m->styleSettings)
	{
		m->styleSettings = new GUI_StyleSettings(this);

		connect(m->styleSettings, &GUI_StyleSettings::sigStyleChanged, this, &VisualPlugin::styleChanged);
		connect(m->styleSettings, &GUI_StyleSettings::sigStyleApplied, this, &VisualPlugin::styleApplied);
	}

	m->styleSettings->show(m->currentColorStyleIndex);
}

void VisualPlugin::configClicked() { showStyleSettings(); }

void VisualPlugin::nextClicked()
{
	m->currentColorStyleIndex = (m->currentColorStyleIndex + 1) % m->colorStyleChooser.count();

	reloadStyle(m->currentColorStyleIndex);
}

void VisualPlugin::prevClicked()
{
	m->currentColorStyleIndex = (m->currentColorStyleIndex - 1);
	if(m->currentColorStyleIndex < 0)
	{
		m->currentColorStyleIndex = m->colorStyleChooser.count() - 1;
	}

	reloadStyle(m->currentColorStyleIndex);
}

void VisualPlugin::playstateChanged(const PlayState playState)
{
	if(isUiInitialized())
	{
		if(playState == PlayState::Stopped)
		{
			m->timer->start();
		}

		else
		{
			m->timer->stop();
		}
	}
}

void VisualPlugin::showEvent(QShowEvent* e)
{
	PlayerPlugin::Base::showEvent(e);

	m->isActive = true;
	notifyDataProvider(m->isActive);

	initButtons();
	repaint();
}

void VisualPlugin::closeEvent(QCloseEvent* e)
{
	PlayerPlugin::Base::closeEvent(e);

	m->isActive = false;
	notifyDataProvider(m->isActive);
}

void VisualPlugin::resizeEvent(QResizeEvent* e)
{
	PlayerPlugin::Base::resizeEvent(e);

	if(isUiInitialized())
	{
		reloadStyle(m->currentColorStyleIndex);
		setButtonSizes();
		repaint();
	}
}

void VisualPlugin::mousePressEvent(QMouseEvent* e)
{
	switch(e->button())
	{
		case Qt::LeftButton:
			nextClicked();
			break;

		case Qt::MiddleButton:
			if(parentWidget())
			{
				parentWidget()->close();
			}
			break;

		case Qt::RightButton:
			showStyleSettings();
			break;
		default:
			break;
	}
}

void VisualPlugin::enterEvent(QEvent* e)
{
	PlayerPlugin::Base::enterEvent(e);

	setButtonSizes();
	setButtonsVisible(true);
}

void VisualPlugin::leaveEvent(QEvent* e)
{
	PlayerPlugin::Base::leaveEvent(e);

	setButtonsVisible(false);
}

void VisualPlugin::styleApplied()
{
	if(isUiInitialized())
	{
		const auto rawColorStyle = m->styleSettings->createStyleFromUi();
		m->currentColorStyle = extractStyle(rawColorStyle);
		applyStyle(m->currentColorStyle);
	}
}

void VisualPlugin::styleChanged()
{
	reloadStyle(m->currentColorStyleIndex);
}

const ColorStyle& VisualPlugin::currentStyle() const { return m->currentColorStyle; }

bool VisualPlugin::isProcessing() const { return m->isActive; }

void VisualPlugin::fadeoutTimerTimedOut()
{
	doFadeoutStep();
	update();
}
