using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static FactWrapper;

public class ShinyThings : MonoBehaviour
{
    public WorldCursor Cursor;
    //Attributes for Highlighting of Facts when Mouse-Over
    private List<FactObject3D> LastFactSelection = new();

    //Variables for Pushout-Highlighting
    private static float timerDuration = 2.5f;

    private IEnumerator rain_wait;
    private IEnumerator rain;

    public Light directionalLight;
    private Color darkColor = new(0.2f, 0.2f, 0.2f);
    private Color light_colour;
    private GameObject active_rainwork;

    public GameObject
        Fireworks_Animation,
        RainPrefab;


    private void OnEnable()
    {
        CommunicationEvents.PushoutFactFailEvent.AddListener(LetItRain);
        CommunicationEvents.AnimateExistingAsSolutionEvent.AddListener(HighlightWithFireworks);
    }

    private void OnDisable()
    {
        CommunicationEvents.PushoutFactFailEvent.RemoveListener(LetItRain);
        CommunicationEvents.AnimateExistingAsSolutionEvent.RemoveListener(HighlightWithFireworks);
    }

    private void Awake()
    {
        rain = rain_wait = IEnumeratorExtensions.yield_break;

        if (Cursor == null)
            Cursor = GetComponent<WorldCursor>();

        if (directionalLight == null)
            directionalLight = FindObjectOfType<Light>();

        light_colour = directionalLight.color;
    }

    public void Update()
    {
        if (Cursor?.Hits != null)
            HighlightCurserHit(Cursor.Hits);
    }

    private void HighlightCurserHit(RaycastHit[] hits)
    {
        List<FactObject3D> selected_fact_objs = hits
            .Select(h => h.transform?.GetComponentInChildren<FactObject3D>())
            .Where(f => f != null)
            .ToList();

        //Set the last Facts unselected
        foreach (var lastFact in LastFactSelection)
            if (lastFact != null // if Fact was deleted
             && !selected_fact_objs.Contains(lastFact))
                _ApplyMaterial(lastFact, lastFact.Default);

        //Set the Facts that were Hit as selected
        foreach (var fact_obj in selected_fact_objs)
            if (!LastFactSelection.Contains(fact_obj))
                _ApplyMaterial(fact_obj, fact_obj.Selected);

        LastFactSelection = selected_fact_objs;
        return;

        void _ApplyMaterial(FactObject3D root, Material new_mat) =>
            root.CoroutineCascadeForMeAndChildrenAllRenderer(
                (_, renderer) =>
                    renderer.ProgrammMaterialChange(new[] {
                            (0f, GlobalBehaviour.AnimationLerpDuration, new_mat),
                    })
                );
    }

    public void HighlightWithFireworks(Fact fact, FactMaterials mat)
    {
        rain_wait = IEnumeratorExtensions.yield_break; //stop rain

        StartCoroutine(_BlossomAndDie());
        CommunicationEvents.AnimateExistingFactEvent.Invoke(fact.Id, mat);

        IEnumerator _BlossomAndDie()
        {
            GameObject firework = GameObject.Instantiate
                (Fireworks_Animation
                , fact.WorldRepresentation.transform
                );

            yield return new WaitForSeconds(timerDuration);

            firework.transform.GetChild(0)
                .GetComponent<ParticleSystem>()
                .Stop();
            var sparks = firework.transform.GetChild(1)
                .GetComponent<ParticleSystem>();
            sparks.Stop();

            while (sparks.IsAlive())
                yield return null;

            Destroy(firework);
        }
    }

    public void LetItRain()
    {
        // check if couroutine is waiting or finished 
        if (!rain_wait.MoveNext() || !rain.MoveNext())
        {
            StopCoroutine(rain);
            StartCoroutine(rain = _BlossomAndDie());
        }
        rain_wait = IEnumeratorExtensions.ClockForSeconds(timerDuration); //reset timer

        IEnumerator _BlossomAndDie()
        {
            Destroy(active_rainwork);
            active_rainwork = Instantiate(RainPrefab, new Vector3(0, 40, 0), Quaternion.identity);

            Color start = directionalLight.color; // may not be original one
            for (IEnumerator<float> lerper = MathfExtensions.LerpInTime(0, 1, GlobalBehaviour.AnimationLerpDuration)
                ; lerper.MoveNext();)
            {
                directionalLight.color = Color.Lerp(start, darkColor, lerper.Current);
                yield return null;
            }

            while (rain_wait.MoveNext())
                yield return null;

            for (IEnumerator<float> lerper = MathfExtensions.LerpInTime(0, 1, GlobalBehaviour.AnimationLerpDuration)
                ; lerper.MoveNext();)
            {
                directionalLight.color = Color.Lerp(darkColor, light_colour, lerper.Current);
                yield return null;
            }

            Destroy(active_rainwork);
        }
    }
}