/* VisualStyleTypes.cpp, (Created on 14.12.2024) */

/* Copyright (C) 2011-2024 Michael Lugmair
 *
 * 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 "VisualStyleTypes.h"

#include <QHash>
#include <QList>
#include <QColor>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma ide diagnostic ignored "readability-magic-numbers"

namespace
{
	const auto offColor = QColor(0, 0, 0, 50);

	QList<float> getBordersByColorList(const ColorList& colorList)
	{
		static const auto borders4 = QList<float> {} << 0 << 0.33F << 0.66F << 1.0F;
		static const auto borders3 = QList<float> {} << 0 << 0.5F << 1.0F;
		static const auto borders2 = QList<float> {} << 0 << 1.0F;

		if(colorList.colors.size() == 4)
		{
			return borders4;
		}

		if(colorList.colors.size() == 3)
		{
			return borders3;
		}

		if(colorList.colors.size() == 2)
		{
			return borders2;
		}

		return borders4;
	}

	int intermediateColor(const float percent, const int previousColorValue, const int currentColorValue,
	                      const float previousBorder, const float currentBorder)
	{
		const auto dx = currentBorder - previousBorder; // difference between two borders e.g. 0.33
		const auto dy = currentColorValue - previousColorValue; // difference between the red values e.g. 100
		const auto scale = percent - previousBorder; // 0.93 - 0.33: 0.6

		return static_cast<int>((scale * dy) / dx + previousColorValue); // NOLINT(*-narrowing-conversions)
	}

	QColor getColorByIndex(const int index, const int count, const ColorList& colorlist)
	{
		if(index == 0)
		{
			return colorlist.colors[0];
		}

		auto i = 0;
		const auto borders = getBordersByColorList(colorlist);
		const auto percent = (index * 1.0F) / count;
		while(percent > borders[i])
		{
			i++;
		}

		const auto previousColor = colorlist.colors[i - 1];
		const auto currentColor = colorlist.colors[i];
		return {
			intermediateColor(percent, previousColor.red(), currentColor.red(), borders[i - 1], borders[i]),
			intermediateColor(percent, previousColor.green(), currentColor.green(), borders[i - 1], borders[i]),
			intermediateColor(percent, previousColor.blue(), currentColor.blue(), borders[i - 1], borders[i]),
			intermediateColor(percent, previousColor.alpha(), currentColor.alpha(), borders[i - 1], borders[i])
		};
	}

	QHash<int, QColor> getColorMap(const int rectCount, const ColorList& colorList)
	{
		auto result = QHash<int, QColor> {};

		for(int i = 0; i < rectCount; i++)
		{
			result[i] = getColorByIndex(i, rectCount, colorList);
		}

		return result;
	}

	ColorStyle::ColorInfo
	createColorInfo(const ColorList& activeColorList, const int rectCount, const int fadingSteps)
	{
		auto result = ColorStyle::ColorInfo {};

		const auto activeColorMap = getColorMap(rectCount, activeColorList);

		for(int rectIndex = 0; rectIndex < rectCount; rectIndex++)
		{
			const auto activeColor = activeColorMap[rectIndex];

			auto fadingColors = ColorList {};
			fadingColors.colors << offColor << activeColor.darker();

			auto fadingMap = getColorMap(fadingSteps + 1, fadingColors);
			fadingMap.insert(-1, activeColor);

			result.push_back(fadingMap);
		}

		return result;
	}
}

namespace Util
{
	ColorStyle createLevelStyle(const RawColorStyle& style, const int widgetWidth)
	{
		auto result = ColorStyle {};

		result.name = style.colorList.name;
		result.fadingSteps = style.fadingStepsLevel;
		result.horSpacing = style.horSpacingLevel;
		result.verSpacing = style.verSpacingLevel;
		result.colorList = style.colorList;
		result.rectWidth = style.rectWidthLevel;
		result.rectHeight = style.rectHeightLevel;
		result.rectCount = widgetWidth / (result.rectWidth + result.horSpacing);
		result.style = createColorInfo(style.colorList, result.rectCount, style.fadingStepsLevel);
		result.name = style.colorList.name;

		return result;
	}

	ColorStyle createSpectrumStyle(const RawColorStyle& style, const int widgetHeight)
	{
		auto result = ColorStyle {};

		result.name = style.colorList.name;
		result.fadingSteps = style.fadingStepsSpectrum;
		result.horSpacing = style.horSpacingSpectrum;
		result.verSpacing = style.verSpacingSpectrum;
		result.colorList = style.colorList;
		result.rectHeight = style.rectHeightSpectrum;
		result.rectCount = widgetHeight / (result.rectHeight + result.verSpacing);
		result.style = createColorInfo(style.colorList, result.rectCount, style.fadingStepsSpectrum);
		result.name = style.colorList.name;

		return result;
	}

	RawColorStyle createRgbStyle()
	{
		auto result = RawColorStyle {};

		result.colorList.colors << QColor(0, 216, 0)
		                        << QColor(216, 216, 0)
		                        << QColor(216, 0, 0)
		                        << QColor(216, 0, 0);
		result.colorList.name = "Fancy";
		result.horSpacingLevel = 2;
		result.horSpacingLevel = 2;
		result.verSpacingSpectrum = 1;
		result.horSpacingSpectrum = 1;
		result.spectrumBins = 50;
		result.fadingStepsLevel = 20;
		result.fadingStepsSpectrum = 20;
		result.rectHeightSpectrum = 2;
		result.rectWidthLevel = 5;
		result.rectHeightLevel = 6;

		return result;
	}

	RawColorStyle createBwStyle()
	{
		auto result = RawColorStyle {};

		result.colorList.colors << QColor(27, 32, 47)
		                        << QColor(134, 134, 134)
		                        << QColor(216, 216, 216)
		                        << QColor(255, 255, 255);
		result.colorList.name = "B/W";
		result.horSpacingLevel = 2;
		result.horSpacingLevel = 2;
		result.verSpacingSpectrum = 1;
		result.horSpacingSpectrum = 1;
		result.spectrumBins = 50;
		result.fadingStepsLevel = 20;
		result.fadingStepsSpectrum = 20;
		result.rectHeightSpectrum = 2;
		result.rectWidthLevel = 3;
		result.rectHeightLevel = 3;

		return result;
	}
}

QString RawColorStyle::toString() const
{
	QString ret;
	ret += colorList.name + "";
	ret += ", n_bins_sp: " + QString::number(spectrumBins);
	ret += ", rect_h_sp: " + QString::number(rectHeightSpectrum);
	ret += ", fad_s_sp: " + QString::number(fadingStepsSpectrum);
	ret += ", rect_w_lv: " + QString::number(rectWidthLevel);
	ret += ", rect_h_lv: " + QString::number(rectHeightLevel);
	ret += ", fad_s_lv: " + QString::number(fadingStepsLevel);
	ret += ", hor_s_lv: " + QString::number(horSpacingLevel);
	ret += ", ver_s_lv: " + QString::number(verSpacingLevel);
	ret += ", hor_s_sp: " + QString::number(horSpacingSpectrum);
	ret += ", ver_s_sp: " + QString::number(verSpacingSpectrum);

	return ret;
}

bool ColorStyle::isValid() const
{
	return (rectCount > 0) && ((rectHeight > 0) || (rectWidth > 0));
}

#pragma clang diagnostic pop
