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

namespace VisiMath
{




    /// <summary>
    /// Vertex List for dynamically building vertex data
    /// Contains quick accessors for commonly used vertex elements
    /// </summary>
    /// <typeparam name="T">Vertex structure, must implement IVertex information, not just vertex attributes</typeparam>
    public class VertexList<T> : List<T>, IVertexArray<T>, IDisposable
        where T : IVertex
    {

        VertexIndexerVec3 positions;
        VertexIndexerVec3 normals;
        VertexIndexerVec2 texcoords;
        VertexIndexerColorByte colors;
        VertexArray<T> array = null;


        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public VertexList()
        {
            ConstructIndexers();
        }

        /// <summary>
        /// Constructs vertexlist with specified capacity
        /// </summary>
        /// <param name="capacity">Required capacity</param>
        public VertexList(int capacity)
            : base(capacity)
        {
            ConstructIndexers();
        }

        /// <summary>
        /// Constructs vertex list from the vertex collection
        /// </summary>
        /// <param name="verarray">Source Vertex array</param>
        public VertexList(IEnumerable<T> verarray) : base(verarray)
        {
            ConstructIndexers();
        }

        protected void ConstructIndexers()
        {
            positions = new VertexIndexerVec3(this, VertexElementUsage.Position);
            normals = new VertexIndexerVec3(this, VertexElementUsage.Normal);
            texcoords = new VertexIndexerVec2(this, VertexElementUsage.TexCoord);
            colors = new VertexIndexerColorByte(this, VertexElementUsage.Color);
        }

        #endregion


        #region Accessors

        /// <summary>
        /// Quick accessor to position vectors
        /// </summary>
        public VertexIndexerVec3 Positions
        {
            get
            {
                return positions;
            }
        }

        /// <summary>
        /// Quick acessor to normal vectors
        /// </summary>
        public VertexIndexerVec3 Normals
        {
            get
            {
                return normals;
            }
        }

        /// <summary>
        /// Quick acessors to texture coordinates
        /// </summary>
        public VertexIndexerVec2 TexCoords
        {
            get
            {
                return texcoords;
            }
        }

        /// <summary>
        /// Quick accessor to color informations
        /// </summary>
        public VertexIndexerColorByte Colors
        {
            get
            {
                return colors;
            }
        }

        new public IVertex this[int index]
        {
            get
            {
                return base[index];
            }

            set
            {
                base[index] = (T)value;
            }
        }

        public VertexDescriptor Descriptor
        {
            get
            {
                return VertexFactory.GetDescriptor(typeof(T));
            }
        }

        #endregion

        /// <summary>
        /// Adds content of the array to the list (and converts vertices to the same format)
        /// </summary>
        /// <param name="varray">array to add</param>
        public void AddArray(IVertexArray varray)
        {
            this.Capacity = this.Capacity + varray.Count;
            
            Type myvertex = typeof (T);
            
            VertexArray<T> vat;

            //optimize!!! use unsafe memory copy if both vertices are of the same type
            VertexFactory.CopyVertexArray<T>(varray, out vat);

            for (int i = 0; i < vat.Count; i++)
            {
                Add((T)vat[i]);
            }
        }


        /// <summary>
        /// Converts vertexlist to vertexarray
        /// </summary>
        /// <returns>New vertexarray containg copy of vertexlist</returns>
        public VertexArray<T> ToVertexArray()
        {
            VertexArray<T> retval = new VertexArray<T>(this);
            return retval;
        }

        #region IVertexArray Members


        public new IEnumerator<IVertex> GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Returns true if it's possible to pin vertices and use it in unmanaged code with GetPointer
        /// Dynamic VertexLists returns false (it's still possible to use GetPointer, but new vertexarray will be created!)
        /// </summary>
        public bool IsPinnable { get { return false; } }


        /// <summary>
        /// Returns pointer to pinned array which can be used in unmanaged code. Must be freed again with freepointer!
        /// Note: each call to this function creates VertexArray from vertexlist. 
        /// It's usualy convert vertexlist to normal VertexArray before using it in renderer. 
        /// </summary>
        public IntPtr GetPointer()
        {
            if (array != null)
                FreePointer();

            array = ToVertexArray();
            return array.GetPointer();
        }

        /// <summary>
        /// Frees previously pinned pointer to array and internal array
        /// </summary>
        public void FreePointer()
        {
            if (array != null)
                array.Dispose();
            array = null;
        }

        /// <summary>
        /// Total size of the array
        /// </summary>
        public int GetSize()
        {
            return Marshal.SizeOf(this[0]) * this.Count;
        }

        /// <summary>
        /// Size of one vertex
        /// </summary>
        public int GetVertexSize()
        {
            return Marshal.SizeOf(this[0]);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            FreePointer();
        }

        #endregion

        #region IVertexArray Members


        /// <summary>
        /// Returns array
        /// </summary>
        /// <returns>vertexarray with contents of the list</returns>
        public IVertexArray GetCopy()
        {
            return ToVertexArray();
        }

        #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)
        {
            if (reader["VertexFormat"] != typeof(T).Name)
                throw new ArgumentException("VertexList: Cannot read different type of vertex from xml!");

            this.Clear();

            while (reader.Read())
            {
                if (reader.IsStartElement("Vertex"))
                {
                    var vertex = VertexFactory.CreateNewVertex(typeof(T));
                    vertex.ReadXml(reader);
                    Add((T)vertex);
                }
                else break;
            }
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("VertexFormat", typeof(T).Name);    //todo - use vertex factory to save description?

            for (int i = 0; i < this.Count; i++)
            {
                this[i].WriteXml(writer, "Vertex");
            }
        }

        #endregion
    }




}
