using CommonAI.Zone;
using CommonAI.Zone.Helper;
using CommonAI.Zone.Instance;
using CommonLang;
using CommonLang.Vector;
using System;
using static CommonAI.Zone.Instance.InstanceUnit;

namespace XmdsCommonServer.Plugin.Units
{
    public class XmdsInstanceUnitStateMachine
    {
        abstract public class XmdsStateFollow : InstanceUnit.State
        {
            public enum MoveState
            {
                Hold,
                Move,
            }

            // 被追目标
            private IPositionObject target;
            // 如果被单位碰撞,则向左或右挪动//
            private MoveAI moveAI;
            // 检测切换走停的间隔时间 //
            readonly private State<MoveState> state = new State<MoveState>(MoveState.Move);
            readonly private bool begin_in_min_range;
            // 检测切换走停的间隔时间 //
            private TimeInterval<int> start_move_hold_time;

            public abstract bool IsActive { get; }

            public bool IsNoWay { get { if (moveAI != null) { return moveAI.IsNoWay; } return false; } }
            public IPositionObject Target { get { return target; } }
            public MoveState FollowState { get { return state.Value; } }
            /// <summary>
            /// Hold到Move之间的检测间隔
            /// </summary>
            public int StartMoveHoldTimeMS
            {
                get { return (start_move_hold_time != null) ? start_move_hold_time.IntervalTimeMS : 0; }
                set
                {
                    if (value > 0 && value != StartMoveHoldTimeMS)
                    {
                        start_move_hold_time = new TimeInterval<int>(value);
                    }
                }
            }

			public virtual void MarkCannotArriveToAttack() { }


			public XmdsStateFollow(InstanceUnit unit, InstanceZoneObject target, bool beginInMinRange = true) : base(unit)
            {
                this.begin_in_min_range = beginInMinRange;
                this.target = target;
                this.moveAI = new MoveAI(unit);
            }

            public void ChangeMoveTarget(IPositionObject target)
            {
                if (moveAI.Target != target)
                {
                    this.target = target;
                    this.moveAI = new MoveAI(unit);
                }
            }

            public override bool onBlock(InstanceUnit.State new_state)
            {
                return true;
            }

            protected override void onStart()
            {
                if (cheekBeginInRange())
                {
                    state.ChangeState(MoveState.Hold);
                    unit.SetActionStatus(UnitActionStatus.Idle);
                }
                else
                {
                    state.ChangeState(MoveState.Move);
                    unit.SetActionStatus(UnitActionStatus.Move);
                    moveAI.FindPath(target);
                }
            }

            protected override void onUpdate()
            {
                if (!IsActive)
                {
                    unit.doSomething();
                }
                else
                {
                    switch (FollowState)
                    {
                        case MoveState.Move:
							// 进入最小追踪距离 //
							if (CheckTargetInMinRange() == true)
                            {
                                changeToHold();
                            }
                            else 
                            {
								if (!this.unit.IsCannotMove)
								{
									if (moveAI.Target == null)
									{
										moveAI.FindPath(target);
									}

									moveAI.Update();
								}

								if (moveAI.IsNoWay)
								{
									if (CheckTargetInMaxRange() == true)
									{
										changeToHold();
									}
									else
									{
										//System.Console.WriteLine("标记不可攻击:" + this.unit.Name);
										this.MarkCannotArriveToAttack();
										unit.doSomething();
									}
								}								
                            }
                            break;
                        case MoveState.Hold:
                            // 超过最大追踪范围 //
                            if ((start_move_hold_time == null || start_move_hold_time.Update(zone.UpdateIntervalMS)) && (CheckTargetInMaxRange() == false))
                            {
                                changeToMove();
                            }
                            else
                            {
                                onUpdateFollowed(target);
                            }
                            break;
                    }
                }
            }

            protected override void onStop() { }

            private bool cheekBeginInRange()
            {
                if (begin_in_min_range)
                {
                    return CheckTargetInMinRange();
                }
                else
                {
                    return CheckTargetInMaxRange();
                }
            }
            private void changeToMove()
            {
                state.ChangeState(MoveState.Move);
                unit.SetActionStatus(UnitActionStatus.Move);
                moveAI.FindPath(target);
                onInRangeChanged();
                onChangedToMove(target);
            }
            private void changeToHold()
            {
                state.ChangeState(MoveState.Hold);
                unit.SetActionStatus(UnitActionStatus.Idle);
                moveAI.Pause();
                onInRangeChanged();
                onChangedToHold(target);
            }

            /// <summary>
            /// 检查目标是否在跟随范围内。
            /// 检测为True,停止移动。
            /// </summary>
            protected abstract bool CheckTargetInMinRange();
            /// <summary>
            /// 检查目标是否在跟随范围内。
            /// 检测为False,开始移动。
            /// </summary>
            protected abstract bool CheckTargetInMaxRange();

            /// <summary>
            /// 行为改变
            /// </summary>
            protected virtual void onInRangeChanged()
            {

            }
            /// <summary>
            /// 目标已经被追踪到
            /// </summary>
            /// <param name="target"></param>
            protected virtual void onUpdateFollowed(IPositionObject target)
            {

            }
            /// <summary>
            /// 开始移动
            /// </summary>
            /// <param name="target"></param>
            protected virtual void onChangedToMove(IPositionObject target)
            {

            }
            /// <summary>
            /// changeToHold
            /// </summary>
            /// <param name="target"></param>
            protected virtual void onChangedToHold(IPositionObject target)
            {

            }
        }

        public class XmdsStateFollowAndAttack : XmdsStateFollow
        {
            /*readonly private*/
            InstanceUnit targetUnit;
            private SkillTemplate.CastTarget expectTarget;
            private TimeInterval<int> checkAdjustLaunchSkillTime;
            private InstanceUnit.SkillState launchSkill;
            private bool can_auto_focus_near_target;
            private bool can_random_skill = true;
            private bool can_attack = true;

            private float min_distance;
            private float max_distance;

            /// <summary>
            /// 是否追踪
            /// </summary>
            /// <returns></returns>
            override public bool IsActive
            {
                get { return can_attack && targetUnit.IsActive; }
            }
            public InstanceUnit.SkillState ExpectSkillState
            {
                get { return launchSkill; }
                set
                {
                    launchSkill = value;
                    expectTarget = value.Data.ExpectTarget;
                }
            }

			/** 标记不能达到目的,并攻击目标 */
			public override void MarkCannotArriveToAttack()
			{
				this.can_attack = false;
			}

            public SkillTemplate ExpectSkill
            {
                get { return (launchSkill != null) ? launchSkill.Data : unit.DefaultSkill; }
            }
            public InstanceUnit TargetUnit
            {
                get { return targetUnit; }
                set { targetUnit = value; }
            }

            public SkillTemplate.CastTarget ExpectTarget
            {
                get { return expectTarget; }
            }
            public bool IsLaunchRandomSkill
            {
                get { return can_random_skill; }
                set { can_random_skill = value; }
            }
            public bool IsAutoFocusNearTarget
            {
                get { return can_auto_focus_near_target; }
                set { can_auto_focus_near_target = value; }
            }

            public XmdsStateFollowAndAttack(
                InstanceUnit unit,
                InstanceUnit target,
                SkillTemplate.CastTarget expectTarget = SkillTemplate.CastTarget.Enemy,
                bool autoFocusNearTarget = false,
                InstanceUnit.SkillState lanuchSkill = null)
                : base(unit, target, false)
            {
                this.targetUnit = target;
                this.expectTarget = expectTarget;
                this.can_auto_focus_near_target = autoFocusNearTarget;
                this.StartMoveHoldTimeMS = zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS;
                this.launchSkill = lanuchSkill;
            }
            protected override void onStart()
            {
				this.resetMaxMinRange();

				this.checkAdjustLaunchSkillTime = new TimeInterval<int>(zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS);
                this.launchSkill = unit.getRandomLaunchableExpectSkill(expectTarget);
                base.onStart();
            }
            protected override void onUpdateFollowed(IPositionObject target)
            {
                this.can_attack = zone.IsAttackable(unit, targetUnit, expectTarget, AttackReason.Tracing, this.ExpectSkill);
                if (can_attack)
                {
					unit.faceTo(target.X, target.Y);
                    if (TryLaunchSkill() == null)
                    {
                        SkillTemplate expect_skill = ExpectSkill;
                        if (expect_skill != null && expect_skill.AttackKeepRange > 0)
                        {
                            if (checkAdjustLaunchSkillTime.IntervalTimeMS == 0 || checkAdjustLaunchSkillTime.Update(zone.UpdateIntervalMS))
                            {
                                if (CUtils.RandomPercent(zone.RandomN, zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_ADJUST_ESCAPE_PCT))
                                {
                                    unit.startAdjustLaunchSkill(expect_skill, targetUnit, this.OnEndAdjustKeepRange);
                                }
                            }
                        }
                    }
                }
            }
            protected override void onStop()
            {
                base.onStop();
            }
            protected override void onUpdate()
            {
                if (IsActive)
                {
                    resetMaxMinRange();
                }
                base.onUpdate();
            }

            private void resetMaxMinRange()
            {
                var expect_skill = ExpectSkillState;
                min_distance = unit.BodyBlockSize + targetUnit.BodyBlockSize;
                max_distance = Math.Max(min_distance, unit.BodyBlockSize + targetUnit.BodyHitSize);
                bool needCheck = true;
                if (expect_skill == null)
                {
                    var new_skill = unit.getRandomLaunchableExpectSkill(targetUnit, expectTarget, AttackReason.Tracing, true);
                    if (new_skill != null)
                    {
                        ExpectSkillState = new_skill;
                        expect_skill = new_skill;
                        needCheck = false;
                    }
                }

                if (expect_skill != null)
                {
                    if (can_random_skill)
                    {
                        if (needCheck == true && !expect_skill.checkTargetRange(targetUnit))
                        {
                            var new_skill = unit.getRandomLaunchableExpectSkill(targetUnit, expectTarget, AttackReason.Tracing, true);
                            if (new_skill != null)
                            {
                                ExpectSkillState = new_skill;
                                expect_skill = new_skill;
                            }
                        }
                    }
                    float skill_distance = unit.GetSkillAttackRange(expect_skill.Data);
                    float half = (skill_distance - min_distance) / 2;
                    if (expect_skill.Data.AttackKeepRange > 0 && expect_skill.Data.AttackRange > expect_skill.Data.AttackKeepRange)
                    {
                        min_distance = Math.Max(min_distance, unit.GetSkillAttackRange(expect_skill.Data.AttackKeepRange) + targetUnit.BodyBlockSize);
                        max_distance = Math.Max(max_distance, min_distance);
                    }
                    else if (half > 0)
                    {
                        min_distance = min_distance + half;
                        max_distance = Math.Max(max_distance, min_distance);
                    }
                    else
                    {
                        min_distance = Math.Max(min_distance, skill_distance + targetUnit.BodyBlockSize);
                        max_distance = Math.Max(max_distance, min_distance);
                    }
                    max_distance = Math.Max(max_distance, skill_distance + targetUnit.BodyHitSize);
                }
            }

            protected override bool CheckTargetInMaxRange()
            {
                return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y);
            }
            protected override bool CheckTargetInMinRange()
            {
                return CMath.includeRoundPoint(unit.X, unit.Y, min_distance, targetUnit.X, targetUnit.Y);
            }
            protected virtual bool OnEndAdjustKeepRange(InstanceUnit.StateMove m)
            {
                unit.faceTo(Target.X, Target.Y);
                unit.changeState(this);
                return true;
            }
            protected virtual SkillTemplate TryLaunchSkill()
            {
                if (launchSkill == null)
                {
                    this.launchSkill = unit.getRandomLaunchableExpectSkill(expectTarget);
                }
                InstanceUnit.StateSkill ret = null;
				Vector2 targetPos = targetUnit == null ? null : new Vector2(targetUnit.X, targetUnit.Y);
                if (launchSkill != null)
                {
                    ret = unit.launchSkill(launchSkill.ID, new InstanceUnit.LaunchSkillParam(targetUnit.ID,	targetPos, can_auto_focus_near_target, false, 0, true, true));
                    if (ret != null)
                    {
                        return ret.SkillData;
                    }
                }
                if (can_random_skill)
                {
                    ret = unit.launchRandomSkill(expectTarget, new InstanceUnit.LaunchSkillParam(targetUnit.ID, targetPos, can_auto_focus_near_target, false, 0, true, true));
                    if (ret != null)
                    {
                        return ret.SkillData;
                    }
                }
                return null;
            }

            public void ChangeAttackTarget(InstanceUnit.SkillState expectSkill, InstanceUnit target)
            {
                TargetUnit = target;
                ExpectSkillState = expectSkill;
                ChangeMoveTarget(target);
            }
        }


        /// <summary>
        /// 嘲讽状态
        /// </summary>
        public class XmdsInstanceUnitStateMock : XmdsStateFollow, IStateNoneControllable
        {
            private InstanceUnit targetUnit;

            // 是否能攻击
            private bool can_attack = true;

            //标记失效
            public bool mMarkRemove = false;

            private float max_distance;

            public override bool IsActive
            {
                get
                {
                    return targetUnit.IsActive && !mMarkRemove;
                }
            }

            public XmdsInstanceUnitStateMock(InstanceUnit unit, InstanceUnit targetUnit)
                : base(unit, targetUnit, false)
            {
                this.targetUnit = targetUnit;
                this.StartMoveHoldTimeMS = zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS;
                this.max_distance = unit.DefaultSkill.AttackRange - targetUnit.BodyBlockSize;
            }

            public InstanceUnit GetTargetUnit() { return this.targetUnit; }

            protected override void onStart()
            {
                unit.SetActionStatus(UnitActionStatus.ServerSyncMove);
                //unit.SendForceSync();
                base.onStart();
            }

            protected override void onStop()
            {
                unit.doSomething();
                base.onStop();
            }

            public override bool onBlock(InstanceUnit.State new_state)
            {
                if (!IsActive || (unit.IsDead() && new_state is StateDead))
                {
                    return true;
                }

                if (new_state is XmdsInstanceUnitStateMock)
                {
                    return true;
                }
                else if (new_state is StateSkill)
                {
                    StateSkill ss = new_state as StateSkill;
                    // 反击或者状态解除//
                    if (ss.Skill.GetSkillType() == XmdsSkillType.normalAtk)
                    {
                        return true;
                    }
                }

                return false;
            }

            protected override void onUpdateFollowed(IPositionObject target)
            {
                this.can_attack = zone.IsAttackable(unit, targetUnit, SkillTemplate.CastTarget.Enemy, AttackReason.Tracing, this.unit.DefaultSkill);
                if (can_attack)
                {
                    unit.faceTo(targetUnit.X, targetUnit.Y);
                    unit.launchSkill(this.unit.DefaultSkillStatus(), new InstanceUnit.LaunchSkillParam(targetUnit.ID));
                }
            }

            protected override bool CheckTargetInMinRange()
            {
                return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y);
            }

            protected override bool CheckTargetInMaxRange()
            {
                return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y);
            }

            public State AsState()
            {
                return this;
            }
        }
    }
}