using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisimBox.Renderer;
using VisiMath;
using OpenTK.Graphics.OpenGL;
using VisimBox.Resources;

namespace VisimRenderGL
{
    class GLMaterialSimpleColor : RenderMaterial
    {
        ColorByte MyColor;


        public GLMaterialSimpleColor(string name, ColorByte col)
        {
            _materialType = RenderMaterialType.Simple;
            this.Name = name;
            MyColor = col;
            if (MyColor.A != 255)
                this.HasAlpha = true;
            else
                this.HasAlpha = false;
        }

        /// <summary>
        /// Creates copy of self with new name
        /// </summary>
        /// <param name="newname">name for the copy</param>
        /// <returns>new material</returns>
        public override IRenderMaterial CreateCopy(string newname)
        {
            return new GLMaterialSimpleColor(newname, MyColor);
        }


        public override void OnActivate(RendererEventArgs args)
        {
            //Disable lighting
            GL.Disable(EnableCap.Lighting);
            //disable textures
            GL.Disable(EnableCap.Texture2D);
            GL.Disable(EnableCap.ColorMaterial);
            //set color
            args.RC.SetColor(ref MyColor);
            if (HasAlpha)
                GL.Enable(EnableCap.Blend);
            else
                GL.Disable(EnableCap.Blend);
        }

        /// <summary>
        /// Returns true if material contains specified color element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasColorElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return true;
                default:
                    return false;
            }
        }
        /// <summary>
        /// Returns true if material contains specified texture element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasTextureElement(MaterialElement element)
        {
            return false;
        }

        /// <summary>
        /// Returns value of specified color element (or white if it doesn't contain this)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Color value</returns>
        public override ColorByte GetColorElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return MyColor;
                default:
                    return ColorByte.White;
            }
        }

        /// <summary>
        /// Returns texture on specified element (or null)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Texture object</returns>
        public override Texture GetTextureElement(MaterialElement element)
        {
            return null;
        }

        /// <summary>
        /// Sets value of specified color element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="color">Color value</param>
        public override void SetColorElement(MaterialElement element, ColorByte color)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    MyColor = color;
                    break;
                default:
                    return;
            }
        }

        /// <summary>
        /// Sets value of specified texture element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="tex">Texture value</param>
        public override void SetTextureElement(MaterialElement element, Texture tex)
        {
            return;
        }

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            //name and type should be done in caller
            while (reader.Read())
            {
                //reader.NodeType == System.Xml.XmlNodeType.
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Description")
                    {
                        this.Description = reader.ReadString();
                    }
                    else
                        if (reader.Name == "Color")
                        {
                            string r = reader.GetAttribute("R");
                            string g = reader.GetAttribute("G");
                            string b = reader.GetAttribute("B");
                            string a = reader.GetAttribute("A");

                            byte colorelement;
                            if (byte.TryParse(r, out colorelement))
                                MyColor.R = colorelement;
                            if (byte.TryParse(g, out colorelement))
                                MyColor.G = colorelement;
                            if (byte.TryParse(b, out colorelement))
                                MyColor.B = colorelement;
                            if (byte.TryParse(a, out colorelement))
                                MyColor.A = colorelement;
                        }
                }
                else
                {
                    //CalcValues();
                    return;
                }
            }
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("name", this.Name);
            writer.WriteAttributeString("type", "simplecolored");
            if ((this.Description != null) && (this.Description != ""))
            {
                writer.WriteStartElement("Description");
                writer.WriteString(this.Description);
                writer.WriteEndElement();
            }
            writer.WriteStartElement("Color");
            writer.WriteAttributeString("R", MyColor.R.ToString());
            writer.WriteAttributeString("G", MyColor.G.ToString());
            writer.WriteAttributeString("B", MyColor.B.ToString());
            writer.WriteAttributeString("A", MyColor.A.ToString());
            writer.WriteEndElement();
        }



    }

    class GLMaterialSimpleTex : RenderMaterial, IResourceConsumer
    {
        Texture Tex;

        public GLMaterialSimpleTex(string name, Texture tex)
        {
            _materialType = RenderMaterialType.SimpleTextured;
            Name = name;
            Tex = tex;
            if (Tex != null)
            {
                Tex.AddReference(this);
                HasAlpha = tex.HasAlpha;
            }
        }

        /// <summary>
        /// Creates copy of self with new name
        /// </summary>
        /// <param name="newname">name for the copy</param>
        /// <returns>new material</returns>
        public override IRenderMaterial CreateCopy(string newname)
        {
            return new GLMaterialSimpleTex(newname, Tex);
        }

        public override void OnActivate(RendererEventArgs args)
        {
            // todo! correct settings
            if (Tex == null)
                return; //note!!!, activate default texture here?
            Tex.Activate(args.RC);
        }

        /// <summary>
        /// Returns true if material contains specified color element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasColorElement(MaterialElement element)
        {
            return false;
        }
        /// <summary>
        /// Returns true if material contains specified texture element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasTextureElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Returns value of specified color element (or white if it doesn't contain this)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Color value</returns>
        public override ColorByte GetColorElement(MaterialElement element)
        {
            return ColorByte.White;
        }

        /// <summary>
        /// Returns texture on specified element (or null)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Texture object</returns>
        public override Texture GetTextureElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return this.Tex;
            }
            return null;
        }

        /// <summary>
        /// Sets value of specified color element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="color">Color value</param>
        public override void SetColorElement(MaterialElement element, ColorByte color)
        {
            return;
        }

        /// <summary>
        /// Sets value of specified texture element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="tex">Texture value</param>
        public override void SetTextureElement(MaterialElement element, Texture tex)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    if (Tex != null)
                        Tex.RemoveReference(this);                    
                    Tex = tex;
                    Tex.AddReference(this);
                    break;
            }
        }

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Description")
                    {
                        this.Description = reader.ReadString();
                    }
                    else if (reader.Name == "Texture")
                    {
                        string name = reader.GetAttribute("name");
                        string filename = reader.GetAttribute("filename");

                        Texture tex = VisimBox.VisimEngine.Instance.Renderer.TextureMan.Load(name, filename);

                        if (Tex != null)
                            Tex.RemoveReference(this);
                        Tex = tex;
                        Tex.AddReference(this);
                    }
                }
                else
                {
                    //CalcValues();
                    return;
                }
            }
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("name", this.Name);
            writer.WriteAttributeString("type", "simpletextured");
            if ((this.Description != null) && (this.Description != ""))
            {
                writer.WriteStartElement("Description");
                writer.WriteString(this.Description);
                writer.WriteEndElement();
            }
            writer.WriteStartElement("Texture");
            Tex.WriteXml(writer);
            writer.WriteEndElement();
        }


        #region IResourceConsumer Members

        public void ResourceDisposing(IResource resource)
        {
            if ((resource as Texture) == Tex)
                Tex = null;
        }

        public bool ResourceReplaced(IResource original, IResource newresource)
        {
            if ((original as Texture) != Tex)
                return false;   //not my texture?

            Texture newtex = (newresource as Texture);

            if (newtex == null)
                return false; //no interest in null textures

            Tex = newtex;   //references updated by caller
            return true;
        }

        #endregion
    }


    class GLMaterialColored : RenderMaterial
    {
        protected StandardColorValues ColorValues;

        protected float[] ambient;
        protected float[] diffuse;
        protected float[] specular;
        protected float[] emissive;


        public GLMaterialColored(string name, ref StandardColorValues values)
        {
            _materialType = RenderMaterialType.Colored;
            Name = name;
            ColorValues = values;
            HasAlpha = ColorValues.Alpha;
            CalcValues();
        }


        /// <summary>
        /// Creates copy of self with new name
        /// </summary>
        /// <param name="newname">name for the copy</param>
        /// <returns>new material</returns>
        public override IRenderMaterial CreateCopy(string newname)
        {
            return new GLMaterialColored(newname, ref ColorValues);
        }


        protected void CalcValues()
        {
            ambient = ColorValues.Ambient.GetFloats();
            diffuse = ColorValues.Diffuse.GetFloats();
            specular = ColorValues.Specular.GetFloats();
            emissive = ColorValues.Emissive.GetFloats();
        }


        public override void OnActivate(RendererEventArgs args)
        {
            GL.Enable(EnableCap.Lighting);
            GL.Disable(EnableCap.Texture2D);
            if (HasAlpha)
                GL.Enable(EnableCap.Blend);
            else
                GL.Disable(EnableCap.Blend);

            //GL.Enable(EnableCap.ColorMaterial);
            //TODO! correct this
            args.RC.SetColor(ref ColorValues.Diffuse);
            GL.Material(MaterialFace.Front, MaterialParameter.Ambient, ambient);
            GL.Material(MaterialFace.Front, MaterialParameter.Diffuse, diffuse);
            GL.Material(MaterialFace.Front, MaterialParameter.Specular, specular);
            GL.Material(MaterialFace.Front, MaterialParameter.Emission, emissive);
            GL.Material(MaterialFace.Front, MaterialParameter.Shininess, ColorValues.Shininess);            
        }

        /// <summary>
        /// Returns true if material contains specified color element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasColorElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Ambient:
                    return true;
                case MaterialElement.Diffuse:
                    return true;
                case MaterialElement.Emmissive:
                    return true;
                case MaterialElement.Specular:
                    return true;
            }
            return false;
        }
        /// <summary>
        /// Returns true if material contains specified texture element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasTextureElement(MaterialElement element)
        {
            return false;
        }

        /// <summary>
        /// Returns value of specified color element (or white if it doesn't contain this)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Color value</returns>
        public override ColorByte GetColorElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Ambient:
                    return this.ColorValues.Ambient;
                case MaterialElement.Diffuse:
                    return this.ColorValues.Diffuse;
                case MaterialElement.Emmissive:
                    return this.ColorValues.Emissive;
                case MaterialElement.Specular:
                    return this.ColorValues.Specular;
            }
            return ColorByte.White;
        }

        /// <summary>
        /// Returns texture on specified element (or null)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Texture object</returns>
        public override Texture GetTextureElement(MaterialElement element)
        {
            return null;
        }

        /// <summary>
        /// Sets value of specified color element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="color">Color value</param>
        public override void SetColorElement(MaterialElement element, ColorByte color)
        {
            switch (element)
            {
                case MaterialElement.Ambient:
                    this.ColorValues.Ambient = color;
                    break;
                case MaterialElement.Diffuse:
                    this.ColorValues.Diffuse = color;
                    break;
                case MaterialElement.Emmissive:
                    this.ColorValues.Emissive = color;
                    break;
                case MaterialElement.Specular:
                    this.ColorValues.Specular = color;
                    break;
            }
            CalcValues();
        }

        /// <summary>
        /// Sets value of specified texture element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="tex">Texture value</param>
        public override void SetTextureElement(MaterialElement element, Texture tex)
        {
            return;
        }

        protected ColorByte ReadColor(System.Xml.XmlReader reader)
        {
            ColorByte rval = new ColorByte();

            string r = reader.GetAttribute("R");
            string g = reader.GetAttribute("G");
            string b = reader.GetAttribute("B");
            string a = reader.GetAttribute("A");

            byte colorelement;
            if (byte.TryParse(r, out colorelement))
                rval.R = colorelement;
            if (byte.TryParse(g, out colorelement))
                rval.G = colorelement;
            if (byte.TryParse(b, out colorelement))
                rval.B = colorelement;
            if (byte.TryParse(a, out colorelement))
                rval.A = colorelement;
            else rval.A = 255;
            return rval;
        }


        public override void ReadXml(System.Xml.XmlReader reader)
        {
            //name and type should be done in caller
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Description")
                    {
                        this.Description = reader.ReadString();
                    }
                    else if (reader.Name == "Ambient")
                    {
                        this.ColorValues.Ambient = ReadColor(reader);
                    }
                    else if (reader.Name == "Diffuse")
                    {
                        this.ColorValues.Diffuse = ReadColor(reader);
                    }
                    else if (reader.Name == "Specular")
                    {
                        this.ColorValues.Specular = ReadColor(reader);
                    }
                    else if (reader.Name == "Emmisive")
                    {
                        this.ColorValues.Emissive = ReadColor(reader);
                    }
                }
                else
                {
                    CalcValues();
                    return;
                }
            }
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("name", this.Name);
            writer.WriteAttributeString("type", "Colored");
            if ((this.Description != null) && (this.Description != ""))
            {
                writer.WriteStartElement("Description");
                writer.WriteString(this.Description);
                writer.WriteEndElement();
            }
            writer.WriteStartElement("Ambient");
            ColorValues.Ambient.WriteXml(writer, true);
            writer.WriteEndElement();

            writer.WriteStartElement("Diffuse");
            ColorValues.Diffuse.WriteXml(writer);
            writer.WriteEndElement();

            writer.WriteStartElement("Specular");
            ColorValues.Specular.WriteXml(writer, true);
            writer.WriteEndElement();

            writer.WriteStartElement("Emmisive");
            ColorValues.Emissive.WriteXml(writer, true);
            writer.WriteEndElement();
        }

    }


    class GLMaterialColoredTex : GLMaterialColored, IResourceConsumer
    {
        Texture Tex;

        public GLMaterialColoredTex(string name, ref StandardColorValues values, Texture tex)
            : base(name, ref values)
        {
            Tex = tex;
            if (tex != null)
            {
                if (tex.HasAlpha)
                    HasAlpha = true;
                tex.AddReference(this);
            }
            _materialType = RenderMaterialType.Textured;

        }

        /// <summary>
        /// Creates copy of self with new name
        /// </summary>
        /// <param name="newname">name for the copy</param>
        /// <returns>new material</returns>
        public override IRenderMaterial CreateCopy(string newname)
        {
            return new GLMaterialColoredTex(newname, ref ColorValues, Tex);
        }


        public override void OnActivate(RendererEventArgs args)
        {
            base.OnActivate(args);
            if (Tex == null)
                return;//note!!!, activate default texture here?
            Tex.Activate(args.RC);
        }

        /// <summary>
        /// Returns true if material contains specified texture element
        /// </summary>
        /// <param name="element">element type</param>
        /// <returns>True if material has this type of element</returns>
        public override bool HasTextureElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return true;
            }
            return false;
        }


        /// <summary>
        /// Returns texture on specified element (or null)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <returns>Texture object</returns>
        public override Texture GetTextureElement(MaterialElement element)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    return Tex;
            }
            return null;
        }


        /// <summary>
        /// Sets value of specified texture element (if supported by material)
        /// </summary>
        /// <param name="element">Element type</param>
        /// <param name="tex">Texture value</param>
        public override void SetTextureElement(MaterialElement element, Texture tex)
        {
            switch (element)
            {
                case MaterialElement.Diffuse:
                    if (Tex != null)
                        Tex.RemoveReference(this);                    
                    Tex = tex;
                    if (Tex != null)
                        Tex.AddReference(this);
                    break;
            }
        }

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            //name and type should be done in caller
            while (reader.Read())
            {                
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Description")
                    {
                        this.Description = reader.ReadString();
                    }
                    else if (reader.Name == "Ambient")
                    {
                        this.ColorValues.Ambient = ReadColor(reader);
                    }
                    else if (reader.Name == "Diffuse")
                    {
                        this.ColorValues.Diffuse = ReadColor(reader);
                    }
                    else if (reader.Name == "Specular")
                    {
                        this.ColorValues.Specular = ReadColor(reader);
                    }
                    else if (reader.Name == "Emmisive")
                    {
                        this.ColorValues.Emissive = ReadColor(reader);
                    }
                    else if (reader.Name == "Texture")
                    {
                        string name = reader.GetAttribute("name");
                        string filename = reader.GetAttribute("file");

                        Texture tex = VisimBox.VisimEngine.Instance.Renderer.TextureMan.Load(name, filename);

                        if (Tex != null)
                            Tex.RemoveReference(this);
                        Tex = tex;
                        Tex.AddReference(this);
                    }
                }
                else
                {
                    CalcValues();
                    return;
                }
            }
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("name", this.Name);
            writer.WriteAttributeString("type", "textured");
            if ((this.Description != null) && (this.Description != ""))
            {
                writer.WriteStartElement("Description");
                writer.WriteString(this.Description);
                writer.WriteEndElement();
            }
            writer.WriteStartElement("Ambient");
            ColorValues.Ambient.WriteXml(writer, true);
            writer.WriteEndElement();

            writer.WriteStartElement("Diffuse");
            ColorValues.Diffuse.WriteXml(writer);
            writer.WriteEndElement();

            writer.WriteStartElement("Specular");
            ColorValues.Specular.WriteXml(writer, true);
            writer.WriteEndElement();

            writer.WriteStartElement("Emmisive");
            ColorValues.Emissive.WriteXml(writer, true);
            writer.WriteEndElement();
            writer.WriteStartElement("Texture");
            Tex.WriteXml(writer);
            writer.WriteEndElement();
        }


        #region IResourceConsumer Members

        public void ResourceDisposing(IResource resource)
        {
            if ((resource as Texture) == Tex)
                Tex = null;
        }

        public bool ResourceReplaced(IResource original, IResource newresource)
        {
            if ((original as Texture) != Tex)
                return false;   //not my texture?

            Texture newtex = (newresource as Texture);

            if (newtex == null)
                return false; //no interest in null textures

            Tex = newtex;   //references updated by caller
            return true;
        }

        #endregion
    }




}
