using CommonAI.Zone;
using CommonAI.Zone.Helper;
using CommonAI.Zone.ZoneEditor;
using CommonAIServer.Connector.Client;
using CommonLang;
using CommonLang.Log;
using CommonNetwork.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XmdsServerTestBots.Bot
{

    public class BotPlayer : IDisposable
    {
        private static Random random = new Random();
        private readonly Logger log;
        private readonly string mName;
        private readonly NetTestClient mClient;
        private readonly int mFixedIntervalMS;

        private System.Threading.Timer mTimer;
        private long mLastUpdateTime;


        public string Name { get { return mName; } }

        public bool IsRunning { get { return mClient.Client.Session.IsConnected; } }

        public BotPlayer(string name, string roomID, UnitInfo unit, int force, EditorTemplates dataroot, string connectString)
        {
            this.log = LoggerFactory.GetLogger("Bot[" + name + "]");

            CommonAI.ZoneServer.CreateUnitInfoR2B ret = new CommonAI.ZoneServer.CreateUnitInfoR2B();
            ret.UnitTemplateID = unit.TemplateID;
            ret.Force = (byte)force;

            this.mFixedIntervalMS = (1000 / dataroot.Templates.CFG.SYSTEM_FPS);
            this.mName = name;
            this.mClient = new NetTestClient(name,
                roomID, typeof(NetSession).FullName,
                connectString,
                mFixedIntervalMS,
                20, ret, false, null, dataroot);
            this.mClient.Client.Layer.ActorAdded += Layer_ActorAdded;
            this.mClient.Client.Disconnectd += Client_Disconnectd;

        }

        private void Client_Disconnectd(CommonAIClient.Client.BattleClient bc)
        {
            this.Dispose();
        }

        private void Layer_ActorAdded(CommonAI.ZoneClient.ZoneLayer layer, CommonAI.ZoneClient.ZoneActor actor)
        {
            actor.SendUnitGuard(true);
            startMoveToTarget();
        }

        public void Start()
        {
            this.mClient.Start();
            this.mTimer = new System.Threading.Timer(update, this, mFixedIntervalMS, mFixedIntervalMS);
        }

        private void update(object state)
        {
            lock (this)
            {
                long curTime = CommonLang.CUtils.CurrentTimeMS;
                if (mLastUpdateTime == 0)
                {
                    mLastUpdateTime = curTime;
                }
                int intervalMS = (int)(curTime - mLastUpdateTime);
                this.mLastUpdateTime = curTime;
                try
                {
                    updateInTarget(intervalMS);
                    this.mClient.Update(intervalMS);
                }
                catch (Exception err)
                {
                    log.Error(err.Message, err);
                }
            }
        }

        public void SendLeaveRoom()
        {
            mClient.Client.SendLeaveRoom();
        }

        public void Dispose()
        {
            try
            {
                lock (this)
                {
                    mTimer.Dispose();
                }
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
            try
            {
                mClient.Stop();
                mClient.Dispose();
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
        }


        private TimeExpire<RegionData> target_expire;

        private void startMoveToTarget()
        {
            var list = new List<RegionData>(mClient.Client.Layer.Data.Regions);
            CUtils.RandomList(random, list);
            foreach (var rd in list)
            {
                foreach (var ab in rd.GetAbilities())
                {
                    if (ab is SpawnUnitAbilityData)
                    {
                        var suab = ab as SpawnUnitAbilityData;
                        if (suab.Force != mClient.Client.Actor.Force)
                        {
                            target_expire = new TimeExpire<RegionData>(rd, random.Next(10000, 100000));
                            mClient.Client.Actor.SendUnitAttackMoveTo(rd.X, rd.Y, true);
                            return;
                        }
                    }
                }
            }
        }
        private void updateInTarget(int intervalMS)
        {
            if (target_expire != null && target_expire.Update(intervalMS))
            {
                var rg = target_expire.Tag;
                var actor = mClient.Client.Actor;
                startMoveToTarget();
                //                 if (CMath.includeRoundPoint(actor.X, actor.Y, actor.Info.GuardRange ,rg.X, rg.Y))
                //                 {
                // 
                //                 }
            }
        }
        /*
        public class MoveAgent
        {
            public delegate void UpdateAction(float x, float y, object ud);
            public delegate void EndAction(bool complete, float x, float y, object ud);
        
            UpdateAction updateAction;
            EndAction endAction;
            object userdata = null;
            float speed = 0;
            //private List<Vector2> wayPoints;
            bool isActor = false;
            List<Vector2> path;
            bool active = false;

            private float mEndDistance;

            public bool IsActive
            {
                get
                {
                    return active;
                }
            }

            public MoveAgent(bool isActor, UpdateAction updateAction, EndAction endAction)
            {
                this.isActor = isActor;
                this.updateAction = updateAction;
                this.endAction = endAction;
                path = new List<Vector2>();
            }

            public List<Vector2> StartSeek(float fromX, float fromY, float toX, float toY, float speed, float endDis, object ud)
            {
                if (active)
                {
                    fromX = path[0].x;
                    fromY = path[0].y;
                }
                AstarManhattan.MWayPoint wayPoints;
                AstarManhattan.FindPathResult ret = layer.FindPathResult(fromX, fromY, toX, toY, out wayPoints);
                if (ret == AstarManhattan.FindPathResult.Destination)
                {
                    path.Clear();
                    path.Add(new Vector2(toX, toY));
                }
                else if (ret == AstarManhattan.FindPathResult.Cross)
                {
                    //已经取过一次,不需要重复取.Editor by Alex.Yu
                    //wayPoints = layer.FindPath(fromX, fromY, toX, toY);
                    path.Clear();
                    path.Add(new Vector2(fromX, fromY));
                    do
                    {
                        path.Add(new Vector2(wayPoints.PosX, wayPoints.PosY));
                        wayPoints = wayPoints.Next;
                    }
                    while (wayPoints != null);
                    //path.Add(new Vector2(toX, toY));
                }
                else
                {
                    return null;
                }

                this.speed = speed;
                this.mEndDistance = endDis;
                this.userdata = ud;
                active = true;

                return path;
            }

            public void update(int deltatime)
            {
                if (!active) return;
                Vector2 pos = path[0];
                float length = speed * deltatime / 1000f;
                if (length > 0 && path.Count > 1)
                {
                    float l = Vector2.Distance(path[1], path[0]);
                    if (l <= length)
                    {
                        path.RemoveAt(0);
                    }
                    else
                    {
                        path[0] = Vector2.MoveTowards(path[0], path[1], length);//path[0] + (path[1] - path[0]).normalized * length;
                    }
                }

                //pos = path[0] - pos;
                //pos = path[0];
                updateAction(path[0].x, path[0].y, userdata);

                if (path.Count < 2)
                {
                    StopSeek(true, path[0].x, path[0].y, userdata);
                }
                else if (mEndDistance > 0)
                {
                    if (path.Count == 2)
                    {
                        if (Vector2.Distance(path[0], path[1]) <= mEndDistance)
                        {
                            StopSeek(true, path[0].x, path[0].y, userdata);
                        }
                    }
                }
            }

            private void StopSeek(bool complete, float x, float y, object ud)
            {
                if (active)
                {
                    path.Clear();
                    //updateAction = null;
                    active = false;
                    if (endAction != null)
                        endAction(complete, 0, 0, ud);
                    userdata = null;
                }
            }

            /// <summary>
            /// 外部打断寻路.
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <param name="ud"></param>
            public void StopSeek(float x = 0, float y = 0, object ud = null)
            {
                this.StopSeek(false, x, y, ud);
            }
        }*/
    }
}