namespace Voile
{
    // Based on https://github.com/ppr-game/PPR/blob/engine/PER.Util/src/Color.cs
    /// <summary>
    /// A record struct representing a color.
    /// </summary>
    public record struct Color
    {
        public static readonly Color Transparent = new(1.0f, 1.0f, 1.0f, 0.0f);
        public static readonly Color AliceBlue = new(0xF0F8FF);
        public static readonly Color AntiqueWhite = new(0xFAEBD7);
        public static readonly Color Aqua = new(0x00FFFF);
        public static readonly Color Aquamarine = new(0x7FFFD4);
        public static readonly Color Azure = new(0xF0FFFF);
        public static readonly Color Beige = new(0xF5F5DC);
        public static readonly Color Bisque = new(0xFFE4C4);
        public static readonly Color Black = new(0x000000);
        public static readonly Color BlanchedAlmond = new(0xFFEBCD);
        public static readonly Color Blue = new(0x0000FF);
        public static readonly Color BlueViolet = new(0x8A2BE2);
        public static readonly Color Brown = new(0xA52A2A);
        public static readonly Color BurlyWood = new(0xDEB887);
        public static readonly Color CadetBlue = new(0x5F9EA0);
        public static readonly Color Chartreuse = new(0x7FFF00);
        public static readonly Color Chocolate = new(0xD2691E);
        public static readonly Color Coral = new(0xFF7F50);
        public static readonly Color CornflowerBlue = new(0x6495ED);
        public static readonly Color Cornsilk = new(0xFFF8DC);
        public static readonly Color Crimson = new(0xDC143C);
        public static readonly Color Cyan = new(0x00FFFF);
        public static readonly Color DarkBlue = new(0x00008B);
        public static readonly Color DarkCyan = new(0x008B8B);
        public static readonly Color White = new(0xFFFFFF);
        public static readonly Color Green = new(0x00FF00);
        public static readonly Color Red = new(0xFF0000);
        public static readonly Color DarkGoldenRod = new(0xB8860B);
        public static readonly Color DarkGray = new(0xA9A9A9);
        public static readonly Color DarkGreen = new(0x006400);
        public static readonly Color DarkKhaki = new(0xBDB76B);
        public static readonly Color DarkMagenta = new(0x8B008B);
        public static readonly Color DarkOliveGreen = new(0x556B2F);
        public static readonly Color DarkOrange = new(0xFF8C00);
        public static readonly Color DarkOrchid = new(0x9932CC);
        public static readonly Color DarkRed = new(0x8B0000);
        public static readonly Color DarkSalmon = new(0xE9967A);
        public static readonly Color DarkSeaGreen = new(0x8FBC8F);
        public static readonly Color DarkSlateBlue = new(0x483D8B);
        public static readonly Color DarkSlateGray = new(0x2F4F4F);
        public static readonly Color DarkTurquoise = new(0x00CED1);
        public static readonly Color DarkViolet = new(0x9400D3);
        public static readonly Color DeepPink = new(0xFF1493);
        public static readonly Color DeepSkyBlue = new(0x00BFFF);
        public static readonly Color DimGray = new(0x696969);
        public static readonly Color DodgerBlue = new(0x1E90FF);
        public static readonly Color FireBrick = new(0xB22222);
        public static readonly Color FloralWhite = new(0xFFFAF0);
        public static readonly Color ForestGreen = new(0x228B22);
        public static readonly Color Gainsboro = new(0xDCDCDC);
        public static readonly Color GhostWhite = new(0xF8F8FF);
        public static readonly Color Gold = new(0xFFD700);
        public static readonly Color GoldenRod = new(0xDAA520);
        public static readonly Color Gray = new(0x808080);
        public static readonly Color GreenYellow = new(0xADFF2F);
        public static readonly Color HoneyDew = new(0xF0FFF0);
        public static readonly Color HotPink = new(0xFF69B4);
        public static readonly Color IndianRed = new(0xCD5C5C);
        public static readonly Color Indigo = new(0x4B0082);
        public static readonly Color Ivory = new(0xFFFFF0);
        public static readonly Color Khaki = new(0xF0E68C);
        public static readonly Color Lavender = new(0xE6E6FA);
        public static readonly Color LavenderBlush = new(0xFFF0F5);
        public static readonly Color LawnGreen = new(0x7CFC00);
        public static readonly Color LemonChiffon = new(0xFFFACD);
        public static readonly Color LightBlue = new(0xADD8E6);
        public static readonly Color LightCoral = new(0xF08080);
        public static readonly Color LightCyan = new(0xE0FFFF);
        public static readonly Color LightGoldenRodYellow = new(0xFAFAD2);
        public static readonly Color LightGray = new(0xD3D3D3);
        public static readonly Color LightGreen = new(0x90EE90);
        public static readonly Color LightPink = new(0xFFB6C1);
        public static readonly Color LightSalmon = new(0xFFA07A);
        public static readonly Color LightSeaGreen = new(0x20B2AA);
        public static readonly Color LightSkyBlue = new(0x87CEFA);
        public static readonly Color LightSlateGray = new(0x778899);
        public static readonly Color LightSteelBlue = new(0xB0C4DE);
        public static readonly Color LightYellow = new(0xFFFFE0);
        public static readonly Color Lime = new(0x00FF00);
        public static readonly Color LimeGreen = new(0x32CD32);
        public static readonly Color Linen = new(0xFAF0E6);
        public static readonly Color Magenta = new(0xFF00FF);
        public static readonly Color Maroon = new(0x800000);
        public static readonly Color MediumAquaMarine = new(0x66CDAA);
        public static readonly Color MediumBlue = new(0x0000CD);
        public static readonly Color MediumOrchid = new(0xBA55D3);
        public static readonly Color MediumPurple = new(0x9370DB);
        public static readonly Color MediumSeaGreen = new(0x3CB371);
        public static readonly Color MediumSlateBlue = new(0x7B68EE);
        public static readonly Color MediumSpringGreen = new(0x00FA9A);
        public static readonly Color MediumTurquoise = new(0x48D1CC);
        public static readonly Color MediumVioletRed = new(0xC71585);
        public static readonly Color MidnightBlue = new(0x191970);
        public static readonly Color MintCream = new(0xF5FFFA);
        public static readonly Color MistyRose = new(0xFFE4E1);
        public static readonly Color Moccasin = new(0xFFE4B5);
        public static readonly Color NavajoWhite = new(0xFFDEAD);
        public static readonly Color Navy = new(0x000080);
        public static readonly Color OldLace = new(0xFDF5E6);
        public static readonly Color Olive = new(0x808000);
        public static readonly Color OliveDrab = new(0x6B8E23);
        public static readonly Color Orange = new(0xFFA500);
        public static readonly Color OrangeRed = new(0xFF4500);
        public static readonly Color Orchid = new(0xDA70D6);
        public static readonly Color PaleGoldenRod = new(0xEEE8AA);
        public static readonly Color PaleGreen = new(0x98FB98);
        public static readonly Color PaleTurquoise = new(0xAFEEEE);
        public static readonly Color PaleVioletRed = new(0xDB7093);
        public static readonly Color PapayaWhip = new(0xFFEFD5);
        public static readonly Color PeachPuff = new(0xFFDAB9);
        public static readonly Color Peru = new(0xCD853F);
        public static readonly Color Pink = new(0xFFC0CB);
        public static readonly Color Plum = new(0xDDA0DD);
        public static readonly Color PowderBlue = new(0xB0E0E6);
        public static readonly Color Purple = new(0x800080);
        public static readonly Color RebeccaPurple = new(0x663399);
        public static readonly Color RosyBrown = new(0xBC8F8F);
        public static readonly Color RoyalBlue = new(0x4169E1);
        public static readonly Color SaddleBrown = new(0x8B4513);
        public static readonly Color Salmon = new(0xFA8072);
        public static readonly Color SandyBrown = new(0xF4A460);
        public static readonly Color SeaGreen = new(0x2E8B57);
        public static readonly Color Seashell = new(0xFFF5EE);
        public static readonly Color Sienna = new(0xA0522D);
        public static readonly Color Silver = new(0xC0C0C0);
        public static readonly Color SkyBlue = new(0x87CEEB);
        public static readonly Color SlateBlue = new(0x6A5ACD);
        public static readonly Color SlateGray = new(0x708090);
        public static readonly Color Snow = new(0xFFFAFA);
        public static readonly Color SpringGreen = new(0x00FF7F);
        public static readonly Color SteelBlue = new(0x4682B4);
        public static readonly Color Tan = new(0xD2B48C);
        public static readonly Color Teal = new(0x008080);
        public static readonly Color Thistle = new(0xD8BFD8);
        public static readonly Color Tomato = new(0xFF6347);
        public static readonly Color Turquoise = new(0x40E0D0);
        public static readonly Color Violet = new(0xEE82EE);
        public static readonly Color Wheat = new(0xF5DEB3);
        public static readonly Color WhiteSmoke = new(0xF5F5F5);
        public static readonly Color Yellow = new(0xFFFF00);
        public static readonly Color YellowGreen = new(0x9ACD32);

        /// <summary>
        /// Red component of this <see cref="Color"/>.
        /// </summary>
        public byte R { get; set; }
        /// <summary>
        /// Green component of this <see cref="Color"/>.
        /// </summary>
        public byte G { get; set; }
        /// <summary>
        /// Blue component of this <see cref="Color"/>.
        /// </summary>
        public byte B { get; set; }
        /// <summary>
        /// Alpha component of this <see cref="Color"/>.
        /// </summary>/// <summary>
        /// Gets the color as a 32-bit ARGB integer in the format 0xAARRGGBB.
        /// </summary>
        public byte A { get; set; } = 255;

        /// <summary>
        /// Gets the color as a 32-bit ARGB integer in the format 0xAARRGGBB.
        /// </summary>
        public int Argb
        {
            get
            {
                int a = A << 24;
                int r = R << 16;
                int g = G << 8;
                int b = B;

                return a | r | g | b;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Color"/> struct using float RGB(A) values between 0 and 1.
        /// </summary>
        /// <param name="r">The red component (0.0 to 1.0).</param>
        /// <param name="g">The green component (0.0 to 1.0).</param>
        /// <param name="b">The blue component (0.0 to 1.0).</param>
        /// <param name="a">The alpha component (0.0 to 1.0), default is 1.0 (fully opaque).</param>
        public Color(float r, float g, float b, float a = 1.0f)
        {
            R = (byte)Math.Clamp(r * 255, 0, 255);
            G = (byte)Math.Clamp(g * 255, 0, 255);
            B = (byte)Math.Clamp(b * 255, 0, 255);
            A = (byte)Math.Clamp(a * 255, 0, 255);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Color"/> struct using byte RGB(A) values.
        /// </summary>
        /// <param name="r">The red component (0 to 255).</param>
        /// <param name="g">The green component (0 to 255).</param>
        /// <param name="b">The blue component (0 to 255).</param>
        /// <param name="a">The alpha component (0 to 255), default is 255 (fully opaque).</param>
        public Color(byte r, byte g, byte b, byte a = 255)
        {
            R = r;
            G = g;
            B = b;
            A = a;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Color"/> struct using a hexadecimal value.
        /// </summary>
        /// <param name="hex">
        /// A 24-bit (RRGGBB) or 32-bit (AARRGGBB) integer representing the color.
        /// Alpha is assumed to be 255 if not included.
        /// </param>
        public Color(int hex)
        {
            A = 255;  // Default alpha to 255 if not provided
            B = (byte)(hex & 0xFF);
            G = (byte)((hex >> 8) & 0xFF);
            R = (byte)((hex >> 16) & 0xFF);
            if (hex > 0xFFFFFF)  // If the hex value includes alpha
            {
                A = (byte)((hex >> 24) & 0xFF);
            }
        }

        /// <summary>
        /// Parses a color from a hexadecimal string in the format "#RRGGBB" or "#AARRGGBB".
        /// </summary>
        /// <param name="hex">The hex string representing the color.</param>
        /// <returns>A <see cref="Color"/> instance parsed from the string.</returns>
        /// <exception cref="ArgumentException">Thrown if the format is invalid.</exception>
        public static Color FromHexString(string hex)
        {
            if (hex.StartsWith("#"))
            {
                hex = hex[1..];
            }

            if (hex.Length == 6)
            {
                int rgb = int.Parse(hex, System.Globalization.NumberStyles.HexNumber);
                return new Color(rgb);
            }
            else if (hex.Length == 8)
            {
                int rgba = int.Parse(hex, System.Globalization.NumberStyles.HexNumber);
                return new Color(rgba);
            }
            else
            {
                throw new ArgumentException("Invalid hex color format. Use #RRGGBB or #RRGGBBAA.");
            }
        }

        /// <summary>
        /// Returns a lightened version of the color by interpolating toward white.
        /// </summary>
        /// <param name="amount">A value from 0.0 (no change) to 1.0 (fully white).</param>
        /// <returns>A lighter <see cref="Color"/>.</returns>
        public Color Lightened(float amount)
        {
            var result = this;
            result.R = (byte)Math.Min(255, R + (255 - R) * amount);
            result.G = (byte)Math.Min(255, G + (255 - G) * amount);
            result.B = (byte)Math.Min(255, B + (255 - B) * amount);
            return result;
        }

        /// <summary>
        /// Returns a darkened version of the color by interpolating toward black.
        /// </summary>
        /// <param name="amount">A value from 0.0 (no change) to 1.0 (fully black).</param>
        /// <returns>A darker <see cref="Color"/>.</returns>
        public Color Darkened(float amount)
        {
            var result = this;
            result.R = (byte)(R * (1.0f - amount));
            result.G = (byte)(G * (1.0f - amount));
            result.B = (byte)(B * (1.0f - amount));
            return result;
        }

        /// <summary>
        /// Converts this color to a <see cref="System.Drawing.Color"/>.
        /// </summary>
        /// <returns>A <see cref="System.Drawing.Color"/> with equivalent ARGB values.</returns>
        public System.Drawing.Color ToSystemColor()
        {
            var result = System.Drawing.Color.FromArgb(Argb);
            return result;
        }
    }
}