using System;
using UnityEngine;
using Me.StefS.Utility;

namespace Me.StefS.UnityUtility
{
    public class WaveMesh : MonoBehaviour
    {
        [SerializeField] private float freqfency = 1f;
        [SerializeField] private float scale = 0.1f;
        [SerializeField] private float noiseTimeScale = 0.1f;
        [SerializeField] private float speed = 1.0f;
        [SerializeField] private float noiseStrength = 1f;
        [SerializeField] private float noiseWalk = 1f;
        [SerializeField] private OrientationSide direction;

        private Vector3[] baseHeight;
        private Mesh mesh;

        void Start()
        {
            mesh = GetComponent<MeshFilter>().mesh;
            mesh.MarkDynamic();
            Facetize(mesh);
            mesh.MarkDynamic();
            if (baseHeight == null)
                baseHeight = mesh.vertices;

            mesh.RecalculateNormals();
        }

        void Update()
        {
            float time = Time.time;
            Vector3[] vertices = new Vector3[baseHeight.Length];
            for (int vertexInde = 0; vertexInde < vertices.Length; vertexInde++)
            {
                Vector3 vertex = baseHeight[vertexInde];
                Vector3 directionVector =
                    Vector3.Project(baseHeight[vertexInde], direction.GetDirection());
                vertex.y =
                    (float)
                    Math.Sin((
                                 +time * speed +
                                 + directionVector.x
                                 + directionVector.y
                                 + directionVector.z
                             ) * freqfency
                    )
                    * scale;
                float fixedSineByTime = Mathf.Sin(time * noiseTimeScale);
                vertex.y +=
                    Mathf.PerlinNoise(
                        baseHeight[vertexInde].x + baseHeight[vertexInde].z,
                        baseHeight[vertexInde].y + fixedSineByTime) * noiseStrength;
                vertices[vertexInde] = vertex;
            }

            mesh.vertices = vertices;

//            Facetize(mesh);
            mesh.RecalculateNormals();
        }

        static bool NullOrEmpty<T>(T[] array)
        {
            return array == null || array.Length < 1;
        }

        public static void Facetize(Mesh mesh)
        {
            int triangleCount = mesh.triangles.Length;
            bool boneWeights_isNull = NullOrEmpty(mesh.boneWeights);
            bool colors_isNull = NullOrEmpty(mesh.colors);
            bool colors32_isNull = NullOrEmpty(mesh.colors32);
            bool normals_isNull = NullOrEmpty(mesh.normals);
            bool tangents_isNull = NullOrEmpty(mesh.tangents);
            bool uv_isNull = NullOrEmpty(mesh.uv);
            bool uv2_isNull = NullOrEmpty(mesh.uv2);
#if UNITY_5
            bool uv3_isNull = NullOrEmpty(mesh.uv3);
            bool uv4_isNull = NullOrEmpty(mesh.uv4);
#endif
            bool vertices_isNull = NullOrEmpty(mesh.vertices);

            BoneWeight[] boneWeights = boneWeights_isNull ? null : new BoneWeight[triangleCount];
            Color[] colors = colors_isNull ? null : new Color[triangleCount];
            Color32[] colors32 = colors32_isNull ? null : new Color32[triangleCount];
            Vector3[] normals = normals_isNull ? null : new Vector3[triangleCount];
            Vector4[] tangents = tangents_isNull ? null : new Vector4[triangleCount];
            Vector2[] uv = uv_isNull ? null : new Vector2[triangleCount];
            Vector2[] uv2 = uv2_isNull ? null : new Vector2[triangleCount];
#if UNITY_5
            Vector2[] uv3 = uv3_isNull ? null : new Vector2[triangleCount];
            Vector2[] uv4 = uv4_isNull ? null : new Vector2[triangleCount];
#endif
            Vector3[] vertices = new Vector3[triangleCount];

            // cache mesh arrays because accessing them through the reference is slooooow
            Vector3[] mVertices = mesh.vertices;
            BoneWeight[] mBoneWeights = mesh.boneWeights;
            Color[] mColors = mesh.colors;
            Color32[] mColors32 = mesh.colors32;
            Vector3[] mNormals = mesh.normals;
            Vector4[] mTangents = mesh.tangents;
            Vector2[] mUv = mesh.uv;
            Vector2[] mUv2 = mesh.uv2;
#if UNITY_5
            Vector2[] mUv3 = mesh.uv3;
            Vector2[] mUv4 = mesh.uv4;
#endif

            int index = 0;
            int[][] triangles = new int[mesh.subMeshCount][];

            for (int i = 0; i < mesh.subMeshCount; i++)
            {
                triangles[i] = GetIndices(mesh, i);

                for (int t = 0; t < triangles[i].Length; t++)
                {
                    int n = triangles[i][t];

                    if (!boneWeights_isNull)
                        boneWeights[index] = mBoneWeights[n];

                    if (!colors_isNull)
                        colors[index] = mColors[n];

                    if (!colors32_isNull)
                        colors32[index] = mColors32[n];

                    if (!normals_isNull)
                        normals[index] = mNormals[n];

                    if (!tangents_isNull)
                        tangents[index] = mTangents[n];

                    if (!uv_isNull)
                        uv[index] = mUv[n];

                    if (!uv2_isNull)
                        uv2[index] = mUv2[n];

#if UNITY_5
                    if (!uv3_isNull)
                        uv3[index] = mUv3[n];

                    if (!uv4_isNull)
                        uv4[index] = mUv4[n];

#endif
                    if (!vertices_isNull)
                        vertices[index] = mVertices[n];


                    triangles[i][t] = index;
                    index++;
                }
            }

            mesh.vertices = vertices;
            mesh.boneWeights = boneWeights;
            mesh.colors = colors;
            mesh.colors32 = colors32;
            mesh.normals = normals;
            mesh.tangents = tangents;
            mesh.uv = uv;
            mesh.uv2 = uv2;
#if UNITY_5
            mesh.uv3 = uv3;
            mesh.uv4 = uv4;
#endif

            for (int i = 0; i < mesh.subMeshCount; i++)
            {
                SetIndices(mesh, i, triangles[i]);
            }
        }


        public static void SetIndices(Mesh cloneMesh, int submesh, int[] tris)
        {
            cloneMesh.SetIndices(tris, cloneMesh.GetTopology(submesh), submesh);
        }

        public static int[] GetIndices(Mesh cloneMesh, int submesh)
        {
            return cloneMesh.GetIndices(submesh);
        }
    }
}