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

/// <summary>
/// Base class all logic blocks are derived from
/// </summary>
[System.Serializable]
public abstract class LogicBlock : ScriptableObject
{

    #region Inspector Fields
    [SerializeField]
    [Header("UI Settings")]
    [Tooltip("Color which will identify this element")]
    public Color Color;

    [SerializeField]
    [Tooltip("Name which will appear in the UI")]
    protected string _DisplayName;

    [SerializeField]
    [Header("Base Settings")]
    [Tooltip("Wait until this block is resolved before moving to next")]
    public bool WaitUntilFinished = false;

    [SerializeField]
    [Tooltip("Amount of times to run this Block before moving to next")]
    protected int RepeatAmount = 1;

    #endregion Inspector Fields

    #region ReadOnly Variables
    public string DisplayName { get { return (string.IsNullOrEmpty(_DisplayName)) ? name : _DisplayName; } }

    [HideInInspector]
    public bool hasBeenRemoved = false;
    #endregion

    #region private variables

    /// <summary>
    /// Amount of times this block has run
    /// </summary>
    protected int RepeatCount = 0;

    #endregion private variables

    #region Class Functions
    /// <summary>
    /// Runs the block
    /// </summary>
    /// <param name="player">Player which will be affected by the block</param>
    /// <returns>returns true if block is finished</returns>
    public virtual bool Run(Character player, float animationTime)
    {
        RepeatCount++;
        BlockLogic(player, animationTime);

        return isFinished();
    }

    /// <summary>
    /// Returns the amount of space this logic block takes up
    /// </summary>
    /// <returns>Int which controlls how much space this takes up</returns>
    public virtual int Size()
    {
        return 1;
    }

    /// <summary>
    /// Where derived callses should implement the logic for their classes
    /// </summary>
    /// <param name="player">Player which will be affected by the block</param>
    /// <returns>returns true if block is finished</returns>
    protected abstract void BlockLogic(Character player, float animationTime);

    /// <summary>
    /// Returns the block that the character will endUp on after they use this logic element
    /// </summary>
    /// <param name="startBlock">block character is on</param>
    /// <param name="transform">transform function will be based off</param>
    /// <param name="layerMask">layers to ignore</param>
    /// <returns>block which character will finish on after performing this function </returns>
    public abstract Block GetEndBlock(Block startBlock, Transform transform, LayerMask layerMask);

    /// <summary>
    /// Resets the block to be ready to used again
    /// </summary>
    public virtual void Reset()
    {
        RepeatCount = 0;
    }

    /// <summary>
    /// False if this block needs to be run again
    /// </summary>
    /// <returns>bool false if block needs to be run again</returns>
    public virtual bool isFinished()
    {
        return (RepeatCount == RepeatAmount);
    }


    #region Serialisation Functions
    /// <summary>
    /// Copies data from BlockToken to this Block
    /// </summary>
    /// <param name="token">Token to Copy</param>
    public virtual void CopyToken(BlockToken token)
    {
        Color = token.Color;
        _DisplayName = token._DisplayName;
        WaitUntilFinished = token.WaitUntilFinished;
        RepeatAmount = token.RepeatAmount;
        name = token.ObjectName;
    }

    /// <summary>
    /// Copies Block data to supplied token, if token is null creates new token
    /// </summary>
    /// <param name="token">token to copy data to</param>
    /// <returns></returns>
    public virtual BlockToken ToToken(BlockToken token = null)
    {
        if (token == null)
            token = new BlockToken(this);
        token.Color = Color;
        token._DisplayName = _DisplayName;
        token.WaitUntilFinished = WaitUntilFinished;
        token.RepeatAmount = RepeatAmount;
        token.ObjectName = name;

        return token;
    }
    #endregion Serialisation Functions


    public  bool isSameType(object other)
    {
        if (Object.ReferenceEquals(null, other))
            return false;

        if (Object.ReferenceEquals(other.GetType(), other))
            return false;

        LogicBlock otherLogic = other as LogicBlock;
        return !Object.ReferenceEquals(null, otherLogic)
            && string.Equals(name, otherLogic.name);
    }

    public virtual void OnDoubleClick()
    {

    }

    #endregion Class Functions
}

[System.Serializable]
public class BlockToken
{

    public System.Type blockType;

    public Color Color;

    public string _DisplayName;

    public string ObjectName;

    public bool WaitUntilFinished;

    public int RepeatAmount;

    public BlockToken(LogicBlock block)
    {
        blockType = block.GetType();
    }

    public LogicBlock ToLogicBlock()
    {

        LogicBlock retVal = (LogicBlock)ScriptableObject.CreateInstance(blockType);
        Debug.Log("type: " + retVal.GetType());
        retVal.CopyToken(this);

        return retVal;
    }

}