using System;
using System.Diagnostics;
using UnityEngine.Scripting.APIUpdating;

namespace UnityEngine.Rendering.RenderGraphModule
{
    /// <summary>
    /// Graphics Buffer resource handle.
    /// </summary>
    [DebuggerDisplay("Buffer ({handle.index})")]
    [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
    public struct BufferHandle
    {
        // Minor Warning: This calls the zeroing constructor this means that the embedded ResourceHandle struct will also be zero-ed
        // which then means ResourceHandle.type will be set to zero == Texture. As this is an "invalid" bufferhandle I guess setting it
        // to type texture just makes it even more properly invalid and not a big issue. But something to keep in mind for tooling/logging.
        private static BufferHandle s_NullHandle = new BufferHandle();

        /// <summary>
        /// Returns a null graphics buffer handle
        /// </summary>
        /// <value>A null graphics buffer handle.</value>
        public static BufferHandle nullHandle { get { return s_NullHandle; } }

        internal ResourceHandle handle;

        internal BufferHandle(in ResourceHandle h) { handle = h; }

        internal BufferHandle(int handle, bool shared = false) { this.handle = new ResourceHandle(handle, RenderGraphResourceType.Buffer, shared); }

        /// <summary>
        /// Cast to GraphicsBuffer
        /// </summary>
        /// <param name="buffer">Input BufferHandle</param>
        /// <returns>Resource as a Graphics Buffer.</returns>
        public static implicit operator GraphicsBuffer(BufferHandle buffer) => buffer.IsValid() ? RenderGraphResourceRegistry.current.GetBuffer(buffer) : null;

        /// <summary>
        /// Return true if the handle is valid.
        /// </summary>
        /// <returns>True if the handle is valid.</returns>
        public bool IsValid() => handle.IsValid();
    }

    /// <summary>
    /// Descriptor used to create graphics buffer resources
    /// </summary>
    public struct BufferDesc
    {
        ///<summary>Number of elements in the buffer..</summary>
        public int count;
        ///<summary>Size of one element in the buffer. Has to match size of buffer type in the shader.</summary>
        public int stride;
        /// <summary>Graphics Buffer name.</summary>
        public string name;
        /// <summary>The intended usage of a GraphicsBuffer.</summary>
        public GraphicsBuffer.Target target;
        /// <summary>The intended update mode of a GraphicsBuffer.</summary>
        public GraphicsBuffer.UsageFlags usageFlags;

        /// <summary>
        /// BufferDesc constructor.
        /// </summary>
        /// <param name="count">Number of elements in the buffer.</param>
        /// <param name="stride">Size of one element in the buffer.</param>
        public BufferDesc(int count, int stride)
            : this()
        {
            this.count = count;
            this.stride = stride;
            this.target = GraphicsBuffer.Target.Structured;
            this.usageFlags = GraphicsBuffer.UsageFlags.None;
        }

        /// <summary>
        /// BufferDesc constructor.
        /// </summary>
        /// <param name="count">Number of elements in the buffer.</param>
        /// <param name="stride">Size of one element in the buffer.</param>
        /// <param name="target">Type of the buffer.</param>
        public BufferDesc(int count, int stride, GraphicsBuffer.Target target)
            : this()
        {
            this.count = count;
            this.stride = stride;
            this.target = target;
            this.usageFlags = GraphicsBuffer.UsageFlags.None;
        }

        /// <summary>
        /// Hash function
        /// </summary>
        /// <returns>The texture descriptor hash.</returns>
        public override int GetHashCode()
        {
            var hashCode = HashFNV1A32.Create();
            hashCode.Append(count);
            hashCode.Append(stride);
            hashCode.Append((int) target);
            hashCode.Append((int) usageFlags);
            return hashCode.value;
        }
    }


    [DebuggerDisplay("BufferResource ({desc.name})")]
    class BufferResource : RenderGraphResource<BufferDesc, GraphicsBuffer>
    {
        public override string GetName()
        {
            if (imported)
                return "ImportedGraphicsBuffer"; // No getter for graphics buffer name.
            else
                return desc.name;
        }

        public override int GetDescHashCode() { return desc.GetHashCode(); }

        public override void CreateGraphicsResource()
        {
            var name = GetName();
            graphicsResource = new GraphicsBuffer(desc.target, desc.usageFlags, desc.count, desc.stride);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
            graphicsResource.name = name == "" ? $"RenderGraphBuffer_{desc.count}_{desc.stride}_{desc.target}" : name;
#endif
        }

        public override void UpdateGraphicsResource()
        {
            if (graphicsResource != null)
                graphicsResource.name = GetName();
        }

        public override void ReleaseGraphicsResource()
        {
            if (graphicsResource != null)
                graphicsResource.Release();
            base.ReleaseGraphicsResource();
        }

        public override void LogCreation(RenderGraphLogger logger)
        {
            logger.LogLine($"Created GraphicsBuffer: {desc.name}");
        }

        public override void LogRelease(RenderGraphLogger logger)
        {
            logger.LogLine($"Released GraphicsBuffer: {desc.name}");
        }
    }

    class BufferPool : RenderGraphResourcePool<GraphicsBuffer>
    {
        protected override void ReleaseInternalResource(GraphicsBuffer res)
        {
            res.Release();
        }

        protected override string GetResourceName(in GraphicsBuffer res)
        {
            return "GraphicsBufferNameNotAvailable"; // GraphicsBuffer.name is a setter only :(
        }

        protected override long GetResourceSize(in GraphicsBuffer res)
        {
            return res.count * res.stride;
        }

        override protected string GetResourceTypeName()
        {
            return "GraphicsBuffer";
        }

        override protected int GetSortIndex(GraphicsBuffer res)
        {
            return res.GetHashCode();
        }
    }
}