using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MovementEffects;
namespace WK
{
    /// 
    /// 公共状态机
    /// 
    public class AsyncStatemachine
    {
        public delegate void LoadStateComplete();
        public abstract class State
        {
            public AsyncStatemachine owner;
            public int stateID; // stateID可以是string.GetHashCode() 以提供扩展性
            public abstract IEnumerator OnStart();
            public abstract IEnumerator OnEnd();
            public abstract void OnUpdate(float deltaTime);
        }
        public const int NULL_STATE_ID = -1;
        public const float COROUINT_DELTIME = 0.01f;
        private Dictionary allState = new Dictionary();
        private int curStateID = NULL_STATE_ID;
        private int stateIDDistributor = 0;
        // 在状态切换完成前为false,不能进行update
        private bool isUpdateActive = false;
        /// 
        /// 添加状态,并返回在状态机中的ID
        /// 
        /// 
        /// 
        public int RegisterState(State newState)
        {
            if (newState == null)
                return NULL_STATE_ID;
            if (stateIDDistributor + 1 >= int.MaxValue)
            {
                LogHelper.LogError("状态机添加状态失败,此状态机中添加的状态过多");
                return NULL_STATE_ID;
            }
            ++stateIDDistributor;
            if (!allState.ContainsKey(stateIDDistributor))
            {
                newState.owner = this;
                newState.stateID = stateIDDistributor;
                allState.Add(stateIDDistributor, newState);
                return stateIDDistributor;
            }
            LogHelper.LogError("状态机添加状态失败,状态已经存在");
            return NULL_STATE_ID;
        }
        public bool RemoveState(State state)
        {
            if (state != null)
            {
                return RemoveState(state.stateID);
            }
            LogHelper.LogError("状态机删除状态失败,状态为空");
            return false;
        }
        public bool RemoveState(int stateID)
        {
            if (allState.ContainsKey(stateID))
            {
                allState.Remove(stateID);
                return true;
            }
            LogHelper.LogError("状态机删除状态失败,该状态不存在,stateID = " + stateID);
            return false;
        }
        /// 
        /// 开始运行状态机
        /// 
        /// 
        /// 
        public bool Start(int stateID)
        {
            if (!HasBegin())
            {
                ForceGotoState(stateID);
                return true;
            }
            return false;
        }
        /// 
        /// 结束状态机
        /// 
        /// 
        public bool Stop()
        {
            if (HasBegin())
            {
                ForceGotoState(NULL_STATE_ID);
                return true;
            }
            return false;
        }
        /// 
        /// 更新状态机
        /// 
        /// 
        public void Update(float deltaTime)
        {
            if (HasBegin())
            {
                UpdateState(curStateID, deltaTime);
            }
        }
        public bool GotoState(int stateID, bool skipStartFunc = false, bool bForce = false, LoadStateComplete callback = null)
        {
            if (HasBegin())
                return ForceGotoState(stateID, skipStartFunc, bForce, callback);
            return false;
        }
        public bool ForceGotoState(int nextStateID, bool skipStartFunc = false, bool bForce = false, LoadStateComplete callback = null)
        {
            if (curStateID != nextStateID || bForce)
            {
                Timing.Instance.RunCoroutineOnInstance(AyncForceGotoState(nextStateID, skipStartFunc, callback));
                return true;
            }
            if (callback != null)
                callback();
            return false;
        }
        /// 
        /// 异步的切换到某个状态
        /// 
        /// 
        private IEnumerator AyncForceGotoState(int nextStateID, bool skipStartFunc = false, LoadStateComplete callback = null)
        {
            isUpdateActive = false;
            CoroutineHandle handle = Timing.RunCoroutine(EndState(curStateID));
            while (handle.IsRunning)
            {
                yield return Timing.WaitForSeconds(COROUINT_DELTIME);
            }
            curStateID = nextStateID;
            if (!skipStartFunc)
            {
                CoroutineHandle handle2 = Timing.RunCoroutine(StartState(curStateID));
                while (handle2.IsRunning)
                {
                    yield return Timing.WaitForSeconds(COROUINT_DELTIME);
                }
            }
            if (callback != null)
                callback();
            isUpdateActive = true;
        }
        public int GetCurStateID()
        {
            return curStateID;
        }
        public bool HasBegin()
        {
            if (curStateID != NULL_STATE_ID && allState.ContainsKey(curStateID))
                return true;
            return false;
        }
        public bool IsInState(int stateID)
        {
            if (HasBegin())
            {
                return curStateID == stateID;
            }
            return false;
        }
        public State GetState(int stateID)
        {
            if (allState.ContainsKey(stateID))
            {
                return allState[stateID];
            }
            return null;
        }
        public void Clean()
        {
            allState.Clear();
            curStateID = NULL_STATE_ID;
            stateIDDistributor = 0;
        }
        public void ResetCurState()
        {
            curStateID = NULL_STATE_ID;
        }
        private IEnumerator StartState(int stateID)
        {
            if (HasBegin())
            {
                State state = GetState(stateID);
                if (state != null)
                {
                    CoroutineHandle handle = Timing.RunCoroutine(state.OnStart());
                    while (handle.IsRunning)
                    {
                        yield return Timing.WaitForSeconds(COROUINT_DELTIME);
                    }
                }
            }
        }
        private IEnumerator EndState(int stateID)
        {
            if (HasBegin())
            {
                State state = GetState(stateID);
                if (state != null)
                {
                    CoroutineHandle handle = Timing.RunCoroutine(state.OnEnd());
                    while (handle.IsRunning)
                    {
                        yield return Timing.WaitForSeconds(COROUINT_DELTIME);
                    }
                }
            }
        }
        private void UpdateState(int stateID, float deltaTime)
        {
            if (HasBegin())
            {
                State state = GetState(stateID);
                if (state != null)
                    state.OnUpdate(deltaTime);
            }
        }
    }
}