#pragma once

#include <Common.h>

#include <algorithm>

#include <Containers/Tags.h>

namespace nCine
{
	inline namespace Primitives
	{
		class Color;

		using Death::Containers::NoInitT;

		/**
			@brief Four-channel normalized color with 32-bit floats per component
			
			Stores the red, green, blue and alpha channels as @cpp float @ce values, with the alpha
			clamped to the 0-1 range. Provides array and channel-wise construction, channel-wise and
			scalar arithmetic, and conversion to and from the 8-bit integer @ref Color.
		*/
		class Colorf
		{
		public:
			/** @{ @name Constants */

			/** @brief Number of channels */
			static constexpr std::int32_t NumChannels = 4;

			/** @} */

			/** @{ @name Predefined colors */

			static const Colorf Black;
			static const Colorf White;
			static const Colorf Red;
			static const Colorf Green;
			static const Colorf Blue;
			static const Colorf Yellow;
			static const Colorf Magenta;
			static const Colorf Cyan;

			/** @} */

			/** @brief Red */
			float R;
			/** @brief Green */
			float G;
			/** @brief Blue */
			float B;
			/** @brief Alpha */
			float A;

			/** @brief Default constructor, creates an opaque white color */
			constexpr Colorf() noexcept
				: Colorf(1.0f, 1.0f, 1.0f, 1.0f) {}
			explicit Colorf(NoInitT) noexcept {}
			/** @brief Constructs an opaque color from three channels */
			constexpr Colorf(float red, float green, float blue) noexcept
				: Colorf(red, green, blue, 1.0f) {}
			/** @brief Constructs a color from four channels, clamping the alpha to the 0-1 range */
			constexpr Colorf(float red, float green, float blue, float alpha) noexcept
				: R(red), G(green), B(blue), A(std::clamp(alpha, 0.0f, 1.0f)) {}
			/** @brief Constructs a color from a four-channel array */
			explicit Colorf(const float channels[NumChannels]) noexcept;
			explicit Colorf(const Color& color) noexcept;

			/** @brief Returns a pointer to the channel array */
			inline const float* Data() const {
				return &R;
			}
			/** @overload */
			inline float* Data() {
				return &R;
			}

			/** @brief Sets all four channels, clamping the alpha to the 0-1 range */
			constexpr void Set(float red, float green, float blue, float alpha)
			{
				R = red;
				G = green;
				B = blue;
				A = std::clamp(alpha, 0.0f, 1.0f);
			}
			/** @brief Sets the three color channels, keeping the alpha unchanged */
			void Set(float red, float green, float blue);
			/** @brief Sets all four channels from an array */
			void SetVec(const float channels[NumChannels]);
			/** @brief Sets the alpha channel */
			void SetAlpha(float alpha);

			/** @brief Assigns from an 8-bit integer color */
			Colorf& operator=(const Color& color);

			bool operator==(const Colorf& color) const;
			bool operator!=(const Colorf& color) const;

			Colorf& operator+=(const Colorf& v);
			Colorf& operator-=(const Colorf& v);

			Colorf& operator*=(const Colorf& color);
			/** @brief Multiplies all channels by a scalar */
			Colorf& operator*=(float scalar);

			Colorf operator+(const Colorf& color) const;
			Colorf operator-(const Colorf& color) const;

			Colorf operator*(const Colorf& color) const;
			/** @brief Multiplies all channels by a scalar */
			Colorf operator*(float scalar) const;
		};

		inline namespace Literals
		{
			// According to https://wg21.link/CWG2521, space between "" and literal name is deprecated because _Uppercase
			// or _double names could be treated as reserved depending on whether the space was present or not,
			// and whitespace is not load-bearing in any other contexts. Clang 17+ adds an off-by-default warning for this;
			// GCC 4.8 however *requires* the space there, so until GCC 4.8 support is dropped, we suppress this warning
			// instead of removing the space. GCC 15 now has the same warning but it's enabled by default on -std=c++23.
			#if (defined(DEATH_TARGET_CLANG) && __clang_major__ >= 17) || (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 15)
			#	pragma GCC diagnostic push
			#	pragma GCC diagnostic ignored "-Wdeprecated-literal-operator"
			#endif

			/** @relatesalso nCine::Primitives::Colorf
				@brief Float RGB color literal
				
				@code{.cpp}
				using namespace nCine::Primitives::Literals;
				Colorf a = 0x5eb233bb_rgbf;	// R: 0x5e, G: 0xb2, B: 0x33, A: 0xff
				@endcode

				See @ref Colorf for more information.
			*/
			constexpr Colorf operator"" _rgbf(unsigned long long value) {
				return { ((value >> 16) & 0xff) / 255.0f, ((value >> 8) & 0xff) / 255.0f, ((value) & 0xff) / 255.0f };
			}

			/** @relatesalso nCine::Primitives::Colorf
				@brief Float RGBA color literal
				
				@code{.cpp}
				using namespace nCine::Primitives::Literals;
				Colorf a = 0x5eb233bb_rgbaf;	// R: 0x5e, G: 0xb2, B: 0x33, A: 0xbb
				@endcode

				See @ref Colorf for more information.
			*/
			constexpr Colorf operator"" _rgbaf(unsigned long long value) {
				return { ((value >> 24) & 0xff) / 255.0f, ((value >> 16) & 0xff) / 255.0f, ((value >> 8) & 0xff) / 255.0f, ((value) & 0xff) / 255.0f };
			}

			#if (defined(DEATH_TARGET_CLANG) && __clang_major__ >= 17) || (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 15)
			#	pragma GCC diagnostic pop
			#endif
		}
	}
}
