using UnityEngine;
using CommonAI.Zone;
using System.Collections.Generic;
using System;
using CommonLang;
using CommonAI.Zone.Helper;
using CommonAI.ZoneClient;

namespace CommonAIClient.Unity.Battle
{
    public partial class ComAIUnit
    {
        private HashMap<UnitActionStatus, ActionStack> mActionStatus = new HashMap<UnitActionStatus, ActionStack>();
        private ActionStatus mCurrentActionStatus;
        private ActionStatus mLockActionStatus;

        protected ActionStatus CurrentActionStatus
        {
            get { return mCurrentActionStatus; }
        }

        //注册动作控制对象//
        protected virtual void InitActionStatus()
        {
//             RegistAction(UnitActionStatus.Pause, new ActionStatus("f_idle", true, WrapMode.Loop));
//             RegistAction(UnitActionStatus.Idle, new ActionStatus("f_idle", true, WrapMode.Loop));
//             RegistAction(UnitActionStatus.Move, new ActionStatus("f_run", true, WrapMode.Loop));
//             RegistAction(UnitActionStatus.Skill, new SkillActionStatus("f_skill_01"));
//             RegistAction(UnitActionStatus.Damage, new ActionStatus("f_hurt"));
//             RegistAction(UnitActionStatus.Dead, new ActionStatus("f_death", true));
//             RegistAction(UnitActionStatus.Stun, new ActionStatus("f_stun"));
//             RegistAction(UnitActionStatus.Pick, new ActionStatus("f_pick"));
//             RegistAction(UnitActionStatus.Chaos, new ActionStatus("f_chaos"));
//             RegistAction(UnitActionStatus.Escape, new ActionStatus("f_escape"));
//             RegistAction(UnitActionStatus.Spawn, new ActionStatus("f_spawn"));
//             RegistAction(UnitActionStatus.Rebirth, new ActionStatus("f_rebirth"));
            
            //从配置表初始化动作列表//
            foreach (UnitActionStatus st in Enum.GetValues(typeof(UnitActionStatus)))
            {
                var action = ZUnit.Templates.GetDefinedUnitAction(st);
                if (action != null)
                {
                    RegistAction(st, new DefinedActionStatus(st.ToString(), action));
                }
            }
            RegistAction(UnitActionStatus.Skill, new SkillActionStatus("f_skill_01"));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="st"></param>
        /// <param name="action">If return True , break</param>
        public bool ForeachAction(UnitActionStatus st, Predicate<ActionStatus> action)
        {
            ActionStack stack;
            if (mActionStatus.TryGetValue(st, out stack))
            {
                return stack.Foreach(action);
            }
            return false;
        }

        public ActionStatus ReplaceAction(UnitActionStatus st, ActionStatus dst)
        {
            ActionStack stack;
            if (mActionStatus.TryGetValue(st, out stack))
            {
                var ret = stack.Remove(dst.Key);
                if (ret != null)
                {
                    stack.Add(dst);
                    ChangeAction(ZUnit.CurrentState);
                }
                return ret;
            }
            return null;
        }

        public ActionStatus RegistAction(UnitActionStatus st, ActionStatus status)
        {
            ActionStack stack;
            if (mActionStatus.TryGetValue(st, out stack) == false)
            {
                stack = new ActionStack();
                mActionStatus.Add(st, stack);
            }
            stack.Add(status);
            ChangeAction(ZUnit.CurrentState);
            return status;
        }

        public ActionStatus RemoveAction(UnitActionStatus st, string key)
        {
            ActionStack stack;
            if (mActionStatus.TryGetValue(st, out stack))
            {
                var status = stack.Remove(key);
                if (status != null)
                {
                    ChangeAction(ZUnit.CurrentState);
                }
                return status;
            }
            return null;
        }

        public ActionStatus GetTopActionStatus(CommonAI.Zone.Helper.UnitActionStatus st)
        {
            ActionStack stack;
            if (mActionStatus.TryGetValue(st, out stack))
            {
                return stack.Top;
            }
            return null;
        }

        public void SetLockActionStatus(ActionStatus status)
        {
            if (status != null)
            {
                mLockActionStatus = status;
                if (status != mCurrentActionStatus)
                {
                    if (mCurrentActionStatus != null)
                    {
                        mCurrentActionStatus.OnStop(this);
                    }
                    mCurrentActionStatus = status;
                    if (mCurrentActionStatus != null)
                    {
                        mCurrentActionStatus.OnStart(this);
                    }
                }
            }
            else
            {
                mLockActionStatus = null;
                ChangeAction(ZUnit.CurrentState);
            }
        }

        protected virtual void ChangeAction(UnitActionStatus st)
        {
            if (mLockActionStatus == null)
            {
                var newAction = GetTopActionStatus(st);
                if (newAction != null)
                {
                    if (mCurrentActionStatus != null)
                    {
                        mCurrentActionStatus.OnStop(this);
                    }
                    mCurrentActionStatus = newAction;
                    if (mCurrentActionStatus != null)
                    {
                        mCurrentActionStatus.OnStart(this);
                    }
                }
            }
        }
        protected virtual void UpdateAction(float deltaTime)
        {
            if (mCurrentActionStatus != null)
            {
                mCurrentActionStatus.OnUpdate(this, deltaTime);
            }
        }


        internal class ActionStack
        {
            readonly HashMap<string, ActionStatus> Map = new HashMap<string, ActionStatus>();
            readonly List<ActionStatus> List = new List<ActionStatus>();
            public ActionStatus Top
            {
                get
                {
                    if (List.Count > 0) return List[0];
                    return null;
                }
            }

            internal ActionStack() { }
            public bool Foreach(Predicate<ActionStatus> action)
            {
                using (var list = ListObjectPool<ActionStatus>.AllocAutoRelease(List))
                {
                    foreach (var a in list)
                    {
                        if (action(a))
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
            public void Add(ActionStatus status)
            {
                Map.Add(status.Key, status);
                List.Add(status);
                List.Sort();
            }
            public ActionStatus Remove(string key)
            {
                var ret = Map.RemoveByKey(key);
                if (ret != null)
                {
                    List.Remove(ret);
                }
                return ret;
            }
        }

        public class ActionStatus : IComparable<ActionStatus>
        {
            private readonly string mKey;
            public string Key { get { return mKey; } }
            public int Priority { get; set; }
            public string ActionName { get; set; }
            public bool CrossFade { get; set; }
            public WrapMode WrapMode { get; set; }
            public float Speed { get; set; }

            public ActionStatus(string key, bool crossFade = false,
                WrapMode wrapMode = WrapMode.Once, float speed = 1f)
            {
                this.mKey = key;
                this.ActionName = key;
                this.WrapMode = wrapMode;
                this.CrossFade = crossFade;
                this.Speed = speed;
            }
            public virtual void OnStart(ComAIUnit owner)
            {
                owner.PlayAnim(ActionName, CrossFade, WrapMode, Speed);
            }
            public virtual void OnStop(ComAIUnit owner)
            {
            }
            public virtual void OnUpdate(ComAIUnit owner, float deltaTime) { }

            public virtual int CompareTo(ActionStatus other)
            {
                return this.Priority - other.Priority;
            }
        }

        public class DefinedActionStatus : ActionStatus
        {
            protected UnitActionDefinitionMap.UnitAction mData;
            protected Queue<UnitActionDefinitionMap.UnitActionKeyFrame> mActionQueue = new Queue<UnitActionDefinitionMap.UnitActionKeyFrame>();
            protected UnitActionDefinitionMap.UnitActionKeyFrame mCurrentAction;
            protected int mCurrentPassTime;

            public DefinedActionStatus(string key, UnitActionDefinitionMap.UnitAction data)
                : base(key)
            {
                this.mData = data;
                if (data.ActionQueue.Count > 0)
                {
                    var a = data.ActionQueue[0];
                    this.ActionName = a.ActionName;
                    this.WrapMode = a.Cycle ? WrapMode.Loop : WrapMode.Once;
                    this.CrossFade = a.CrossFade;
                    this.Speed = a.Speed;
                }
            }
            public override void OnStart(ComAIUnit owner)
            {
                mActionQueue.Clear();
                for (int i = 0; i < mData.ActionQueue.Count; i++)
                {
                    this.mActionQueue.Enqueue(mData.ActionQueue[i]);
                }
                NextAction(owner);
            }
            public override void OnUpdate(ComAIUnit owner, float deltaTime)
            {
                this.mCurrentPassTime += (int)(owner.ZUnit.Parent.CurrentIntervalMS);
                if (mActionQueue.Count > 0 && mCurrentAction != null)
                {
                    if (mCurrentPassTime >= mCurrentAction.TimeMS)
                    {
                        NextAction(owner);
                    }
                }
            }
            protected virtual void NextAction(ComAIUnit owner)
            {
                if (mActionQueue.Count > 0)
                {
                    mCurrentAction = mActionQueue.Dequeue();
                    if (mCurrentAction != null)
                    {
                        this.ActionName = mCurrentAction.ActionName;
                        this.WrapMode = mCurrentAction.Cycle ? WrapMode.Loop : WrapMode.Once;
                        this.CrossFade = mCurrentAction.CrossFade;
                        this.Speed = mCurrentAction.Speed;
                        owner.PlayAnim(this.ActionName, this.CrossFade, this.WrapMode, this.Speed);
                    }
                }
                mCurrentPassTime = 0;
            }
        }

        public class SkillActionStatus : ActionStatus
        {
            private ZoneUnit.SkillState mSkill;
            private Queue<UnitActionData> mActionQueue = new Queue<UnitActionData>();
            private UnitActionData mCurrentAction;
            private int mCurrentPassTime;

            public SkillActionStatus(string key) : base(key)
            {
            }
            protected void NextAction(ComAIUnit owner)
            {
                if (mActionQueue.Count > 0)
                {
                    mCurrentAction = mActionQueue.Dequeue();
                    if (mCurrentAction != null)
                    {
                        ActionName = mCurrentAction.ActionName;
                        WrapMode = mCurrentAction.IsCycAction ? WrapMode.Loop : WrapMode.Once;
                        owner.PlayAnim(ActionName, false, WrapMode, Speed);
                    }
                }
                else
                {
                    mCurrentAction = null;
                }
                mCurrentPassTime = 0;

            }
            public override void OnUpdate(ComAIUnit owner, float delteTime)
            {
                if (mSkill != null && !mSkill.Data.IsSingleAction)
                {
                    this.mCurrentPassTime += (int)(owner.ZUnit.Parent.CurrentIntervalMS);
                    if (mCurrentAction != null)
                    {
                        if (mCurrentPassTime >= mCurrentAction.TotalTimeMS)
                        {
                            // 下段动作 //
                            NextAction(owner);
                        }
                    }
                }
            }
            public virtual void ZUnit_OnLaunchSkill(ComAIUnit unit, ZoneUnit.SkillState skill, CommonAI.Zone.UnitLaunchSkillEvent evt)
            {
                mSkill = skill;
                Speed = mSkill.ActionSpeed;
                mActionQueue.Clear();
                if (skill.Data.IsSingleAction)
                {
                    this.mActionQueue.Enqueue(skill.Data.ActionQueue[evt.action_index]);
                }
                else
                {
                    for (int i = evt.action_index; i < skill.Data.ActionQueue.Count; i++)
                    {
                        this.mActionQueue.Enqueue(skill.Data.ActionQueue[i]);
                    }
                }
                NextAction(unit);
            }
            public virtual void ZUnit_OnSkillActionChanged(ComAIUnit owner, byte index)
            {
                var skill = mSkill.Data.ActionQueue[index];
                if (skill != null)
                {
                    while (mActionQueue.Count > 0)
                    {
                        var sk = mActionQueue.Peek();
                        if (sk == skill)
                        {
                            NextAction(owner);
                            break;
                        }
                        else
                        {
                            mActionQueue.Dequeue();
                        }
                    }
                }
            }
        }
    }

}