using CommonAI.RTS.Manhattan;
using CommonAI.Zone.Helper;
using CommonLang;
using CommonLang.Vector;
using System;

namespace CommonAI.ZoneClient.Agent
{
    public class ActorMoveAgent : AbstractAgent
    {
        public float TargetX { get { return target.X; } }
        public float TargetY { get { return target.Y; } }
        public AstarManhattan.FindPathResult Result { get; private set; }
        private float EndDistance { get; set; }
        public UnitActionStatus MoveState { get; set; }
        public object UserData { get; set; }
        public override bool IsEnd { get { return way_points == null; } }
        public override bool IsDuplicate { get { return false; } }
        public bool IsPause { get; set; }
        public System.Action<float, float, UnitActionStatus> UpdateCheck;
        private float fSeekDstRadius = 3f;

        public AstarManhattan.MWayPoint WayPoints { get { return way_points; } }

        private AstarManhattan.MWayPoint way_points;
        private Vector2 target;
        private float cur_dir = 0;
        private bool auto_adjust = false;
        private Vector2 cur_pos = new Vector2();
        private bool bNeedFixShake = false;

        public ActorMoveAgent(float toX, float toY, float endDistance, UnitActionStatus st = UnitActionStatus.Move, bool autoAdjust = true, object ud = null, float seekDstRadius = 3.0f)
        {
            this.target = new Vector2(toX, toY);
            this.auto_adjust = autoAdjust;
            this.EndDistance = endDistance;
            this.MoveState = st;
            this.UserData = ud;
            this.fSeekDstRadius = seekDstRadius;
        }
        protected override void OnInit(ZoneActor actor)
        {
            this.Owner.OnDoEvent += Owner_OnDoEvent;
            this.OnEnd += ActorMoveAgent_OnEnd;
            this.Start();
        }
        private void ActorMoveAgent_OnEnd(AbstractAgent agent)
        {
            if (this.Owner != null)
            {
                this.Owner.OnDoEvent -= Owner_OnDoEvent;
            }

        }
        protected override void OnDispose()
        {
            this.Owner.OnDoEvent -= Owner_OnDoEvent;
            base.OnDispose();
            way_points = null;
        }

        public override void Abort()
        {
            base.Abort();
            Stop();
        }

        private void Owner_OnDoEvent(ZoneObject obj, Zone.ObjectEvent e)
        {
            if (e is Zone.UnitForceSyncPosEvent)
            {
                this.Abort();
            }
        }

        //目标移动位置了,重新规则路线
        public void RelocateTarget(float x, float y)
        {
            target.SetX(x);
            target.SetY(y);
            Start();
        }

        /// <summary>
        /// 再次开始
        /// </summary>
        public void Start()
        {
            float distance = MathVector.getDistance(Owner.X, Owner.Y, TargetX, TargetY);
            if (distance <= EndDistance)
            {
                Result = AstarManhattan.FindPathResult.Destination;
                return;
            }

            float destX = TargetX, destY = TargetY;
            if(Layer.TryTouchMap(Owner, destX, destY))
            {
                way_points = null;
                Result = AstarManhattan.FindPathResult.NoWay;
                //目的地不可到达
                if (!auto_adjust)
                {
                    return;
                }

                //优化为附近可到达区域
                float fixx = -1, fixy = -1;
                if (!Owner.Parent.GetNearWay(Owner.X, Owner.Y, destX, destY, fSeekDstRadius, ref fixx, ref fixy))
                {
                    return;
                }
                destX = fixx;
                destY = fixy;
            }

            Result = Owner.Parent.FindPathResult(Owner.X, Owner.Y, destX, destY, out way_points);
            if (Result == AstarManhattan.FindPathResult.Cross)
            {
                if (way_points == null || Layer.TryTouchMap(Owner, way_points.PosX, way_points.PosY))
                {
                    Result = AstarManhattan.FindPathResult.NoWay;
                }
                else if (way_points != null && way_points.Next != null)
                {
                    bNeedFixShake = true;
                }
            }
        }

        /// <summary>
        /// 外部打断寻路.
        /// </summary>
        public void Stop()
        {
            this.way_points = null;
        }

        static string GetStackTraceModelName()
        {
            //当前堆栈信息
            System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
            System.Diagnostics.StackFrame[] sfs = st.GetFrames();
            string _filterdName = "ResponseWrite,ResponseWriteError,";
            string _fullName = string.Empty, _methodName = string.Empty;
            for (int i = 1; i < sfs.Length; ++i)
            {
                if (System.Diagnostics.StackFrame.OFFSET_UNKNOWN == sfs[i].GetILOffset()) break;
                _methodName = sfs[i].GetMethod().Name;
                if (_filterdName.Contains(_methodName)) continue;
                _fullName = _methodName + "()->" + _fullName;
            }
            st  = null;
            sfs = null;
            _filterdName = _methodName = null;
            return _fullName.TrimEnd('-', '>');
        }

        private void Turn(int intervalMS)
        {
            if (way_points != null)
            {
                float direction = MathVector.getDegree(cur_pos.X, cur_pos.Y, way_points.PosX, way_points.PosY);
                this.cur_dir = MoveHelper.DirectionChange(
                           direction,
                           cur_dir,
                           Owner.TurnSpeedSEC,
                           intervalMS);
            }
        }

        //解决快速连续寻路时,移动方向会一直摇摆的问题
        private void fixShake(Vector2 cur, AstarManhattan.MWayPoint head)
        {
            if(head.Next != null)
            {
                TVector2 nowPos = new TVector2(cur.X, cur.Y);
                TVector2 vcFirst = new TVector2(head.PosX, head.PosY);
                TVector2 vcNext = new TVector2(head.Next.PosX, head.Next.PosY);
                TVector2 v1 = nowPos - vcFirst;
                TVector2 v2 = vcNext - vcFirst;
                float a = (float)Math.Atan2(v1.Y, v1.X);
                float b = (float)Math.Atan2(v2.Y, v2.X);
                if(float.Equals(a, b) || float.Equals(Math.PI, Math.Abs(a - b)))
                {
                    //现在的点就是在连线之间,或延长线上
                    TVector2 v3 = nowPos - vcNext;
                    if (v3.X * v2.X <= 0 && v3.Y * v2.Y <= 0)
                    { 
                        //如果当前点在两路点连线之间,就不能朝vcFirst移动了(这样就会倒退,造成摇摆)
                        head.SetPos(nowPos.X, nowPos.Y);
                    }
                }
                else
                {
                    //当前位置不在两个路点的连线上
                    float dis = nowPos.DistanceTo(vcFirst) * (float)Math.Cos(Math.Abs(a - b));
                    float dx = (float)(Math.Cos(b) * dis);
                    float dy = (float)(Math.Sin(b) * dis);
                    //如果垂足在两路点连线之外,初始路点不变;如果在连线之间,将寻路起点设为垂足
                    if (dx * v2.X > 0)
                    {
                        head.SetPos(vcFirst.X + dx, vcFirst.Y + dy);
                    }
                }
            }
        }

        protected override void BeginUpdate(int intervalMS)
        {
            if (way_points == null)
            {
                return;
            }
            if (IsPause || Owner.CurrentState == UnitActionStatus.Spawn)
            {
                return;
            }
            if (!Owner.IsCanControlMove)
            {
                this.Abort();
                return;
            }

            cur_pos.SetX(Owner.X);
            cur_pos.SetY(Owner.Y);
            cur_dir = Owner.Direction;
            if (way_points != null)
            {
                float disSquare = MathVector.getDistanceSquare(cur_pos.X, cur_pos.Y, TargetX, TargetY);
                if (disSquare < EndDistance * EndDistance)
                {
                    this.Stop();
                    return;
                }
            }

            if (bNeedFixShake)
            {
                fixShake(cur_pos, way_points);
                bNeedFixShake = false;
            }
            float length = MoveHelper.GetDistance(intervalMS, Owner.MoveSpeedSEC);
            float distance = MathVector.getDistance(cur_pos.X, cur_pos.Y, way_points.PosX, way_points.PosY);
            if (MathVector.moveTo(cur_pos, way_points.PosX, way_points.PosY, length))
            {
                this.way_points = way_points.Next;
                if (distance < length && way_points != null)
                {
                    MathVector.moveTo(cur_pos, way_points.PosX, way_points.PosY, length - distance);
                }
            }
            else
            {
                Turn(intervalMS);
            }
            if (Layer.TryTouchMap(Owner, cur_pos.X, cur_pos.Y))
            {
                this.Abort();
            }
            else
            {
                Owner.SendUpdatePos(cur_pos.X, cur_pos.Y, cur_dir, MoveState);
                if (UpdateCheck != null)
                {
                    UpdateCheck(TargetX, TargetY, MoveState);
                }
            }
        }

        public bool TryStep()
        {
            if (way_points != null)
            {
                float px = Owner.X;
                float py = Owner.Y;
                int intervalMS = Layer.CurrentIntervalMS;
                float length = MoveHelper.GetDistance(intervalMS, Owner.MoveSpeedSEC);
                float distance = MathVector.getDistance(px, py, way_points.PosX, way_points.PosY);
                MathVector.moveTo(ref px, ref py, way_points.PosX, way_points.PosY, length);
                if (Layer.TryTouchMap(Owner, px, py))
                {
                    return false;
                }
                return true;
            }
            return false;
        }
    }
}