using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath;
using System.Xml.Serialization;
using System.ComponentModel;

namespace VisimBox.Renderer.RenderObjects
{
    /// <summary>
    /// Data for Skybox renderable object
    /// </summary>
    public class SkyboxData : IXmlSerializable, Resources.IResourceConsumer, IDisposable
    {
        /// <summary>
        /// Names of textures used for skybox faces
        /// </summary>
        public string[] Textures;
        /// <summary>
        /// Scale used for skybox (how far the skybox will extend, should be set on limit of visibility
        /// </summary>
        [Description("Scale used for skybox (how far the skybox will extend, should be set on limit of visibility")]
        [TypeConverter(typeof(VectorConvertor))]
        public Vector3 Scale { get { return _scale; } set { _scale = value; } }

        Vector3 _scale;

        [Description("Left side texture")]
        public string LeftTexture
        {
            get { return Textures[0]; }
            set { ChangeTexture(0, value); }
        }

        [Description("Right side texture")]
        public string RightTexture
        {
            get { return Textures[1]; }
            set { ChangeTexture(1, value); }
        }

        [Description("Front side texture")]
        public string FrontTexture
        {
            get { return Textures[2]; }
            set { ChangeTexture(2, value); }
        }

        [Description("Back side texture")]
        public string BackTexture
        {
            get { return Textures[3]; }
            set { ChangeTexture(3, value); }
        }

        [Description("Top side texture")]        
        public string TopTexture
        {
            get { return Textures[4]; }
            set { ChangeTexture(4, value); }
        }

        [Description("Bottom side texture")]
        public string BottomTexture
        {
            get { return Textures[5]; }
            set { ChangeTexture(5, value); }
        }


        /// <summary>
        /// Cached textures
        /// </summary>
        private Texture[] SkyboxTextures;

        public Texture GetTexture(int index)
        {
            return SkyboxTextures[index];
        }

        protected void ChangeTexture(int index, string newtexture)
        {
            if ((index <0) || (index > 5))
            {
                VisimBox.Console.DebugLine("Attempt to change nonexisting texture in skybox!");
                return;
            }
            Textures[index] = newtexture;

            if (SkyboxTextures[index] != null)
            {
                SkyboxTextures[index].RemoveReference(this);
            }

            Texture t = VisimEngine.Instance.Renderer.TextureMan.Load(Textures[index]);
            if (t != null)
            {
                SkyboxTextures[index] = t;
                SkyboxTextures[index].AddReference(this);
            }
        }

        /// <summary>
        /// Creates skybox data from texture names and scale vector. If any string is set to null, this part of the skybox won't be rendered.
        /// Bottom side of the skybox is usually not needed, so just use null for bottomn.
        /// </summary>
        /// <param name="left">Left side of the skybox (on y axis)</param>
        /// <param name="right">Right side of the skybox (on y axis)</param>
        /// <param name="front">Front side of the skybox (on X axis)</param>
        /// <param name="back">Back side of the skybox (on x axis)</param>
        /// <param name="top">Top side of the skybox (on z axis)</param>
        /// <param name="bottom">Bottom side of the skybox (on y axis)</param>
        /// <param name="scale">Scale of skybox</param>
        public SkyboxData(string left, string right, string front, string back, string top, string bottom, Vector3 scale)
        {
            Textures = new string[6];
            Textures[0] = left;
            Textures[1] = right;
            Textures[2] = front;
            Textures[3] = back;
            Textures[4] = top;
            Textures[5] = bottom;
            Scale = scale;
            SkyboxTextures = new Texture[6];
            for (int i = 0; i < SkyboxTextures.Length; i++)
                SkyboxTextures[i] = null;
        }

        /// <summary>
        /// Creates skybox data from texture names and scale vector. If any string is set to null, this part of the skybox won't be rendered.
        /// Bottom side of the skybox is usually not needed, so just use null for bottomn.
        /// </summary>
        /// <param name="scale">Scale of skybox</param>
        /// <param name="textures">Array of 6 strings containig textures for Left, Right, Front, Back, Top and Bottom face of skybox</param>
        public SkyboxData(string[] textures, Vector3 scale)
        {
            if (textures.Length != 6)
                throw new ArgumentException("SkyboxData requires array of 6 textures");

            Textures = textures;
            Scale = scale;
            SkyboxTextures = new Texture[6];
            for (int i = 0; i < SkyboxTextures.Length; i++)
                SkyboxTextures[i] = null;
        }

        public SkyboxData(System.Xml.XmlReader reader)
        {
            Textures = new string[6];
            Scale = new Vector3(1);
            SkyboxTextures = new Texture[6];
            ReadXml(reader);
        }

        public SkyboxData(SkyboxData source)
        {
            Textures = new string[6];
            Scale = source.Scale;
            SkyboxTextures = new Texture[6];

            for (int i = 0; i < 6; i++)
            {
                Textures[i] = source.Textures[i];
            }
            LoadTextures();
        }

        /// <summary>
        /// Loads textures from renderer.TextureManager, sets internal IDS
        /// </summary>
        public void LoadTextures()
        {
            Texture t;

            for (int i = 0; i < Textures.Length; i++)
            {
                if (Textures[i] != null)
                {
                    t = VisimEngine.Instance.Renderer.TextureMan.Load(Textures[i]);
                    if (t!= null)
                    {
                        if (SkyboxTextures[i] != null)
                        {
                            SkyboxTextures[i].RemoveReference(this);
                        }

                        SkyboxTextures[i] = t;
                        t.AddReference(this);
                    }
                }
            }
        }
    
#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)
        {
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    switch (reader.Name)
                    {
                        case "Left":
                            Textures[0] = reader["Texture"];
                            break;
                        case "Right":
                            Textures[1] = reader["Texture"];
                            break;
                        case "Front":
                            Textures[2] = reader["Texture"];
                            break;
                        case "Back":
                            Textures[3] = reader["Texture"];
                            break;
                        case "Top":
                            Textures[4] = reader["Texture"];
                            break;
                        case "Bottom":
                            Textures[5] = reader["Texture"];
                            break;
                        case "Scale":
                            _scale.ReadXml(reader);
                            break;
                    }
                }
                else return;
            }
        }

        private void WriteOnePart(System.Xml.XmlWriter writer, int index, string texturename)
        {
 	        if (Textures[index] != null)
            {
                writer.WriteStartElement(texturename);
                writer.WriteAttributeString("Texture", Textures[index]);
                writer.WriteEndElement();
            }
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            WriteOnePart(writer, 0, "Left");
            WriteOnePart(writer, 1, "Right");
            WriteOnePart(writer, 2, "Front");
            WriteOnePart(writer, 3, "Back");
            WriteOnePart(writer, 4, "Top");
            WriteOnePart(writer, 5, "Bottom");

            writer.WriteStartElement("Scale");
            Scale.WriteXml(writer);
            writer.WriteEndElement();
        }

#endregion

        #region IResourceConsumer Members

        public void ResourceDisposing(Resources.IResource resource)
        {
            for (int i = 0; i < SkyboxTextures.Length; i++)
            {
                if (SkyboxTextures[i] == resource as Texture)
                {
                    SkyboxTextures[i] = null;
                    Console.DebugLine("Skybox lost texture!");
                }
            }
        }

        public bool ResourceReplaced(Resources.IResource original, Resources.IResource newresource)
        {
            for (int i = 0; i < SkyboxTextures.Length; i++)
            {
                if (SkyboxTextures[i] == original as Texture)
                {
                    SkyboxTextures[i] = newresource as Texture;
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            for (int i = 0; i < SkyboxTextures.Length; i++)
            {
                if (SkyboxTextures[i] != null)
                {
                    SkyboxTextures[i].RemoveReference(this);
                    SkyboxTextures[i] = null;
                }
                Textures[i] = null;
            }
        }

        #endregion
    }
}
