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 CommonAIServer.Connector.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 = 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();
            }
        }
    }
}