using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Xml.Serialization;


namespace VisiMath
{
    /// <summary>
    /// Standard color structure
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ColorByte : IEquatable<ColorByte>, IXmlSerializable
    {
        //todo :  make equatable, add comments, methods?
        /// <summary>
        /// Red channel
        /// </summary>
        public byte R;
        /// <summary>
        /// Green channel
        /// </summary>
        public byte G;
        /// <summary>
        /// Blue channel
        /// </summary>
        public byte B;
        /// <summary>
        /// Alpha channel
        /// </summary>
        public byte A;

        public ColorByte(byte r, byte g, byte b)
        {
            R = r; G = g; B = b; A = 255;
        }

        public ColorByte(byte r, byte g, byte b, byte a)
        {
            R = r; G = g; B = b; A = a;
        }

        /// <summary>
        /// Creates color from float values (0 to 1.0, will be clamped automatically)
        /// </summary>
        public ColorByte(float r, float g, float b, float a)
        {
            R = (byte)(MathTools.Clamp(r, 0, 1) * 255);
            G = (byte)(MathTools.Clamp(g, 0, 1) * 255);
            B = (byte)(MathTools.Clamp(b, 0, 1) * 255);
            A = (byte)(MathTools.Clamp(a, 0, 1) * 255);
        }

        /// <summary>
        /// Creates color from float values (0 to 1.0, will be clamped automatically)
        /// </summary>
        public ColorByte(float r, float g, float b)
        {
            R = (byte)(MathTools.Clamp(r, 0, 1) * 255);
            G = (byte)(MathTools.Clamp(g, 0, 1) * 255);
            B = (byte)(MathTools.Clamp(b, 0, 1) * 255);
            A = 255;
        }

        /// <summary>
        /// Constructs color from ARGB values in int32
        /// </summary>
        /// <param name="ARGB">Color as int32</param>
        public ColorByte(Int32 ARGB)
        {
            byte[] b = BitConverter.GetBytes(ARGB);
            B = b[0];
            G = b[1];
            R = b[2];
            A = b[3];
        }

        /// <summary>
        /// XML reader constructor
        /// </summary>
        /// <param name="reader">XML reader</param>
        public ColorByte(System.Xml.XmlReader reader)
        {
            A = 255;
            R = G = B = 0;
            ReadXml(reader);
        }

        /// <summary>
        /// Returns 32 bit representation of the color
        /// </summary>
        /// <returns>Color as INT32</returns>
        public Int32 GetARGB()
        {
            return R >> 24 + G >> 16 + B >> 8 + A;
        }

        /// <summary>
        /// Constructs color from 32 bit representation
        /// </summary>
        /// <param name="argb">Int32 holding color value</param>
        public void FromARGB(Int32 argb)
        {

            byte[] b = BitConverter.GetBytes(argb);
            B = b[0];
            G = b[1];
            R = b[2];
            A = b[3];
        }

        /// <summary>
        /// Returns color value as array of floats 0.0-1.0
        /// </summary>
        /// <returns>Float values</returns>
        public float[] GetFloats()
        {
            float[] rval = new float[4];
            rval[0] = (float)R / 255.0f;
            rval[1] = (float)G / 255.0f;
            rval[2] = (float)B / 255.0f;
            rval[3] = (float)A / 255.0f;
            return rval;
        }


        public static ColorByte Black = new ColorByte(0, 0, 0);
        public static ColorByte White = new ColorByte(255, 255, 255);
        public static ColorByte Red = new ColorByte(255, 0, 0);
        public static ColorByte Green = new ColorByte(0, 255, 0);
        public static ColorByte Blue = new ColorByte(0, 0, 255);

        public override int GetHashCode()
        {
            return R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (!(obj is ColorByte))
                return false;

            ColorByte cb = (ColorByte)obj;

            return cb == this;
        }

        public override string ToString()
        {
            return string.Format("R {0} G {1} B{2} A{3}", R, G, B, A);
        }

        
        public static bool operator ==(ColorByte left, ColorByte right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(ColorByte left, ColorByte right)
        {
            return !left.Equals(right);
        }
        

        #region IEquatable<ColorByte> Members

        public bool Equals(ColorByte other)
        {
            return
                    R == other.R &&
                    G == other.G &&
                    B == other.B &&
                    A == other.A;
        }

        #endregion
    
#region IXmlSerializable Members

        /// <summary>
        /// Reqired by IXMLserializable
        /// </summary>
        /// <returns>Null</returns>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
 	        return null;
        }

        /// <summary>
        /// Deserializes object from XML
        /// </summary>
        /// <param name="reader">XML reader</param>
        public void  ReadXml(System.Xml.XmlReader reader)
        {
 	        A = 255;            
            byte.TryParse(reader["R"], out R);
            byte.TryParse(reader["G"], out G);
            byte.TryParse(reader["B"], out B);
            byte.TryParse(reader["A"], out A);
        }


        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        /// <param name="RootName">Node to save color to</param>
        /// <param name="withoutalpha">If true, alpha value won't be saved</param>
        public void WriteXml(System.Xml.XmlWriter writer, string RootName, bool withoutalpha = false)
        {
            writer.WriteStartElement(RootName);
            WriteXml(writer, withoutalpha);
            writer.WriteEndElement();
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        /// <param name="withoutalpha">If true, alpha value won't be saved</param>
        public void WriteXml(System.Xml.XmlWriter writer, bool withoutalpha = false)
        {
            writer.WriteAttributeString("R", R.ToString());
            writer.WriteAttributeString("G", G.ToString());
            writer.WriteAttributeString("B", B.ToString());
            if (!withoutalpha)
                writer.WriteAttributeString("A", A.ToString());
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            WriteXml(writer, false);
        }

#endregion
}

}
