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

public class FSM<T>
{
    public string CurrentState => mCurrentState != null ? mCurrentState.GetType().ToString() : "";

    private Dictionary<Type, BaseState<T>> mStateDic;
    private T mMaster;
    private BaseState<T> mCurrentState;
    private bool mActive;

    public FSM(T pMaster)
    {
        mMaster = pMaster;
        mStateDic = new Dictionary<Type, BaseState<T>>();
        mActive = true;
    }

    public void ChangeState(Type pStateType = null)
    {
        if (pStateType == null)//change to empty
        {
            if (mCurrentState != null)
                mCurrentState.ExitState(null);
            mCurrentState = null;

            return;
        }

        BaseState<T> tNextState = null;
        if (pStateType != null)
        {
            if (!mStateDic.ContainsKey(pStateType))
            {
                object tInstance = pStateType.Assembly.CreateInstance(pStateType.Name);
                if (tInstance is BaseState<T>)
                {
                    tNextState = tInstance as BaseState<T>;
                    tNextState.SetFSM(this);
                    tNextState.SetMaster(mMaster);
                    mStateDic.Add(pStateType, tNextState);
                }
                else
                {
                    Debug.LogError("State Class : " + pStateType.Name + " is not a " + typeof(BaseState<T>).Name + "!");
                }
            }
            else
            {
                tNextState = mStateDic[pStateType];
            }
        }
        else
        {
            Debug.LogError("State Class : " + pStateType.Name + " is not found!");
        }

        if (tNextState != null && tNextState != mCurrentState)
        {
            if (mCurrentState != null)
                mCurrentState.ExitState(tNextState.GetType());
            tNextState.EnterState(mCurrentState?.GetType());
            mCurrentState = tNextState;
        }
    }

    public void Execute(float pDeltaTime)
    {
        if (!mActive)
            return;

        if (mCurrentState != null)
            mCurrentState.ExecuteState(pDeltaTime);
    }

    public void SetActive(bool pActive)
    {
        mActive = pActive;
    }
}

public class BaseState<T>
{
    protected T mMaster;
    protected FSM<T> mFSM;

    public void SetFSM(FSM<T> pFSM)
    {
        mFSM = pFSM;
    }

    public void SetMaster(T pMaster)
    {
        mMaster = pMaster;
    }

    public virtual void EnterState(Type pLastState)
    {

    }

    public virtual void ExitState(Type pNextState)
    {

    }

    public virtual void ExecuteState(float pDeltaTime)
    {

    }

    protected void ChangeState(Type pType)
    {
        mFSM.ChangeState(pType);
    }
}