/* 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>()};
		QString text;

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

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

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

	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 = Gui::Util::textWidth(fm, m->text);
		m->overflow = std::max(m->textWidth - width(), 0);
		m->pixelsPerIntervalStep = getPixelsPerIntervalStep(
			m->overflow,
			Gui::Util::textWidth(fm, "ai") * 1.0,
			Gui::Util::textWidth(fm, "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);
		recalculateDimensions();
	}

	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();
	}
}