using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System.Threading.Tasks;

public class SmoothingDeformation : BaseDeformation {


    private float intensity = 1f;
    public override float Intensity
    {
        get
        {
            return intensity;
        }

        set
        {
            if (value > maxIntensity)
            {
                intensity = maxIntensity;
            } else if (value < minIntensity)
            {
                intensity = minIntensity;
            } else
            {
                intensity = value;
            }
        }
    }


    private Coroutine _deformingCoroutine;
    

    override public string DeformationName { get; protected set; } = "Smoothing";



    override public string Description { get; set; } = "Smooth region";



    override public void StartDeforming()
    {
        if (IsDeforming)
        {
            return;
        }
        IsDeforming = true;
        _deformingCoroutine = StartCoroutine(Deform());
    }

    public override void StopDeforming()
    {
        if (_deformingCoroutine != null)
            StopCoroutine(_deformingCoroutine);
        DeformationManager.DeformingMesh.RecalculateNormals();
        IsDeforming = false;
    }

    public IEnumerator Deform()
    {
        while (IsDeforming)
        {
            Smoothing(0.1f, DeformationManager.LocalCurrentPosition, DeformationManager.ScaledAreaOfEffect);

            yield return new WaitForSecondsRealtime(0.2f - (0.15f * (intensity)));
        }
        

    }

    public override void InitializeDeformation(DeformationManager deformationManager)
    {
        DeformationManager = deformationManager;
    }

    private void Smoothing(float shrinkingRatio, Vector3 toolPosition, float areaOfEffect, bool isHC = false)
    {
#if DEBUG
        System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
        stopWatch.Start();
#endif
        Vector3[] currentVertices = DeformationManager.CurrentVertices;

        var neighbourVertices = DeformationManager.NeighbourVertices;

        Vector3[] newVertices = new Vector3[currentVertices.Length];
        Array.Copy(currentVertices, newVertices, currentVertices.Length);

        Vector3[] b = new Vector3[currentVertices.Length];

        Parallel.For(0, currentVertices.Length, i =>
        { 
            if (DeformationManager.MaskedVertices[i] == 1 || Vector3.Distance(currentVertices[i], toolPosition) > areaOfEffect)
            {
                return;
            }
            
            if (neighbourVertices[i].Count != 0)
            {
                Vector3 neighbourSum = Vector3.zero;
                foreach (int neighbourIndex in neighbourVertices[i])
                {
                    neighbourSum += currentVertices[neighbourIndex];
                }

                Vector3 changedVertex = (neighbourSum / neighbourVertices[i].Count);

                newVertices[i] = changedVertex;

            }

            b[i] = newVertices[i] - currentVertices[i];
        });

        if (!isHC)
        {
            DeformationManager.CurrentVertices = newVertices;
            DeformationManager.DeformingMesh.vertices = newVertices;
            
        }
        else
        {

            // another cycle for laplacian smoothing without shrinking 
            Parallel.For(0, currentVertices.Length, i =>
            {
                if (DeformationManager.MaskedVertices[i] == 1 || Vector3.Distance(currentVertices[i], toolPosition) > areaOfEffect)
                {
                    return;
                }


                if (neighbourVertices[i].Count != 0)
                {
                    Vector3 neighbourSum = Vector3.zero;
                    foreach (int neighbourIndex in neighbourVertices[i])
                    {
                        neighbourSum += b[neighbourIndex];
                    }

                    Vector3 changedVertex = newVertices[i] - (shrinkingRatio * b[i] + (((1 - shrinkingRatio) / neighbourVertices[i].Count) * neighbourSum));

                    newVertices[i] = changedVertex;

                }
            });



            DeformationManager.CurrentVertices = newVertices;
            DeformationManager.DeformingMesh.vertices = newVertices;
        }
#if DEBUG
        stopWatch.Stop();
        if (PerformanceTest.Instance != null)
            PerformanceTest.Instance.LogDeformationTime(stopWatch.Elapsed.Milliseconds);
#endif
    }


}
