using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Class which initialize compute shader for calculation deformations
/// </summary>
public class ComputeShaderManager : MonoBehaviour {

    [SerializeField]
    private ComputeShader shader;

    public int VerticesCount { get; private set; }

    int grabDeformationKernel;
    int inflateDeformationKernel;
    int maskVerticesKernel;

    ComputeBuffer verticesToDeform;
    ComputeBuffer normalsOfVertices;
    ComputeBuffer maskVertices;
    ComputeBuffer labelVertices;

    int numDimx = 1024;

    private void Start()
    {
        grabDeformationKernel = shader.FindKernel("GrabDeformation");
        inflateDeformationKernel = shader.FindKernel("InflateDeformation");
        maskVerticesKernel = shader.FindKernel("MaskVertices");
    }

    public void SetupBuffers(int verticesCount)
    {
        if (verticesToDeform != null)
            verticesToDeform.Release();


        verticesToDeform = new ComputeBuffer(verticesCount, 12, ComputeBufferType.Default);
        shader.SetBuffer(grabDeformationKernel, "vertices", verticesToDeform);
        shader.SetBuffer(inflateDeformationKernel, "vertices", verticesToDeform);
        shader.SetBuffer(maskVerticesKernel, "vertices", verticesToDeform);


        if (normalsOfVertices != null)
            normalsOfVertices.Release();

        normalsOfVertices = new ComputeBuffer(verticesCount, 12, ComputeBufferType.Default);
        shader.SetBuffer(inflateDeformationKernel, "normals", normalsOfVertices);


        if (maskVertices != null)
            maskVertices.Release();

        maskVertices = new ComputeBuffer(verticesCount, 16, ComputeBufferType.Default);
        shader.SetBuffer(maskVerticesKernel, "vertColors", maskVertices);

        if (labelVertices != null)
            labelVertices.Release();

        labelVertices = new ComputeBuffer(verticesCount, 4, ComputeBufferType.Default);
        shader.SetBuffer(grabDeformationKernel, "labeledVertices", labelVertices);
        shader.SetBuffer(inflateDeformationKernel, "labeledVertices", labelVertices);
        shader.SetBuffer(maskVerticesKernel, "labeledVertices", labelVertices);

        VerticesCount = verticesCount;
    }

    
    public void StartGrabDeformation(int[] maskedVertices, Vector3[] currentVertices)
    {
        labelVertices.SetData(maskedVertices);
        verticesToDeform.SetData(currentVertices);
    }

    public void GrabDeformation(Vector3 lastPosition, Vector3 newPosition, float areaOfEffect, float deformationIntensity, int[] maskedVertices, Vector3[] currentVertices, Mesh deformingMesh)
    {
#if DEBUG
        System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
        stopWatch.Start();
#endif

        Vector3 direction = newPosition - lastPosition;
        shader.SetVector("lastPosition", lastPosition);
        shader.SetVector("newPosition", newPosition);
        shader.SetVector("direction", direction);
        shader.SetFloat("areaOfEffect", areaOfEffect);
        shader.SetFloat("deformationIntensity", deformationIntensity);

        shader.Dispatch(grabDeformationKernel, ((currentVertices.Length / numDimx) + 1), 1, 1);

        verticesToDeform.GetData(currentVertices);

        deformingMesh.vertices = currentVertices;


#if DEBUG
        stopWatch.Stop();
        if (PerformanceTest.Instance != null)
            PerformanceTest.Instance.LogDeformationTime(stopWatch.Elapsed.Milliseconds);
#endif

    }

    public void OnDestroy()
    {
        verticesToDeform.Release();
        normalsOfVertices.Release();
        maskVertices.Release();
        labelVertices.Release();
    }

    public void StartMasking(int[] maskedVertices, Vector3[] currentVertices, Color[] vertColor)
    {
        maskVertices.SetData(vertColor);
        labelVertices.SetData(maskedVertices);
        verticesToDeform.SetData(currentVertices);
    }

    public void Masking(Vector3 currentPosition, float areaOfEffect, float intensity, int[] maskedVertices, Vector3[] currentVertices, Color[] vertColor, Mesh deformingMesh)
    {
        shader.SetVector("newPosition", currentPosition);
        shader.SetFloat("areaOfEffect", areaOfEffect);
        shader.SetFloat("deformationIntensity", intensity);
        
        
        shader.Dispatch(maskVerticesKernel, ((currentVertices.Length / numDimx) + 1), 1, 1);
        maskVertices.GetData(vertColor);
        labelVertices.GetData(maskedVertices);

        deformingMesh.colors = vertColor;

        int counter = 0;
        foreach(var vert in maskedVertices)
        {
            if (vert == 1)
            {
                counter++;
            }
        }
        Debug.Log("Masking " + counter);
    }

    public void StartInflate(int[] maskedVertices, Vector3[] currentVertices, Vector3[] normals)
    {
        labelVertices.SetData(maskedVertices);
        verticesToDeform.SetData(currentVertices);
        normalsOfVertices.SetData(normals);
    }

    public void InflateDeformation(Vector3 currentPosition, float areaOfEffect, float intensity, Vector3[] currentVertices, Mesh deformingMesh, int inflateDeflate /*inflate 1, deflate -1*/)
    {
#if DEBUG
        System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
        stopWatch.Start();
#endif
        shader.SetVector("newPosition", currentPosition);
        shader.SetFloat("areaOfEffect", areaOfEffect);
        shader.SetFloat("deformationIntensity", intensity);


        shader.Dispatch(inflateDeformationKernel, ((currentVertices.Length / numDimx) + 1), 1, 1);
        verticesToDeform.GetData(currentVertices);

        deformingMesh.vertices = currentVertices;

#if DEBUG
        stopWatch.Stop();
        if (PerformanceTest.Instance != null)
            PerformanceTest.Instance.LogDeformationTime(stopWatch.Elapsed.Milliseconds);
#endif
    }
}
