using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath;
using VisimBox.Resources;
using System.Runtime.InteropServices;

namespace VisimBox.Renderer
{
    /*
    /// <summary>
    /// Interface for vertex buffer - vertex data stored inside the renderer (in graphics memory)
    /// 
    /// </summary>
    public interface IVertexBuffer : IResource
    {
        /// <summary>
        /// Internal ID for the vertex buffer
        /// </summary>
        uint InternalID{get;}

        /// <summary>
        /// If cached, vertex buffer will keep VertexArray copy of the data in the system memory.
        /// All user read / write operations will be routed to cached copy. 
        /// Use ReadCache / WriteCache to update vertexbuffer
        /// </summary>
        bool Cached{get;set;}

        /// <summary>
        /// Reference to VertexArray used as cache
        /// </summary>
        IVertexArray Cache { get; }

        /// <summary>
        /// Reads content of the vertex buffer to cache
        /// </summary>
        void ReadCache();

        /// <summary>
        /// Reads part of the vertex buffer to cache
        /// </summary>
        /// <param name="start">starting index</param>
        /// <param name="count">number of vertex to be read from the VBO</param>
        void ReadCache(int start, int count);

        /// <summary>
        /// Writes content of the cache to vertex buffer
        /// </summary>
        void WriteCache();

        /// <summary>
        /// Writes content of the cache to vertex buffer
        /// </summary>
        /// <param name="start">starting index</param>
        /// <param name="count">number of vertex to be written to the VBO</param>
        void WriteCache(int start, int count);
    }
    */

    /// <summary>
    /// Interface for vertex buffer - vertex data stored inside the renderer (in graphics memory)
    /// 
    /// </summary>
    public interface IVertexBuffer : IHWBuffer<IVertex>
    {

        /// <summary>
        /// Descriptor for the used vertex
        /// </summary>
        VertexDescriptor Descriptor { get; }
        
        /// <summary>
        /// Binds buffer to the renderer on default position (depending on the vertex format)
        /// </summary>
        void BindBuffer();
    }

    /// <summary>
    /// Base abstract class for vertexbuffers
    /// </summary>
    public abstract class VertexBuffer : HWBuffer<IVertex>, IVertexBuffer
    {
        protected VertexDescriptor descriptor;
        protected Type VertexType;


        /// <summary>
        /// Descriptor for the used vertex
        /// </summary>
        public virtual VertexDescriptor Descriptor
        {
            get
            {
                return descriptor;
            }
        }


        protected override void CreateCache()
        {
            if (VertexType == null)
            {
                throw new ArgumentException("Can't create vertexbuffer cache without vertextype");
            }
            
            _cache = VertexFactory.CreateNormalArray(VertexType, _count);
            _cached = true;
        }

        /// <summary>
        /// Just setup buffer with specified usage)
        /// </summary>
        /// <param name="usage"></param>
        public VertexBuffer(ResourceUsage usage) : base(usage)
        {
        }


        /// <summary>
        /// Initializes buffer with specified number of basic elements without cache
        /// </summary>
        /// <param name="vertexType">Required type of vertex (needed for size checking)</param>
        /// <param name="count">Count of elements</param>
        /// <param name="usage">Usage of the buffer, default is static (create once)</param>        
        public VertexBuffer(Type vertexType, int count, ResourceUsage usage = ResourceUsage.Static)
            : base(count * Marshal.SizeOf(vertexType), false, usage)
        {
            VertexType = vertexType;
        }

        /// <summary>
        /// Initializes buffer with specified number of basic elements
        /// </summary>
        /// <param name="vertexType">Required type of vertex (needed for size checking)</param>
        /// <param name="count">Count of elements</param>
        /// <param name="usecache">If true, cache is constructed and used</param>
        /// <param name="usage">Usage of the buffer, default is static (create once)</param>
        public VertexBuffer(Type vertexType, int count, bool usecache = false, ResourceUsage usage = ResourceUsage.Static)
            : base(count * Marshal.SizeOf(vertexType), usecache, usage)
        {
            VertexType = vertexType;
        }

        /// <summary>
        /// Initialized vertex buffer from array of values
        /// </summary>
        /// <param name="values">Source values</param>
        /// <param name="cache">If true, cache is constructed and used</param>
        /// <param name="useArrayAsCache">If true, vertex buffer will store reference to the source array as cache (so any direct read/write from/to array will be reflected in cache. 
        /// Note, automatically sets cache using to true!</param>
        /// <param name="usage">Usage of the buffer, default is static (create once)</param>
        protected VertexBuffer(IVertex[] values, bool cache = false, bool useArrayAsCache = false, ResourceUsage usage = ResourceUsage.Static)
            : base(values, cache, useArrayAsCache, usage)
        {
            descriptor = VertexFactory.GetDescriptor(values[0].GetType());
            VertexType = values[0].GetType();
        }

        /// <summary>
        /// Constructs buffer from collection of values
        /// </summary>
        /// <param name="values">Source values</param>
        /// <param name="cache">If true, cache is constructed and used</param>
        /// <param name="usage">Usage of the buffer, default is static (create once)</param>
        protected VertexBuffer(ICollection<IVertex> values, bool cache = false, ResourceUsage usage = ResourceUsage.Static) : 
            base(values, cache, usage)
        {
            IVertex iv = values.First();
            VertexType = iv.GetType();
            descriptor = VertexFactory.GetDescriptor(iv.GetType());
        }

        /// <summary>
        /// Initialized buffer from Vertex array
        /// </summary>
        /// <param name="values">Source values</param>
        /// <param name="cache">If true, cache is constructed and used</param>
        /// <param name="useArrayAsCache">If true, hw buffer will store reference to the source array as cache (so any direct read/write from/to array will be reflected in cache. 
        /// Note, automatically sets cache using to true!</param>
        /// <param name="usage">Usage of the buffer, default is static (create once)</param>
        protected VertexBuffer(IVertexArray values, bool cache = false, ResourceUsage usage = ResourceUsage.Static)
            : base(values.Count, true, usage)
        {
            VertexType = values[0].GetType();
            descriptor = VertexFactory.GetDescriptor(VertexType);
            //NOTe!!! find some cleaner and faster way to work with this, maybe with dynamic?
            for (int i = 0; i < _count; i++)
            {
                this._cache[i] = values[i];
            }
            WriteCache();
            if (cache)
            {
                _cached = true;
            }
            else
            {
                DisposeCache();
            }
        }

        public override ResourceType ResourceType
        {
            get { return Resources.ResourceType.VertexBuffer; }
        }

        public abstract void BindBuffer();

    }
}
