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

public class RenderPipeline : MonoBehaviour {
    public bool DebugLog = false;

    private static RenderPipeline instance;
    public static void Render(Render rend)
    {
        instance.AddRender(rend);
    }

    public static void RenderCode(RenderTrigger.FireTrigger code, float delay = 0.01f)
    {
        if (delay <= 0)
            delay = 0.001f;

        if (instance.DebugLog)
            Debug.Log("Explicitly Rendering code (may be from sound)");
        EmptyRender r = new EmptyRender(delay);
        r.AddTrigger(new RenderTrigger(code, 0.99f));
        Render(r);
    }

    public static void RenderSound(string soundName, float delay = 0.01f)
    {
        if (instance.DebugLog)
        {
            Debug.Log("Render queuing sound " + soundName);
            RenderCode(() => { Debug.Log("Render playing sound " + soundName); RenderSounds.PlaySound(soundName); }, delay);
        }
        else
            RenderCode(() => RenderSounds.PlaySound(soundName), delay);
    }

    public string DEBUG;
    private List<Render> currentRenders = new List<Render>();
    private List<Render> newRenders = new List<Render>();
    
    void Start()
    {
        instance = this;
    }

    public void AddRender(Render rend)
    {
        if (rend != null)
        {            
            rend.ForTurn = TurnBasedGameLoop.instance.SubTurnCount;            
            WrapInDelay(ref rend);
            newRenders.Add(rend);
            rend.StartRender();
        }
    }

    private void WrapInDelay(ref Render rend)
    {
        int turn = rend.ForTurn;
        var befores = currentRenders.Union(newRenders).Where(r => r.ForTurn == turn);
        float waitTime = (befores.Count() == 0) ? 0 : befores.Max(s => s.NextRenderDelay);
        if(DebugLog) 
            Debug.Log("      Adding rend " + rend + " for turn " + rend.ForTurn + " behind " + befores.Count() + " others, wait is " + waitTime);
        if (waitTime != 0)
        {
            DEBUG = rend.ForTurn + ", " + waitTime;
            rend = new RenderDelay(rend, waitTime);
        }
    }

    public bool Finished()
    {
        if (newRenders.Count != 0)
            return false;

        foreach(Render r in currentRenders)
        {
            if (r != null && !r.IsDone())
                return false;
        }
        return true;
    }

    public void CatchUpWithGame()
    {
        int loopCount = 0;
        do
        {
            AcceptNewRenders();
            foreach (Render r in currentRenders)
            {
                if (r != null && !r.IsDone())
                    r.SkipToEnd();
            }
            currentRenders.Clear();
            loopCount++;
        }
        while (newRenders.Count != 0);

        if (loopCount > 1)
            Debug.Log("!!! The Render Pipeline Catchup had to loop through " + loopCount + " times!");
    }


	void Update () 
    {
        AcceptNewRenders();
        //DEBUG = "" + currentRenders.Count;
	    foreach(Render r in currentRenders)            
        {
            if(r!=null && !r.IsDone())
                r.UpdateRender();
        }
	}

    private void AcceptNewRenders()
    {
        if (newRenders.Count == 0)
            return;

        currentRenders.AddRange(newRenders);
        newRenders.Clear();
    }
}
