using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisimBox.Resources;
using VisimBox.Renderer.RenderObjects;
using VisiMath.Geometry;
using VisiMath;

namespace VisimBox.Renderer
{
    public abstract class MeshManager : AbstractResMan<Mesh>
    {
        /// <summary>
        /// Adds mesh created from specified geometry to manager and returns it
        /// </summary>
        /// <param name="geometry">Requested geometry.</param>
        /// <param name="name">required name (will be made unique)</param>
        /// <returns>Finished mesh</returns>
        public Mesh Add(IGeometry geometry, string name = null, float requality = 1)
        {
            Mesh rval = Create(geometry, requality, null);
            if (rval == null)
                throw new ArgumentException("Can't convert geometry to renderable type!");
            if (name == null)
            {
                name = "Geometry";
            }
            string keyname = name;
            int i = 0;
            while (resources.ContainsKey(keyname))
            {
                keyname = string.Format("{0}{1}", name, i);
                i++;
            }
            Add(keyname, rval);
            return rval;
        }

        public Mesh CreateNewSphere(Vector3 position, float radius, float quality = 0.5f)
        {
            Sphere sph = new Sphere(position, radius);
            IndexedMesh im = sph.ToIndexedMesh(quality);
            return Add(im, "Sphere");
        }

        //todo public Mesh CreateBox; CreateQuad

        /// <summary>
        /// Creates new mesh from vertices and indices
        /// </summary>
        /// <param name="vertices">Vertex array</param>
        /// <param name="indices">Index array for indexed geo</param>
        /// <param name="type">Type of mesh</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public abstract Mesh Create(IVertexArray vertices, int[] indices, PrimitiveTypes type, Type VertexType = null);

        /// <summary>
        /// Creates new mesh from indexed triangles
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(IndexedMesh geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, geometry.CurrentIndices, PrimitiveTypes.IndexedTriangles, VertexType);
        }

        /// <summary>
        /// Creates new mesh from indexed quads
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(IndexedQuads geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, geometry.CurrentIndices, PrimitiveTypes.IndexedQuads, VertexType);
        }

        /// <summary>
        /// Creates new mesh from indexed lines
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(IndexedLines geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, geometry.CurrentIndices, PrimitiveTypes.IndexedLines, VertexType);
        }

        /// <summary>
        /// Creates new mesh from triangle list
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(TriangleList geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, null, PrimitiveTypes.TriangleList, VertexType);
        }

        /// <summary>
        /// Creates new mesh from quad list
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(QuadList geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, null, PrimitiveTypes.QuadList, VertexType);
        }

        /// <summary>
        /// Creates new mesh from line list
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(LineList geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, null, PrimitiveTypes.LineList, VertexType);
        }

        /// <summary>
        /// Creates new mesh from point list
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Required vertex type</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(Point3DList geometry, Type VertexType = null)
        {
            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;
            return Create(geometry.Vertices, null, PrimitiveTypes.PointList, VertexType);
        }

        /// <summary>
        /// Creates new mesh from generic geometry (uses default conversion and preferred primitive)
        /// </summary>
        /// <param name="geometry">Geometry object</param>
        /// <param name="VertexType">Type of the vertex for the final mesh. If null, either vertex in geometry object will be used (if set), or NormalVertex</param>
        /// <returns>New mesh</returns>
        public virtual Mesh Create(IGeometry geometry, float requiredQuality = 1, Type VertexType = null)
        {
            PrimitiveTypes pt = geometry.PreferredPrimitive;

            if (VertexType == null)
                VertexType = geometry.PreferredVertexType;

            var vim = geometry as IndexedMesh;
            if (vim != null)
                return Create(vim, VertexType);
            var viq = geometry as IndexedQuads;
            if (viq != null)
                return Create(viq, VertexType);
            var vtlist = geometry as TriangleList;
            if (vtlist != null)
                return Create(vtlist, VertexType);
            //todo, add other typical overrides?

            switch (pt)
            {
                case PrimitiveTypes.IndexedTriangles:
                    return Create(geometry.ToIndexedMesh(VertexType, requiredQuality), VertexType);
                case PrimitiveTypes.TriangleList:
                    return Create(geometry.ToTriangles(VertexType, requiredQuality), VertexType);
                case PrimitiveTypes.LineList:
                    return Create(geometry.ToLines(VertexType, requiredQuality), VertexType);
                case PrimitiveTypes.PointList:
                    return Create(geometry.ToPoints(VertexType, requiredQuality), VertexType);
                case PrimitiveTypes.IndexedQuads:
                    return Create(geometry as IndexedQuads, VertexType);

                default:
                    TriangleList tl = geometry.ToTriangles(VertexType, requiredQuality);
                    if (tl != null)
                    {
                        return Create(tl, VertexType);
                    }
                    LineList ll = geometry.ToLines(VertexType, requiredQuality);
                    if (ll != null)
                    {
                        return Create(ll, VertexType);
                    }
                    Point3DList plist = geometry.ToPoints(VertexType, requiredQuality);
                    if (plist != null)
                    {
                        return Create(plist, VertexType);
                    }
                    return null;
            }
        }
        
    }
}
