using System;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.U2D;
using Unity.Collections;

[Serializable]
internal struct SpriteShapeGeometryInfo
{
    [SerializeField]
    internal int geomIndex;
    [SerializeField]
    internal int indexCount;
    [SerializeField]
    internal int vertexCount;
    [SerializeField]
    internal int spriteIndex;
}

// Simple Cache for SpriteShape Geometry Data.
[AddComponentMenu("")]
internal class SpriteShapeGeometryCache : MonoBehaviour
{

    // Serialized Data.
    [SerializeField]
    [HideInInspector]
    int m_MaxArrayCount;
    [SerializeField]
    [HideInInspector]
    Vector3[] m_PosArray = null;
    [SerializeField]
    [HideInInspector]
    Vector2[] m_Uv0Array = null;
    [SerializeField]
    [HideInInspector]
    Vector4[] m_TanArray = null;
    [SerializeField]
    [HideInInspector]
    ushort[] m_IndexArray = null;
    [SerializeField]
    [HideInInspector]
    SpriteShapeGeometryInfo[] m_GeomArray = null;

    // Update set.
    bool m_RequiresUpdate = false;
    bool m_RequiresUpload = false;
    NativeSlice<Vector3> m_PosArrayCache;
    NativeSlice<Vector2> m_Uv0ArrayCache;
    NativeSlice<Vector4> m_TanArrayCache;
    NativeArray<ushort> m_IndexArrayCache;
    NativeArray<SpriteShapeSegment> m_GeomArrayCache;

    internal ushort[] indexArray
    {
        get => m_IndexArray;
    }

    internal Vector3[] posArray
    {
        get => m_PosArray;
    }

    public Vector4[] tanArray
    {
        get => m_TanArray;
    }

    internal int maxArrayCount
    {
        get { return m_MaxArrayCount; }
    }

    internal bool requiresUpdate
    {
        get { return m_RequiresUpdate; }
    }

    internal bool requiresUpload
    {
        get { return m_RequiresUpload; }
    }

    void OnEnable()
    {
        m_RequiresUpload = true;
        m_RequiresUpdate = false;
    }

    // Set Geometry Cache.
    internal void SetGeometryCache(int _maxArrayCount, NativeSlice<Vector3> _posArray, NativeSlice<Vector2> _uv0Array, NativeSlice<Vector4> _tanArray, NativeArray<ushort> _indexArray, NativeArray<UnityEngine.U2D.SpriteShapeSegment> _geomArray)
    {
        m_RequiresUpdate = true;
        m_PosArrayCache = _posArray;
        m_Uv0ArrayCache = _uv0Array;
        m_TanArrayCache = _tanArray;
        m_GeomArrayCache = _geomArray;
        m_IndexArrayCache = _indexArray;
        m_MaxArrayCount = _maxArrayCount;
    }

    // Update GeometryCache.
    internal void UpdateGeometryCache()
    {
        bool updateCache = m_RequiresUpdate && m_GeomArrayCache.IsCreated;
        updateCache = updateCache && m_IndexArrayCache.IsCreated;
        if (updateCache)
        {
            int geomCount = 0;
            int indexCount = 0;
            int vertexCount = 0;

            for (int i = 0; (i < m_GeomArrayCache.Length); ++i)
            {
                var geom = m_GeomArrayCache[i];
                indexCount += geom.indexCount;
                vertexCount += geom.vertexCount;
                if (geom.vertexCount > 0)
                    geomCount = i + 1;
            }

            m_GeomArray = new SpriteShapeGeometryInfo[geomCount];
            NativeArray<SpriteShapeGeometryInfo> geomInfoArray = m_GeomArrayCache.Reinterpret<SpriteShapeGeometryInfo>();
            SpriteShapeCopyUtility<SpriteShapeGeometryInfo>.Copy(m_GeomArray, geomInfoArray, geomCount);

            m_PosArray = new Vector3[vertexCount];
            m_Uv0Array = new Vector2[vertexCount];
            m_IndexArray = new ushort[indexCount];

            SpriteShapeCopyUtility<ushort>.Copy(m_IndexArray, m_IndexArrayCache, indexCount);
            SpriteShapeCopyUtility<Vector3>.Copy(m_PosArray, m_PosArrayCache, vertexCount);
            SpriteShapeCopyUtility<Vector2>.Copy(m_Uv0Array, m_Uv0ArrayCache, vertexCount);
            
            // Do not store Tangent data if not enabled. Just store a stub.
            m_TanArray = new Vector4[(m_TanArrayCache.Length >= vertexCount) ? vertexCount : 1];
            if (m_TanArrayCache.Length >= vertexCount)
                SpriteShapeCopyUtility<Vector4>.Copy(m_TanArray, m_TanArrayCache, vertexCount);

            m_MaxArrayCount = (vertexCount > indexCount) ? vertexCount : indexCount;
            m_RequiresUpdate = false;
        }
    }

    internal JobHandle Upload(SpriteShapeRenderer sr, SpriteShapeController sc)
    {

        JobHandle jobHandle = (default);
        if (m_RequiresUpload)
        {

            // Update Geometries.
            NativeArray<SpriteShapeSegment> geomArray = sr.GetSegments(m_GeomArray.Length);
            NativeArray<SpriteShapeGeometryInfo> geomInfoArray = geomArray.Reinterpret<SpriteShapeGeometryInfo>();
            geomInfoArray.CopyFrom(m_GeomArray);

            // Update Mesh Data.
            NativeSlice<Vector3> posArray;
            NativeSlice<Vector2> uv0Array;
            NativeArray<ushort> indexArray;

            if (sc.enableTangents && m_TanArray.Length > 1)
            {
                NativeSlice<Vector4> tanArray;
                sr.GetChannels(m_MaxArrayCount, out indexArray, out posArray, out uv0Array, out tanArray);
                SpriteShapeCopyUtility<Vector4>.Copy(tanArray, m_TanArray, m_TanArray.Length);
            }
            else
            {
                sr.GetChannels(m_MaxArrayCount, out indexArray, out posArray, out uv0Array);
            }

            SpriteShapeCopyUtility<Vector3>.Copy(posArray, m_PosArray, m_PosArray.Length);
            SpriteShapeCopyUtility<Vector2>.Copy(uv0Array, m_Uv0Array, m_Uv0Array.Length);
            SpriteShapeCopyUtility<ushort>.Copy(indexArray, m_IndexArray, m_IndexArray.Length);
            sr.Prepare(jobHandle, sc.spriteShapeParameters, sc.spriteArray);
            m_RequiresUpload = false;

        }
        return jobHandle;

    }

}