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

#include <QPaintEvent>
#include <QPainter>
#include <QTimer>

namespace
{
	constexpr const auto TimerInterval = 40;
	constexpr const auto Padding = 10;
	constexpr const auto overflowTime = 3.0;

	double getPixelsPerIntervalStep(const int overflow, const double minPixelsPerSecond,
	                                const double maxPixelsPerSecond)
	{
		const auto pixelsPerSecond = std::clamp(overflow / overflowTime, minPixelsPerSecond, maxPixelsPerSecond);
		return (pixelsPerSecond * TimerInterval) / 1000.0; // NOLINT(*-magic-numbers)
	}
}

namespace Gui
{
	struct FloatingLabel::Private
	{
		std::unique_ptr<QTimer> timer{std::make_unique<QTimer>()};
		std::unique_ptr<QTimer> resizeTimer{std::make_unique<QTimer>()};

		QString text;

		double x{0};
		int height{25};
		int textWidth{0};
		int overflow{0};
		int direction{1};
		double pixelsPerIntervalStep{1.0};

		Private()
		{
			resizeTimer->setSingleShot(true);
		}

		~Private()
		{
			timer->stop();
			resizeTimer->stop();
		}
	};

	FloatingLabel::FloatingLabel(QWidget* parent) :
		Gui::WidgetTemplate<QLabel>(parent),
		m{Pimpl::make<Private>()}
	{
		connect(m->timer.get(), &QTimer::timeout, this, &FloatingLabel::updateOffset);
		connect(m->resizeTimer.get(), &QTimer::timeout, this, &FloatingLabel::recalculateDimensions);
	}

	FloatingLabel::~FloatingLabel() = default;

	void FloatingLabel::paintEvent(QPaintEvent* event)
	{
		auto painter = QPainter(this);
		event->ignore();

		if(m->overflow <= 0)
		{
			painter.drawText(
				QRect{0, 0, width(), height()},
				static_cast<int>(alignment()),
				m->text
			);
		}

		else
		{
			painter.drawText(
				QRect{static_cast<int>(-m->x), 0, (m->textWidth + (2 * Padding) + 10), m->height},
				m->text);
		}
	}

	void FloatingLabel::recalculateDimensions()
	{
		const auto fm = fontMetrics();
		m->timer->stop();
		m->textWidth = Util::textWidth(fm, m->text);
		m->overflow = std::max(m->textWidth - width(), 0);
		m->pixelsPerIntervalStep = getPixelsPerIntervalStep(
			m->overflow,
			Util::textWidth(fm, QStringLiteral("ai")) * 1.0,
			Util::textWidth(fm, QStringLiteral("abcde")) * 1.0);
		m->height = fontMetrics().height();

		if(m->overflow > 0)
		{
			m->timer->start(TimerInterval);
		}

		else
		{
			repaint();
		}
	}

	void FloatingLabel::resizeEvent(QResizeEvent* event)
	{
		QLabel::resizeEvent(event);
		m->resizeTimer->start(250); // NOLINT(*-magic-numbers)
	}

	void FloatingLabel::setFloatingText(const QString& text)
	{
		if(text != m->text)
		{
			m->text = text;
			m->x = 1;
			m->direction = 1;

			recalculateDimensions();
		}
	}

	void FloatingLabel::updateOffset()
	{
		m->x += (m->pixelsPerIntervalStep * m->direction);

		// * = Padding
		const auto upperBorder = static_cast<double>(m->overflow + Padding); // *Ein l|anger text*|
		const auto lowerBorder = static_cast<double>(-Padding); // |*Ein langer t|ext

		if((m->x > upperBorder) || (m->x < lowerBorder))
		{
			m->x = std::max(m->x, lowerBorder);
			m->x = std::min(m->x, upperBorder);
			m->direction = -m->direction;
		}

		repaint();
	}
}
