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

namespace VisimBox.Renderer.RenderObjects
{
    /// <summary>
    /// Renderer object - mesh made from vertices (and optionally indices) stored in vertex / index buffer.
    /// </summary>
    public abstract class Mesh : IResource, IResourceConsumer
    {
        /// <summary>
        /// Vertices making the mesh
        /// </summary>
        public IVertexBuffer Vertices { get { return _Vertices; } }
        /// <summary>
        /// Indices for elements (can be null for list type meshes)
        /// </summary>
        public IIndexBuffer Indices { get { return _Indices; } }
        /// <summary>
        /// Primitive type. Accepted types are PointList, LineList, TriangleList, QuadList and IndexedLines, IndexedTriangles and IndexedQuads
        /// </summary>
        public PrimitiveTypes PrimitiveType { get; set; }

        protected IVertexBuffer _Vertices;
        protected IIndexBuffer _Indices;

        /// <summary>
        /// Constructs new mesh.
        /// </summary>
        /// <param name="vert">Vertex buffer with vertices</param>
        /// <param name="ind">Indexbuffer, can be null for nonindexed elements</param>
        /// <param name="type">Type of the primitive</param>
        public Mesh(IVertexBuffer vert, IIndexBuffer ind, PrimitiveTypes type)
        {
            _Vertices = vert;
            _Indices = ind;
            if (vert != null)
                vert.AddReference(this);
            if (Indices != null)
                Indices.AddReference(this);
            PrimitiveType = type;
        }


        /// <summary>
        /// Pushes geometry inside the renderer
        /// </summary>
        public abstract void Render(IRenderContext irc);

        protected ResourceUsage usage;
        protected string _FileName;

        #region IResource Members

        /// <summary>
        /// If true, resource will stay in memory even if it's not referenced
        /// </summary>
        public virtual bool AlwaysStay { get; set; }

        public ResourceType ResourceType
        {
            get { return Resources.ResourceType.Mesh; }
        }

        public ResourceUsage Usage
        {
            get { return usage; }
        }

        public string FileName
        {
            get
            {
                return _FileName;
            }
            set
            {
                _FileName = value;
            }
        }

        protected List<IResourceConsumer> Consumers = new List<IResourceConsumer>();

        /// <summary>
        /// Number of current references
        /// </summary>
        public int References { get { return Consumers.Count; } }

        /// <summary>
        /// Returns number of null generic references
        /// </summary>
        public int NullReferences
        {
            get
            {
                int rval = 0;
                for (int i = 0; i < Consumers.Count; i++)
                {
                    if (Consumers[i] == null)
                        rval++;
                }
                return rval;
            }
        }

        /// <summary>
        /// Adds reference to this resource
        /// </summary>
        /// <param name="consumer">Consumer of resource</param>
        public virtual void AddReference(IResourceConsumer consumer)
        {
            Consumers.Add(consumer);
        }

        /// <summary>
        /// Removes reference to this resource
        /// </summary>
        /// <param name="consumer">Consumer of resource</param>
        public virtual void RemoveReference(IResourceConsumer consumer)
        {
            Consumers.Remove(consumer);
        }

        /// <summary>
        /// Manager wants to replace this resource by another - resource should inform all of it's consumers that it's being replaced 
        /// and update references based on their response
        /// It should also check if new resource is of compatible type
        /// </summary>
        /// <param name="newresource">New resource</param>
        /// <returns>True if resource is compatible type and all consumers accepted resource changing, false otherwise</returns>
        public virtual bool ReplaceReferences(IResource newresource)
        {

            if ((newresource as Mesh) == null)
                return false;

            bool rval = true;

            for (int i = 0; i < Consumers.Count; i++)
            {
                IResourceConsumer consumer = Consumers[i];
                if (consumer != null)
                {
                    bool consresponse = consumer.ResourceReplaced(this, newresource);

                    if (consresponse)
                    {
                        RemoveReference(consumer);

                        if (newresource != null)
                        {
                            newresource.AddReference(consumer);
                        }
                    }
                    else rval = false;
                }
            }

            return rval;
        }

        protected virtual void DisposeConsumers()
        {
            for (int i = 0; i < Consumers.Count; i++)
            {
                if (Consumers[i] != null)
                {
                    Consumers[i].ResourceDisposing(this);
                }
            }
            Consumers.Clear();
        }


        #endregion

        #region IDisposable Members

        public virtual void Dispose()
        {
            if (_Indices != null)
            {
                _Indices.RemoveReference(this);
                if (_Indices.References == 0)
                {
                    _Indices.Dispose(); //no one else is remembering it, so throw it away?
                }
                _Indices = null;
            }
            if (_Vertices != null)
            {
                _Vertices.RemoveReference(this);
                if (_Vertices.References == 0)
                {
                    _Vertices.Dispose();    //no one else is remembering it, so throw it away?
                }
                _Vertices = null;
            }
            DisposeConsumers();
        }

        #endregion

        #region IResourceConsumer Members

        public virtual void ResourceDisposing(IResource resource)
        {
            bool throwex = false;
            if ((resource as IVertexBuffer) == _Vertices)
            {
                _Vertices = null;
                throwex = true;
            }
            if ((resource as IIndexBuffer) == _Indices)
            {
                _Indices = null;
                throwex = true;
            }

            if (throwex)
            {
                throw new ArgumentException("Mesh just lost it's buffers unexpectedly!");   //this is NOT supposed to happen...
            }
        }

        public virtual bool ResourceReplaced(IResource original, IResource newresource)
        {
            if ((original as IVertexBuffer) == _Vertices)
            {
                var newvert = newresource as IVertexBuffer;
                if (newvert == null)
                    return false;   // no no no
                _Vertices = newvert;
                return true;
            }

            if ((original as IIndexBuffer) == _Indices)
            {
                var newind = newresource as IIndexBuffer;
                _Indices = newind;  //this is acceptable... in some cases
                return true;
            }
            return false;   //not my stuff
        }

        #endregion
    }


}
