using System.Collections;
using UnityEngine;

public class FlashlightHit : MonoBehaviour
{
    // References to the Flashlight and its Light component
    private Light flashlight;
    private GameObject player;
    private PlayerVitals playerVitals;
    public LayerMask enemyLayer; // LayerMask to represent the enemies
    public AudioClip audioDissolution;

    // Duration and delay for the lerp effect
    //public float lerpDuration = 1f;
    private Coroutine _currentCoroutine = null;

    // Cached references for each enemy instance
    private AudioSource audioSource;
    private ParticleSystem[] particleSystems;
    private SkinnedMeshRenderer[] renderers;
    private Material[][] originalMaterials;
    private Light enemyLight;

    private void Start()
    {
        player = GameObject.Find("Player");
        playerVitals = FindObjectOfType(typeof(PlayerVitals)) as PlayerVitals;
        flashlight = playerVitals.LightSource.GetComponent<Light>();
    }
    private void Update()
    {
        // Check if the flashlight is on
        if (playerVitals.LightOn)
        {
            CheckForEnemyHit();
        }
    }

    private void CacheEnemyComponents(GameObject enemy)
    {
        audioSource = enemy.GetComponent<AudioSource>();
        particleSystems = enemy.GetComponentsInChildren<ParticleSystem>();
        renderers = enemy.GetComponentsInChildren<SkinnedMeshRenderer>();
        enemyLight = enemy.GetComponentInChildren<Light>();

        // Storing the original materials to avoid shared material modifications
        originalMaterials = new Material[renderers.Length][];
        for (int i = 0; i < renderers.Length; ++i)
        {
            originalMaterials[i] = renderers[i].materials;
        }
    }

    private void CheckForEnemyHit()
    {
        // Determine how many rays to cast based on desired precision
        int rays = 5;
        // Calculate the spread based on the spot angle
        float spread = Mathf.Tan(flashlight.spotAngle * Mathf.Deg2Rad / 2);

        // Cast multiple rays in a cone pattern
        for (int i = 0; i < rays; i++)
        {
            // Calculate the direction of this ray within the cone
            Vector3 spreadDir = Quaternion.Euler(0, spread * (i / (float)(rays - 1) - 0.5f) * 2, 0) * flashlight.transform.forward;

            RaycastHit hit;
            if (Physics.Raycast(flashlight.transform.position, spreadDir, out hit, flashlight.range, enemyLayer))
            {
                {
                    // Check if we hit an enemy
                    GameObject hitObject = hit.collider.gameObject;
                    // Cache components associated with the enemy when a hit is first detected
                    CacheEnemyComponents(hitObject);
                    EnemyDestruction enemyDestruction = hitObject.GetComponent<EnemyDestruction>();
                    if (hitObject.CompareTag("ShadowPeople") && enemyDestruction != null && !enemyDestruction.IsDissolving) // Make sure the enemy has a tag of “Enemy”
                    {
                        // Mark the enemy as dissolving
                        enemyDestruction.StartDissolve();
                        // Call this when you want to start the coroutine and keep a reference to it
                        _currentCoroutine = StartCoroutine(LerpExpandAndDestroy(hitObject));

                    }
                }
            }
        }
    }


    private IEnumerator LerpExpandAndDestroy(GameObject enemy)
    {
        // First, find the particle system in the child objects of the enemy
        // If a particle system is found, stop it and wait for the particles to settle before continuing
        if (particleSystems != null)
        {
            foreach (var particleSystem in particleSystems)
            {
                // Stop the particle system from emitting new particles
                particleSystem.Stop();

                // Optionally, make sure all particles are cleared
                particleSystem.Clear();

                // Wait until the particle system is no longer playing
                while (particleSystem.IsAlive())
                {
                    yield return null; // Wait until the next frame
                }

                // If you need to completely destroy the particle system, uncomment the line below
                Destroy(particleSystem.gameObject);
            }
        }

        if (audioSource != null)
        {
            audioSource.Stop();
            // Play the audio clip one-shot.
            audioSource.PlayOneShot(audioDissolution);
        }

        if (enemyLight != null)
        {
            enemyLight.enabled = false;
        }

        // Create material instances and initialize the _Expand property to zero for all materials
        foreach (var smr in renderers)
        {
            Material[] materialInstances = new Material[smr.materials.Length];
            for (int i = 0; i < smr.materials.Length; i++)
            {
                // Create a new material instance
                materialInstances[i] = new Material(smr.materials[i]);
                if (materialInstances[i].HasProperty("_Exapnd") && materialInstances[i].HasProperty("_Weight") && materialInstances[i].HasProperty("_Direction")) // Ensure the shader has the _Expand property
                {
                    // Set initial _Expand value
                    materialInstances[i].SetFloat("_Exapnd", 0f);
                    materialInstances[i].SetFloat("_Weight", 0.3f);
                }
            }
            smr.materials = materialInstances; // Assign the new instances to the renderer
        }
        // Initial and target values for Direction lerp
        Vector4 startDirection = new Vector4(0f, 0f, 0f, 0f); // Replace with your desired starting direction values
        Vector4 endDirection = new Vector4(0f, 0f, -5.0f, 0f);   // Replace with your desired end direction values

        float weightStartValue = 0.3f;
        float weightEndValue = 1f;

        float startValue = 0f;
        float endValue = 100f;
        float timeElapsed = 0f;
        float minDuration = 1f;
        float maxDuration = 3f;     // Control lerp duration variability
        float lerpDuration = Random.Range(minDuration, maxDuration);
        // Lerp the _Expand value over time for all materials
        while (timeElapsed < lerpDuration)
        {
            float currentValue = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
            float weightCurrentValue = Mathf.Lerp(weightStartValue, weightEndValue, timeElapsed / lerpDuration);
            Vector4 currentDirection = Vector4.Lerp(startDirection, endDirection, timeElapsed / lerpDuration);

            // Apply the lerped value to all materials of all SkinnedMeshRenderers
            foreach (var smr in renderers)
            {
                foreach (var mat in smr.materials)
                {
                    if (mat.HasProperty("_Exapnd") && mat.HasProperty("_Weight"))
                    {
                        mat.SetFloat("_Exapnd", currentValue);
                        mat.SetFloat("_Weight", weightCurrentValue);
                        mat.SetVector("_Direction", currentDirection);
                    }
                }
            }

            timeElapsed += Time.deltaTime;
            yield return null; // Wait until the next frame
        }

        // Ensure the final value is set for all materials
        foreach (var smr in renderers)
        {
            foreach (var mat in smr.materials)
            {
                if (mat.HasProperty("_Exapnd") && mat.HasProperty("_Weight") && mat.HasProperty("_Direction"))
                {
                    mat.SetFloat("_Exapnd", endValue);
                    mat.SetFloat("_Weight", weightEndValue);
                    mat.SetVector("_Direction", endDirection);
                }
            }
        }

        // Optional delay before destroying the enemy can be added here
        // yield return new WaitForSeconds(delayBeforeDestroy);
        yield return new WaitForSeconds(lerpDuration); // wait for the audio to finish
        // Now destroy the enemy object
        Destroy(enemy);
    }

    private void DestroyEnemy(GameObject enemy)
    {
        // A function to handle the destruction of the enemy
        // You could instantiate some sort of a disappear effect here if you wanted
        if (_currentCoroutine != null)
        {
            StopCoroutine(_currentCoroutine);
        }

        Destroy(enemy);
    }
}