using CommonAI.Zone;
using CommonAI.Zone.Instance;
using CommonAI.Zone.ZoneEditor;
using CommonAIClient.Client;
using CommonAIServer.Connector;
using CommonAIServer.Node;
using CommonFroms;
using CommonFroms.G2D;
using CommonFroms.Utils;
using CommonLang;
using CommonLang.Vector;
using GameEditorPlugin.Win32.BattleClient;
using GameEditorPluginServer.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace GameEditorPluginServer.Server
{
    public partial class FormServer : Form
    {
        private ConsoleOutput console;
        private ServerNode server;
        private ClientLoader client_loader;
        private BattleView battle_view;
        private DirectoryInfo dataDir;

        public FormServer(DirectoryInfo dataDir, int sceneID, ZoneNodeConfig cfg = null)
        {
            InitializeComponent();

            this.console = new ConsoleOutput();

            this.pictureBoxRoomLayer.MouseWheel += new MouseEventHandler(pictureBoxRoomLayer_MouseWheel);
            this.Shown += FormServer_Shown;
            this.FormClosing += FormServer_FormClosing;
            this.FormClosed += FormServer_FormClosed;
            this.Disposed += FormServer_Disposed;
            this.dataDir = dataDir;
            var templates = new EditorTemplates(dataDir.FullName, TemplateManager.MessageCodec);
            templates.LoadAllTemplates();
            FormLauncher.Templates = templates;
            if (cfg == null)
            {
                cfg = new ZoneNodeConfig();
                cfg.GAME_UPDATE_INTERVAL_MS = 1000 / templates.Templates.CFG.SYSTEM_FPS;
                cfg.CLIENT_SYNC_OBJECT_IN_RANGE = (int)templates.Templates.CFG.CLIENT_UNIT_MOVE_MODIFY_MAX_RANGE;
                cfg.CLIENT_SYNC_OBJECT_OUT_RANGE = (int)(cfg.CLIENT_SYNC_OBJECT_OUT_RANGE * 1.25f);
            }
            this.server = new ServerNode(templates, cfg, sceneID);
            this.client_loader = new ClientLoader(server);
            this.server.Node.EnableAOI = chk_EnableAOI.Checked;
            this.server.Node.OnZoneStart += Node_OnZoneStart;
            this.server.Node.OnZoneStop += Node_OnZoneStop;
            this.server.Start();
        }

        private void FormServer_Disposed(object sender, EventArgs e)
        {
            server.Dispose();
        }

        private void FormServer_Shown(object sender, EventArgs e)
        {
            console.DockTo = this;
            console.Show();
        }

        private void FormServer_FormClosed(object sender, FormClosedEventArgs e)
        {
            console.Close();
            client_loader.DisposeClients();
        }

        private void FormServer_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        private void Node_OnZoneStart(BaseZoneNode zone)
        {
            this.timer2.QueueTask(() =>
            {
                this.battle_view = new BattleView(server);
                this.battle_view.Layer.LayerInit += Layer_LayerInit;
            });
        }

        private void Node_OnZoneStop(BaseZoneNode zone)
        {
            this.timer2.QueueTask(() =>
            {
                this.Close();
            });
        }


        private void Layer_LayerInit(CommonAI.ZoneClient.ZoneLayer layer)
        {
            var bv = battle_view;
            if (bv != null)
            {
                float scale = Math.Max(
                    ((float)pictureBoxRoomLayer.Width) / bv.Width,
                    ((float)pictureBoxRoomLayer.Height) / bv.Height);
                bv.setCamera(bv.Width / 2, bv.Height / 2);
                bv.setCameraScale(scale, scale);
            }
            this.client_loader.AddRandomClient();
        }
        private void timer2_Tick(object sender, EventArgs e)
        {
            if (server.Running)
            {
                if (server != null)
                {
                    this.Text = "FormServer : " + server.Server.ClientConnectString;
                }
                if (battle_view != null)
                {
                    battle_view.IsSyncPos = chk_ServerVisible.Checked;
                }
                if (battle_view != null)
                {
                    battle_view.update();
                }
                if (chk_ServerVisible.Checked)
                {
                    pictureBoxRoomLayer.Refresh();
                }
            }
        }

        //----------------------------------------------------------------------------------------------------------------------
        #region __BattleView__

        internal class BattleLocalView : BattleLocal
        {
            private EditorScene mZone;
            public BattleLocalView(ServerNode server)
                : base(server.Node.DataRoot)
            {
                this.mZone = server.Node.Zone;
                this.Layer.ProcessMessage(new CommonAI.ZoneClient.ClientEnterScene(mZone.Data.ID, mZone.SpaceDivSize, server.Node.Zone.Templates.ResourceVersion));
            }
            public override EditorScene Zone
            {
                get { return mZone; }
            }
        }
        internal class BattleView : DisplayBattleLocal<BattleLocalView>
        {
            public ZoneNode Node { get { return server.Node; } }
            public bool IsSyncPos { get; set; }
            private ServerNode server;
            private long mLastUpdateTime;
            public BattleView(ServerNode server)
            {
                base.ShowGuardRange = false;
                base.ShowAttackRange = false;
                base.ShowDamageRange = false;
                base.ShowName = true;
                base.ShowHP = true;
                base.ShowSpaceDiv = false;
                this.server = server;
                base.InitClient(new BattleLocalView(server));
                this.server.Node.Zone.OnPostEvent += zone_OnPostEvent;
                this.server.Node.Zone.OnUpdate += zone_OnUpdate;
            }
            private void zone_OnPostEvent(InstanceZone zone, Event e)
            {
                Battle.onEventHandler(e);
            }
            private void zone_OnUpdate(CommonAI.Zone.Instance.InstanceZone zone)
            {
                if (IsSyncPos)
                {
                    SyncPosEvent.UnitPos pos = new SyncPosEvent.UnitPos();
                    SyncPosEvent.UnitState st = new SyncPosEvent.UnitState();
                    foreach (CommonAI.Zone.Instance.InstanceZoneObject so in server.Node.Zone.AllObjects)
                    {
                        CommonAI.ZoneClient.ZoneObject co = Client.Layer.GetObject(so.ID);
                        if (co != null)
                        {
                            pos = so.GetSyncPos();
                            co.SyncPos(ref pos);
                            if (so is InstanceUnit)
                            {
                                st = (so as InstanceUnit).GetSyncState();
                                co.SyncPos(ref st);
                            }
                        }
                    }
                }
            }
            protected override void clientUpdate(int intervalMS)
            {
                Battle.BeginUpdate(intervalMS);
                Battle.Update();
            }
            public void update()
            {
                long curTime = CommonLang.CUtils.CurrentTimeMS;
                if (mLastUpdateTime == 0)
                {
                    mLastUpdateTime = curTime;
                }
                int intervalMS = (int)(curTime - mLastUpdateTime);
                this.mLastUpdateTime = curTime;
                base.update(intervalMS);
                base.update_flush();
            }

        }

        //----------------------------------------------------------------------------------------------------------------------

        private Vector2 pic_lastMouesDown;
        private Vector2 pic_lastCameraPos;

        private void pictureBoxRoomLayer_MouseWheel(object sender, MouseEventArgs e)
        {
            try
            {
                BattleView bv = battle_view;
                if (bv != null)
                {
                    int d = CMath.getDirect(e.Delta);
                    if (d > 0)
                    {
                        float newD = bv.getCameraScale() * 1.1f;
                        bv.setCameraScale(newD, newD);
                    }
                    else if (d < 0)
                    {
                        float newD = bv.getCameraScale() / 1.1f;
                        bv.setCameraScale(newD, newD);
                    }
                }
            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
            }
        }
        private void pictureBoxRoomLayer_MouseDown(object sender, MouseEventArgs e)
        {
            pictureBoxRoomLayer.Focus();
            BattleView bv = battle_view;
            if (bv != null)
            {
                if (e.Button == MouseButtons.Left)
                {
                    bv.PickObject(
                        bv.screenToWorldX(e.X),
                        bv.screenToWorldY(e.Y));
                }
                else if (e.Button == MouseButtons.Right)
                {
                    pic_lastMouesDown = new Vector2(e.Location.X, e.Location.Y);
                    pic_lastCameraPos = new Vector2(bv.CameraX, bv.CameraY);
                }
            }
        }
        private void pictureBoxRoomLayer_MouseUp(object sender, MouseEventArgs e)
        {
            pic_lastMouesDown = null;
            pic_lastCameraPos = null;
        }

        private void pictureBoxRoomLayer_MouseMove(object sender, MouseEventArgs e)
        {
            BattleView bv = battle_view;
            if (bv != null)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Right)
                {
                    if (pic_lastMouesDown != null)
                    {
                        float x = pic_lastCameraPos.X + bv.screenToWorldSizeX(pic_lastMouesDown.X - e.X);
                        float y = pic_lastCameraPos.Y + bv.screenToWorldSizeY(pic_lastMouesDown.Y - e.Y);
                        bv.setCamera(x, y);
                    }
                }
            }
        }
        private void pictureBoxRoomLayer_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                if (chk_ServerVisible.Checked)
                {
                    BattleView bv = battle_view;
                    if (bv != null)
                    {
                        bv.setWindow(new RectangleF(0, 0, pictureBoxRoomLayer.Width, pictureBoxRoomLayer.Height));
                        bv.render(e.Graphics);
                    }
                }
            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
            }
        }

        #endregion
        //----------------------------------------------------------------------------------------------------------------------

        #region __测试客户端__

        private class ClientLoader : TestClientLoader
        {
            private static int id_gen = 0;
            private readonly ServerNode server;
            private Random random = new Random();
            private List<FormClient> client_list = new List<FormClient>();

            public ClientLoader(ServerNode server)
            {
                this.server = server;
            }

            public override CommonAI.ZoneServer.CreateUnitInfoR2B GenUnitInfoR2B(int unitID)
            {
                CommonAI.ZoneServer.CreateUnitInfoR2B ret = new CommonAI.ZoneServer.CreateUnitInfoR2B();
                ret.UnitTemplateID = unitID;
                return ret;
            }
            public override Type ZoneFactoryType
            {
                get { return TemplateManager.Factory.GetType(); }
            }

            public void DisposeClients()
            {
                foreach (var f in client_list)
                {
                    f.Dispose();
                }
            }

            public void AddRandomClient()
            {
                UnitInfo info;
                PlayerStartAbilityData start;
                var node = server.Node;
                if (node.Zone.TryGetTestTemplate(out info, out start, random))
                {
                    var enter = GenUnitInfoR2B(info.ID);
                    enter.Force = start.START_Force;
                    var client = new FormTestClient();
                    client.Init(
                       server.Config,
                       info.Name + "_" + id_gen++, // player_uuid
                       node.SceneID.ToString(),
                       server.Server.ClientConnectString,
                       typeof(CommonNetwork.Sockets.NetSession).FullName,
                       enter);
                    client_list.Add(client);
                    client.Show();
                }
            }
            public void AddTestClient()
            {
                UnitInfo info;
                PlayerStartAbilityData start;
                var node = server.Node;
                if (node.Zone.TryGetTestTemplate(out info, out start, random))
                {
                    var enter = GenUnitInfoR2B(info.ID);
                    enter.Force = start.START_Force;

                    FormTestClient.StartLauncher(
                        server.Config,
                       info.Name + "_" + id_gen++, // player_uuid
                       server.Server.ClientConnectString,
                       node.SceneID,
                       enter, this, (launcher) =>
                       {
                           enter.UnitTemplateID = launcher.UnitTemplateID;
                           enter.Force = launcher.Force;
                           var client = new FormTestClient();
                           client.Init(
                                server.Config,
                                launcher.PlayerUUID, // player_uuid
                                node.SceneID.ToString(),
                                server.Server.ClientConnectString,
                                launcher.NetDirver,
                                enter);
                           client_list.Add(client);
                           client.Show();
                           launcher.Close();
                           return true;
                       }).Show();
                }
            }
        }

        #endregion

        private void btn_AddPlayer_Click(object sender, EventArgs e)
        {
            client_loader.AddTestClient();
        }

        private void btn_EmulateDelay_Click(object sender, EventArgs e)
        {
            int min, max;
            server.Server.GetEmulateLaggingMS(out min, out max);
            string text = G2DTextDialog.Show(string.Format("{0} - {1}", min, max), "模拟网络延时");
            try
            {
                var kv = text.Split(new char[] { '-' }, 2, StringSplitOptions.RemoveEmptyEntries);
                if (kv.Length == 2)
                {
                    min = int.Parse(kv[0]);
                    max = int.Parse(kv[1]);
                    server.Server.SetEmulateLaggingMS(min, max);
                }
                else
                {
                    min = int.Parse(text);
                    server.Server.SetEmulateLaggingMS(min, min);
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btn_Bots_Click(object sender, EventArgs e)
        {
            try
            {
                ProcessStartInfo start = new ProcessStartInfo(
                    Application.StartupPath + @"\CommonAIServer.Connector.exe",
                    string.Format("FactoryClass={0} DataRoot=\"{1}\" ConnectString={2}",
                    TemplateManager.Factory.GetType().FullName,
                    dataDir.FullName,
                    server.Server.ClientConnectString
                    ));
                start.WorkingDirectory = Application.StartupPath;
                Process.Start(start);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btn_ShowMsgBytes_Click(object sender, EventArgs e)
        {
            var msg_bytes_rec = new FormMsgBytes(server.Codec);
            msg_bytes_rec.Show();
            msg_bytes_rec.BringToFront();
        }

        private void btn_GC_Click(object sender, EventArgs e)
        {
            System.GC.Collect();
        }

        private void btn_disconnectAll_Click(object sender, EventArgs e)
        {
            foreach (var session in server.Server.GetSessions())
            {
                session.Disconnect(true);
            }
        }

        private void chk_EnableAOI_Click(object sender, EventArgs e)
        {
            server.Node.EnableAOI = chk_EnableAOI.Checked;
        }

    }
}