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

/// <summary>
/// Class which reads a a list of logic blocks and applies to a character
/// </summary>
[System.Serializable]
public class BlockReader
{
    #region Inspector Variables
    [SerializeField]
    [Tooltip("List of LogicBlocks which will affected this character")]
    public List<LogicBlock> LogicChain = new List<LogicBlock>();
    #endregion Inspector Variables

    #region Read-only Variables
    /// <summary>
    /// returns true if  all LogicBlocks in LogicChain have been completed
    /// </summary>
    public bool Finished { get { return currentBlockIndex >= LogicChain.Count; } }

    /// <summary>
    /// Fired whenever the logic chain is updated, useful for UI
    /// </summary>
    public event System.Action OnUpdate;

    /// <summary>
    /// Gets the current block in the logic chain
    /// </summary>
    public LogicBlock CurrentBlock { get { return ((currentBlockIndex < LogicChain.Count) ? LogicChain[currentBlockIndex] : null); } }

    /// <summary>
    /// Sum of size of all Blocks in the chain
    /// </summary>
    public int Length { get { int retVal = 0; LogicChain.ForEach(p => retVal += p.Size()); return retVal; } }
    #endregion Read-only Variables

    #region Private Variables
    private int currentBlockIndex;//Block currently on
    #endregion Private Variables

    #region Class Implementation
    /// <summary>
    /// Resets Block reader;
    /// </summary>
    public void Reset()
    {
        currentBlockIndex = 0;
    }

    /// <summary>
    /// Reads the current block in the logic chain
    /// </summary>
    /// <param name="character">Character to control</param>
    /// <returns>Returns false if other readers should wait for this one to run again</returns>
    public IEnumerator Read(Character character, float speedMultiplier)
    {
        if(currentBlockIndex >= LogicChain.Count)
        {
            yield break;
        }
        LogicBlock currentBlock = LogicChain[currentBlockIndex];
        
        //If the character has become stuck, they forfeit their remaining moves - we skip directly to their end of their chain
        if (character.stuck == true)
        {
            currentBlockIndex = LogicChain.Count;
            currentBlock.Reset();

            yield break;
        }

        //apply blocks to character
        yield return character.StartCoroutine(currentBlock.Run(character, speedMultiplier));

        //if the block is finished reset it and move on to next one
        //(it might not be finished if it is a for loop or something)
        if (currentBlock.isFinished())
        {
            currentBlockIndex++;
            currentBlock.Reset();
        }
    }

    /// <summary>
    /// Clears all elements out of the Block Reader
    /// </summary>
    public void Clear()
    {
        LogicChain.Clear();
        if (OnUpdate != null)
            OnUpdate.Invoke();
    }

    /// <summary>
    /// Removes the first instance found in the Block Reader
    /// </summary>
    /// <param name="item">LogicElement to remove</param>
    public void Remove(LogicBlock item)
    {
        LogicChain.Remove(item);
        if (OnUpdate != null)
            OnUpdate.Invoke();
    }

    /// <summary>
    /// Addes item to the end of the BlockReader
    /// </summary>
    /// <param name="item">item to add</param>
    public void Add(LogicBlock item)
    {
        LogicChain.Add(item);
        if (OnUpdate != null)
            OnUpdate.Invoke();
    }

    /// <summary>
    /// Inserts item at index of the Block Reader
    /// </summary>
    /// <param name="index">index to insert at</param>
    /// <param name="item">item to insert</param>
    public void Insert(int index, LogicBlock item)
    {
        LogicChain.Insert(index, item);
        if (OnUpdate != null)
            OnUpdate.Invoke();
    }
    #endregion
}