using System;
using ANX.Framework.Graphics.PackedVector;
using ANX.Framework.NonXNA.Development;
using System.ComponentModel;
using ANX.Framework.Design;

// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license

namespace ANX.Framework
{
    [PercentageComplete(100)]
    [Developer("Glatzemann, SilentWarrior")]
    [TestState(TestStateAttribute.TestState.Tested)]
#if !WINDOWSMETRO
    [Serializable]
    [TypeConverter(typeof(ColorConverter))]
#endif
    public struct Color : IPackedVector<uint>, IPackedVector, IEquatable<Color>
    {
        #region Private Members
        /// <summary>
        /// Marcel NOTE: I made this internal and befriend the ANX.Framework
        /// with the OGL Module so the datatype conversion can be made faster.
        /// </summary>
        internal uint packedValue;

        #endregion // Private Members

        #region Constructors

        public Color(int r, int g, int b)
        {
            if ((((r | g) | b) & -256) != 0)
            {
                r = r < 0 ? 0 : (r > 255 ? 255 : r);
                g = g < 0 ? 0 : (g > 255 ? 255 : g);
                b = b < 0 ? 0 : (b > 255 ? 255 : b);
            }

            this.packedValue = (uint)(((r | g << 8) | b << 16) | -16777216);
        }

        public Color(int r, int g, int b, int a)
        {
            if (((((r | g) | b) | a) & -256) != 0)
            {
                r = r < 0 ? 0 : (r > 255 ? 255 : r);
                g = g < 0 ? 0 : (g > 255 ? 255 : g);
                b = b < 0 ? 0 : (b > 255 ? 255 : b);
                a = a < 0 ? 0 : (a > 255 ? 255 : a);
            }

            this.packedValue = (uint)(((r | g << 8) | b << 16) | a << 24);
        }

        public Color(float r, float g, float b)
        {
            this.packedValue = ColorPack(r, g, b, 1.0f);
        }

        public Color(float r, float g, float b, float a)
        {
            this.packedValue = ColorPack(r, g, b, a);
        }

        public Color(Vector3 vector)
            : this(vector.X, vector.Y, vector.Z)
        {
        }

        public Color(Vector4 vector)
            : this(vector.X, vector.Y, vector.Z, vector.W)
        {
        }

        private Color(uint packedValue)
        {
            this.packedValue = packedValue;
        }

        #endregion // Constructors

        #region Color Constants

        public static Color AliceBlue
        {
            get { return new Color(240, 248, 255, 255); }
        }

        public static Color AntiqueWhite
        {
            get { return new Color(250, 235, 215, 255); }
        }

        public static Color Aqua
        {
            get { return new Color(0, 255, 255, 255); }
        }

        public static Color Aquamarine
        {
            get { return new Color(127, 255, 212, 255); }
        }

        public static Color Azure
        {
            get { return new Color(240, 255, 255, 255); }
        }

        public static Color Beige
        {
            get { return new Color(245, 245, 220, 255); }
        }

        public static Color Bisque
        {
            get { return new Color(255, 228, 196, 255); }
        }

        public static Color Black
        {
            get { return new Color(0, 0, 0, 255); }
        }

        public static Color BlanchedAlmond
        {
            get { return new Color(255, 235, 205, 255); }
        }

        public static Color Blue
        {
            get { return new Color(0, 0, 255, 255); }
        }

        public static Color BlueViolet
        {
            get { return new Color(138, 43, 226, 255); }
        }

        public static Color Brown
        {
            get { return new Color(165, 42, 42, 255); }
        }

        public static Color BurlyWood
        {
            get { return new Color(222, 184, 135, 255); }
        }

        public static Color CadetBlue
        {
            get { return new Color(95, 158, 160, 255); }
        }

        public static Color Chartreuse
        {
            get { return new Color(127, 255, 0, 255); }
        }

        public static Color Chocolate
        {
            get { return new Color(210, 105, 30, 255); }
        }

        public static Color Coral
        {
            get { return new Color(255, 127, 80, 255); }
        }

        public static Color CornflowerBlue
        {
            get { return new Color(0xffed9564); }
        }

        public static Color Cornsilk
        {
            get { return new Color(255, 248, 220, 255); }
        }

        public static Color Crimson
        {
            get { return new Color(220, 20, 60, 255); }
        }

        public static Color Cyan
        {
            get { return new Color(0, 255, 255, 255); }
        }

        public static Color DarkBlue
        {
            get { return new Color(0, 0, 139, 255); }
        }

        public static Color DarkCyan
        {
            get { return new Color(0, 139, 139, 255); }
        }

        public static Color DarkGoldenrod
        {
            get { return new Color(184, 134, 11, 255); }
        }

        public static Color DarkGray
        {
            get { return new Color(169, 169, 169, 255); }
        }

        public static Color DarkGreen
        {
            get { return new Color(0, 100, 0, 255); }
        }

        public static Color DarkKhaki
        {
            get { return new Color(189, 183, 107, 255); }
        }

        public static Color DarkMagenta
        {
            get { return new Color(139, 0, 139, 255); }
        }

        public static Color DarkOliveGreen
        {
            get { return new Color(85, 107, 47, 255); }
        }

        public static Color DarkOrange
        {
            get { return new Color(255, 140, 0, 255); }
        }

        public static Color DarkOrchid
        {
            get { return new Color(153, 50, 204, 255); }
        }

        public static Color DarkRed
        {
            get { return new Color(139, 0, 0, 255); }
        }

        public static Color DarkSalmon
        {
            get { return new Color(233, 150, 122, 255); }
        }

        public static Color DarkSeaGreen
        {
            get { return new Color(143, 188, 139, 255); }
        }

        public static Color DarkSlateBlue
        {
            get { return new Color(72, 61, 139, 255); }
        }

        public static Color DarkSlateGray
        {
            get { return new Color(47, 79, 79, 255); }
        }

        public static Color DarkTurquoise
        {
            get { return new Color(0, 206, 209, 255); }
        }

        public static Color DarkViolet
        {
            get { return new Color(148, 0, 211, 255); }
        }

        public static Color DeepPink
        {
            get { return new Color(255, 20, 147, 255); }
        }

        public static Color DeepSkyBlue
        {
            get { return new Color(0, 191, 255, 255); }
        }

        public static Color DimGray
        {
            get { return new Color(105, 105, 105, 255); }
        }

        public static Color DodgerBlue
        {
            get { return new Color(30, 144, 255, 255); }
        }

        public static Color Firebrick
        {
            get { return new Color(178, 34, 34, 255); }
        }

        public static Color FloralWhite
        {
            get { return new Color(255, 250, 240, 255); }
        }

        public static Color ForestGreen
        {
            get { return new Color(34, 139, 34, 255); }
        }

        public static Color Fuchsia
        {
            get { return new Color(255, 0, 255, 255); }
        }

        public static Color Gainsboro
        {
            get { return new Color(220, 220, 220, 255); }
        }

        public static Color GhostWhite
        {
            get { return new Color(248, 248, 255, 255); }
        }

        public static Color Gold
        {
            get { return new Color(255, 215, 0, 255); }
        }

        public static Color Goldenrod
        {
            get { return new Color(218, 165, 32, 255); }
        }

        public static Color Gray
        {
            get { return new Color(128, 128, 128, 255); }
        }

        public static Color Green
        {
            get { return new Color(0, 128, 0, 255); }
        }

        public static Color GreenYellow
        {
            get { return new Color(173, 255, 47, 255); }
        }

        public static Color Honeydew
        {
            get { return new Color(240, 255, 240, 255); }
        }

        public static Color HotPink
        {
            get { return new Color(255, 105, 180, 255); }
        }

        public static Color IndianRed
        {
            get { return new Color(205, 92, 92, 255); }
        }

        public static Color Indigo
        {
            get { return new Color(75, 0, 130, 255); }
        }

        public static Color Ivory
        {
            get { return new Color(255, 255, 240, 255); }
        }

        public static Color Khaki
        {
            get { return new Color(240, 230, 140, 255); }
        }

        public static Color Lavender
        {
            get { return new Color(230, 230, 250, 255); }
        }

        public static Color LavenderBlush
        {
            get { return new Color(255, 240, 245, 255); }
        }

        public static Color LawnGreen
        {
            get { return new Color(124, 252, 0, 255); }
        }

        public static Color LemonChiffon
        {
            get { return new Color(255, 250, 205, 255); }
        }

        public static Color LightBlue
        {
            get { return new Color(173, 216, 230, 255); }
        }

        public static Color LightCoral
        {
            get { return new Color(240, 128, 128, 255); }
        }

        public static Color LightCyan
        {
            get { return new Color(224, 255, 255, 255); }
        }

        public static Color LightGoldenrodYellow
        {
            get { return new Color(250, 250, 210, 255); }
        }

        public static Color LightGray
        {
            get { return new Color(211, 211, 211, 255); }
        }

        public static Color LightGreen
        {
            get { return new Color(144, 238, 144, 255); }
        }

        public static Color LightPink
        {
            get { return new Color(255, 182, 193, 255); }
        }

        public static Color LightSalmon
        {
            get { return new Color(255, 160, 122, 255); }
        }

        public static Color LightSeaGreen
        {
            get { return new Color(32, 178, 170, 255); }
        }

        public static Color LightSkyBlue
        {
            get { return new Color(135, 206, 250, 255); }
        }

        public static Color LightSlateGray
        {
            get { return new Color(119, 136, 153, 255); }
        }

        public static Color LightSteelBlue
        {
            get { return new Color(176, 196, 222, 255); }
        }

        public static Color LightYellow
        {
            get { return new Color(255, 255, 224, 255); }
        }

        public static Color Lime
        {
            get { return new Color(0, 255, 0, 255); }
        }

        public static Color LimeGreen
        {
            get { return new Color(50, 205, 50, 255); }
        }

        public static Color Linen
        {
            get { return new Color(250, 240, 230, 255); }
        }

        public static Color Magenta
        {
            get { return new Color(255, 0, 255, 255); }
        }

        public static Color Maroon
        {
            get { return new Color(128, 0, 0, 255); }
        }

        public static Color MediumAquamarine
        {
            get { return new Color(102, 205, 170, 255); }
        }

        public static Color MediumBlue
        {
            get { return new Color(0, 0, 205, 255); }
        }

        public static Color MediumOrchid
        {
            get { return new Color(186, 85, 211, 255); }
        }

        public static Color MediumPurple
        {
            get { return new Color(147, 112, 219, 255); }
        }

        public static Color MediumSeaGreen
        {
            get { return new Color(60, 179, 113, 255); }
        }

        public static Color MediumSlateBlue
        {
            get { return new Color(123, 104, 238, 255); }
        }

        public static Color MediumSpringGreen
        {
            get { return new Color(0, 250, 154, 255); }
        }

        public static Color MediumTurquoise
        {
            get { return new Color(72, 209, 204, 255); }
        }

        public static Color MediumVioletRed
        {
            get { return new Color(199, 21, 133, 255); }
        }

        public static Color MidnightBlue
        {
            get { return new Color(25, 25, 112, 255); }
        }

        public static Color MintCream
        {
            get { return new Color(245, 255, 250, 255); }
        }

        public static Color MistyRose
        {
            get { return new Color(255, 228, 225, 255); }
        }

        public static Color Moccasin
        {
            get { return new Color(255, 228, 181, 255); }
        }

        public static Color NavajoWhite
        {
            get { return new Color(255, 222, 173, 255); }
        }

        public static Color Navy
        {
            get { return new Color(0, 0, 128, 255); }
        }

        public static Color OldLace
        {
            get { return new Color(253, 245, 230, 255); }
        }

        public static Color Olive
        {
            get { return new Color(128, 128, 0, 255); }
        }

        public static Color OliveDrab
        {
            get { return new Color(107, 142, 35, 255); }
        }

        public static Color Orange
        {
            get { return new Color(255, 165, 0, 255); }
        }

        public static Color OrangeRed
        {
            get { return new Color(255, 69, 0, 255); }
        }

        public static Color Orchid
        {
            get { return new Color(218, 112, 214, 255); }
        }

        public static Color PaleGoldenrod
        {
            get { return new Color(238, 232, 170, 255); }
        }

        public static Color PaleGreen
        {
            get { return new Color(152, 251, 152, 255); }
        }

        public static Color PaleTurquoise
        {
            get { return new Color(175, 238, 238, 255); }
        }

        public static Color PaleVioletRed
        {
            get { return new Color(219, 112, 147, 255); }
        }

        public static Color PapayaWhip
        {
            get { return new Color(255, 239, 213, 255); }
        }

        public static Color PeachPuff
        {
            get { return new Color(255, 218, 185, 255); }
        }

        public static Color Peru
        {
            get { return new Color(205, 133, 63, 255); }
        }

        public static Color Pink
        {
            get { return new Color(255, 192, 203, 255); }
        }

        public static Color Plum
        {
            get { return new Color(221, 160, 221, 255); }
        }

        public static Color PowderBlue
        {
            get { return new Color(176, 224, 230, 255); }
        }

        public static Color Purple
        {
            get { return new Color(128, 0, 128, 255); }
        }

        public static Color Red
        {
            get { return new Color(255, 0, 0, 255); }
        }

        public static Color RosyBrown
        {
            get { return new Color(188, 143, 143, 255); }
        }

        public static Color RoyalBlue
        {
            get { return new Color(65, 105, 225, 255); }
        }

        public static Color SaddleBrown
        {
            get { return new Color(139, 69, 19, 255); }
        }

        public static Color Salmon
        {
            get { return new Color(250, 128, 114, 255); }
        }

        public static Color SandyBrown
        {
            get { return new Color(244, 164, 96, 255); }
        }

        public static Color SeaGreen
        {
            get { return new Color(46, 139, 87, 255); }
        }

        public static Color SeaShell
        {
            get { return new Color(255, 245, 238, 255); }
        }

        public static Color Sienna
        {
            get { return new Color(160, 82, 45, 255); }
        }

        public static Color Silver
        {
            get { return new Color(192, 192, 192, 255); }
        }

        public static Color SkyBlue
        {
            get { return new Color(135, 206, 235, 255); }
        }

        public static Color SlateBlue
        {
            get { return new Color(106, 90, 205, 255); }
        }

        public static Color SlateGray
        {
            get { return new Color(112, 128, 144, 255); }
        }

        public static Color Snow
        {
            get { return new Color(255, 250, 250, 255); }
        }

        public static Color SpringGreen
        {
            get { return new Color(0, 255, 127, 255); }
        }

        public static Color SteelBlue
        {
            get { return new Color(70, 130, 180, 255); }
        }

        public static Color Tan
        {
            get { return new Color(210, 180, 140, 255); }
        }

        public static Color Teal
        {
            get { return new Color(0, 128, 128, 255); }
        }

        public static Color Thistle
        {
            get { return new Color(216, 191, 216, 255); }
        }

        public static Color Tomato
        {
            get { return new Color(255, 99, 71, 255); }
        }

        public static Color Transparent
        {
            get { return new Color(0, 0, 0, 0); }
        }

        public static Color Turquoise
        {
            get { return new Color(64, 224, 208, 255); }
        }

        public static Color Violet
        {
            get { return new Color(238, 130, 238, 255); }
        }

        public static Color Wheat
        {
            get { return new Color(245, 222, 179, 255); }
        }

        public static Color White
        {
            get { return new Color(255, 255, 255, 255); }
        }

        public static Color WhiteSmoke
        {
            get { return new Color(245, 245, 245, 255); }
        }

        public static Color Yellow
        {
            get { return new Color(255, 255, 0, 255); }
        }

        public static Color YellowGreen
        {
            get { return new Color(154, 205, 50, 255); }
        }

        #endregion // Color Constants

        #region Methods
        public bool Equals(Color other)
        {
            return this.packedValue.Equals(other.packedValue);
        }

        public override bool Equals(object obj)
        {
            return obj is Color && Equals((Color)obj);
        }

        public static bool operator ==(Color a, Color b)
        {
            return a.packedValue == b.packedValue;
        }

        public static bool operator !=(Color a, Color b)
        {
            return a.packedValue != b.packedValue;
        }

        public static Color FromNonPremultiplied(int r, int g, int b, int a)
        {
            Color color;

            r = ClampValue64((long)r * a / 255L);
            g = ClampValue64((long)g * a / 255L);
            b = ClampValue64((long)b * a / 255L);
            a = ClampValue32(a);

            /* //What the heck is this? Anyway it does not work!
            if (((((r | g) | b) | a) & -256) != 0)
            {
                r = r < 0 ? 0 : (r > 255 ? 255 : r);
                g = g < 0 ? 0 : (g > 255 ? 255 : g);
                b = b < 0 ? 0 : (b > 255 ? 255 : b);
                a = a < 0 ? 0 : (a > 255 ? 255 : a);
            }*/

            color.packedValue = (uint)(((r | g << 8) | b << 16) | a << 24);

            return color;
        }

        public static Color FromNonPremultiplied(Vector4 vector)
        {
            Color color;

            color.packedValue = ColorPack(vector.X * vector.W, vector.Y * vector.W, vector.Z * vector.W, vector.W);

            return color;
        }

        public override int GetHashCode()
        {
            return this.packedValue.GetHashCode();
        }

        #region Lerp
        public static Color Lerp(Color value1, Color value2, float amount)
        {
            Color color;

            byte r1 = (byte) value1.packedValue;
            byte g1 = (byte) (value1.packedValue >> 8);
            byte b1 = (byte) (value1.packedValue >> 16);
            byte a1 = (byte) (value1.packedValue >> 24);

            byte r2 = (byte) value2.packedValue;
            byte g2 = (byte) (value2.packedValue >> 8);
            byte b2 = (byte) (value2.packedValue >> 16);
            byte a2 = (byte) (value2.packedValue >> 24);

            int factor = (int) PackUNormal(65536f, amount);

            int r3 = r1 + (((r2 - r1)*factor) >> 16);
            int g3 = g1 + (((g2 - g1)*factor) >> 16);
            int b3 = b1 + (((b2 - b1)*factor) >> 16);
            int a3 = a1 + (((a2 - a1)*factor) >> 16);

            color.packedValue = (uint) (((r3 | (g3 << 8)) | (b3 << 16)) | (a3 << 24));

            return color;
        }
        #endregion

        #region Multiply
        public static Color Multiply(Color value, float scale)
        {
            Color color;

            uint r = (byte) value.packedValue;
            uint g = (byte) (value.packedValue >> 8);
            uint b = (byte) (value.packedValue >> 16);
            uint a = (byte) (value.packedValue >> 24);

            uint uintScale = (uint) MathHelper.Clamp(scale*65536f, 0, 0xffffff);

            r = (r*uintScale) >> 16;
            g = (g*uintScale) >> 16;
            b = (b*uintScale) >> 16;
            a = (a*uintScale) >> 16;

            r = r > 255 ? 255 : r;
            g = g > 255 ? 255 : g;
            b = b > 255 ? 255 : b;
            a = a > 255 ? 255 : a;

            color.packedValue = ((r | (g << 8)) | (b << 0x10)) | (a << 0x18);

            return color;
        }

        #endregion

        public static Color operator *(Color a, float scale)
        {
            return Multiply(a, scale);
        }

        public override string ToString()
        {
            // This may look a bit more ugly, but String.Format should be avoided cause of it's bad performance!
            return "{R:" + R + " G:" + G + " B:" + B + " A:" + A + "}";
        }

        public Vector3 ToVector3()
        {
            Vector3 result;

            result.X = (packedValue & 255);
            result.Y = (packedValue >> 8 & 255);
            result.Z = (packedValue >> 16 & 255);

            result /= 0xff;

            return result;
        }

        public Vector4 ToVector4()
        {
            Vector4 result;

            result.X = (packedValue & 255);
            result.Y = (packedValue >> 8 & 255);
            result.Z = (packedValue >> 16 & 255);
            result.W = (packedValue >> 24 & 255);

            result /= 0xff;

            return result;
        }

        #endregion // Methods

        #region Properties

        [CLSCompliant(false)]
        public uint PackedValue
        {
            get
            {
                return this.packedValue;
            }
            set
            {
                this.packedValue = value;
            }
        }

        public byte R
        {
            get
            {
                return (byte)this.packedValue;
            }
            set
            {
                this.packedValue = (this.packedValue & 0xffffff00) | value;
            }
        }

        public byte G
        {
            get
            {
                return (byte) (this.packedValue >> 8);
            }
            set
            {
                this.packedValue = (this.packedValue & 0xffff00ff) | ((uint) (value << 8));
            }
        }

        public byte B
        {
            get
            {
                return (byte)(this.packedValue >> 0x10);
            }
            set
            {
                this.packedValue = (this.packedValue & 0xff00ffff) | ((uint)(value << 0x10));
            }
        }

        public byte A
        {
            get
            {
                return (byte)(this.packedValue >> 0x18);
            }
            set
            {
                this.packedValue = (this.packedValue & 0xffffff) | ((uint)(value << 0x18));
            }
        }

        #endregion // Properties

        #region Helper
        private static int ClampValue64(long value)
        {
            if (value < 0L)
                return 0;
            if (value > 255L)
                return 255;
            return (int)value;
        }

        private static int ClampValue32(int value)
        {
            if (value < 0)
                return 0;
            if (value > 255)
                return 255;
            return value;
        }

        private static uint ColorPack(float r, float g, float b, float a)
        {
            uint pr = PackUNormal(255f, r);
            uint pg = PackUNormal(255f, g) << 8;
            uint pb = PackUNormal(255f, b) << 16;
            uint pa = PackUNormal(255f, a) << 24;

            return (((pr | pg) | pb) | pa);
        }

        private static uint PackUNormal(float bitmask, float value)
        {
            value *= bitmask;

            if (float.IsNaN(value))
            {
                return 0;
            }
            if (float.IsInfinity(value))
            {
                return (uint)(float.IsNegativeInfinity(value) ? 0 : bitmask);
            }
            if (value < 0)
            {
                return 0;
            }

            return (uint)value;
        }

        void IPackedVector.PackFromVector4(Vector4 vector)
        {
            this.packedValue = ColorPack(vector.X, vector.Y, vector.Z, vector.W);
        }

        #endregion // Helper
    }
}