using CommonFroms.G2D;
using CommonLang;
using CommonLang.Property;
using CommonLang.Xml;
using Pomelo.DotNetClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using XmdsBattleClientBot;
using XmdsBattleClientBot.Bot;
using XmdsBattleClientWin32.Battle;
using XmdsBotTest.Runner;

namespace XmdsBotTest
{
    public partial class FormBotTest : Form
    {
        private static bool first_start = true;
        private int lastIndex = 0;
        private long last_update_time;
        private int totalTestCount = 0;
        private BotConfig config;

        public FormBotTest(BotConfig cfg)
        {
            this.config = cfg;
            InitializeComponent();
            InitBotModules();
        }

        public BotListViewItem SelectedBotItem
        {
            get
            {
                if (list_Bots.SelectedItems.Count > 0)
                {
                    return list_Bots.SelectedItems[0] as BotListViewItem;
                }
                return null;
            }
        }

        private void FormBotTest_Load(object sender, EventArgs e)
        {
        }
        private void FormBotTest_Shown(object sender, EventArgs e)
        {
            if (LaunchArgs.IsAuto && first_start)
            {
                first_start = false;
                StartBots(LaunchArgs.DefaultBotPrefix, LaunchArgs.DefaultBotCount);
            }
        }
        private void FormBotTest_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (BotListViewItem item in list_Bots.Items)
            {
                try
                {
                    item.Dispose();
                }
                catch (Exception err)
                {
                    Console.WriteLine(err.Message);
                }
            }
        }

        private void btn_GC_Click(object sender, EventArgs e)
        {
            System.GC.Collect();
        }
        private void timer_Update_Tick(object sender, EventArgs e)
        {
            if (base.Visible)
            {
                long curTime = CommonLang.CUtils.CurrentTimeMS;
                if (last_update_time == 0)
                {
                    last_update_time = curTime;
                }
                int intervalMS = (int)(curTime - last_update_time);
                last_update_time = curTime;
                intervalMS = Math.Min(intervalMS, timer_Update.Interval * 2);
                foreach (BotListViewItem item in list_Bots.Items)
                {
                    item.Update(intervalMS);
                }
            }
            var selected = SelectedBotItem;
            if (text_Events.Tag != selected)
            {
                text_Events.Tag = selected;
                text_Events.Clear();
            }
            if (selected != null)
            {
                if (text_Events.TextLength < selected.Events.Length)
                {
                    char[] copyto = new char[selected.Events.Length - text_Events.TextLength];
                    selected.Events.CopyTo(text_Events.TextLength, copyto, 0, copyto.Length);
                    text_Events.AppendText(new string(copyto));
                }
                if (text_Events.TextLength > selected.Events.Length)
                {
                    text_Events.Clear();
                }
            }
        }

        private void timer_RefreshList_Tick(object sender, EventArgs e)
        {
            foreach (BotListViewItem item in list_Bots.Items)
            {
                item.Refresh();
            }
            lbl_NetStatus.Text = PomeloStatus.Status;
        }

        private void btn_AddBots_Click(object sender, EventArgs e)
        {
            StartBots();
        }
        private void btn_StopAll_Click(object sender, EventArgs e)
        {
            foreach (BotListViewItem item in list_Bots.Items)
            {
                try
                {
                    item.Stop();
                }
                catch (Exception err)
                {
                    Console.WriteLine(err.Message);
                }
            }
        }
        private void btn_ClearConsole_Click(object sender, EventArgs e)
        {
            text_Events.Clear();
            var selected = SelectedBotItem;
            if (selected != null)
            {
                selected.Events.Clear();
            }
        }
        private void btn_ClearAllConsole_Click(object sender, EventArgs e)
        {
            text_Events.Clear();
            foreach (BotListViewItem item in list_Bots.Items)
            {
                item.Events.Clear();
            }
        }
        private void list_Bots_SelectedIndexChanged(object sender, EventArgs e)
        {
            splitContainer2.Panel1.Controls.Clear();
            var selected = SelectedBotItem;
            if (selected != null && selected.BattleView != null)
            {
                splitContainer2.Panel1.Controls.Add(selected.BattleView);
            }
        }

        private void moduleItem_CheckedChanged(object sender, EventArgs e)
        {
            var item = sender as ToolStripMenuItem;
            var mt = item.Tag as Type;
            BotRunner.RunnerModule.SetModuleEnable(mt, item.Checked);
            SaveBotModules();
        }

        private void childItem_CheckedChanged(object sender ,EventArgs e) {
            var item = sender as ToolStripMenuItem;
            var mt = item.Tag as Type;
            string fullName = mt.Namespace + "." + mt.Name + "+Config";
            Type type = Type.GetType(fullName);
            ConfigBase obj = ReflectionUtil.CreateInstance(type) as ConfigBase;
            obj.popG2DPropertyDialog();
            AddBotConfig.SaveProp(mt.Name, type);
        }

        private void menu_BotItem_Opening(object sender, CancelEventArgs e)
        {
            var menu = sender as ContextMenuStrip;
            if (menu != null)
            {
                var mp = menu.PointToScreen(new Point(0, 0));
                var lp = list_Bots.PointToClient(mp);
                var item = list_Bots.GetItemAt(lp.X, lp.Y);
                if (item != null)
                {
                    item.Selected = true;
                    var selected = SelectedBotItem;
                    if (selected == null)
                    {
                        e.Cancel = true;
                    }
                }
                else
                {
                    e.Cancel = true;
                }
            }

        }
        private void btn_BotReconnect_Click(object sender, EventArgs e)
        {
            var selected = SelectedBotItem;
            if (selected != null)
            {
                selected.Runner.reconnect();
            }
        }
        private void btn_BotStop_Click(object sender, EventArgs e)
        {
            var selected = SelectedBotItem;
            if (selected != null)
            {
                selected.Runner.Stop();
                totalTestCount = 0;
            }
        }
        
        //-------------------------------------------------------------------------------------------------------------
        #region OP

        private void StartBots(string name_prefix = null, int count = 1)
        {
            var add = AddBotConfig.TryLoadAddConfig();
            if (name_prefix != null)
            {
                add.name_format = name_prefix;
                add.count = count;
            }
            else
            {
                add.index = lastIndex;
                add = G2DPropertyDialog<AddBotConfig>.Show("add robot", add);
            }
            if (add != null)
            {
                totalTestCount += add.count;
                this.Text = "BotTest : " + add.name_format + " (" + totalTestCount + ")";
                try
                {
                    if (add.name_format.Contains("{0}") && !string.IsNullOrEmpty(add.digit_format))
                    {
                        for (int i = 0; i < add.count; i++)
                        {
                            var id = add.index + i;
                            var name = string.Format(add.name_format, id.ToString(add.digit_format));
                            var item = new BotListViewItem(name, id, add, config);
                            list_Bots.Items.Add(item);
                            //item.Start();
                        }
                    }
                    else
                    {
                        var name = add.name_format;
                        var item = new BotListViewItem(name, lastIndex, add, config);
                        list_Bots.Items.Add(item);
                        item.Start();
                    }
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message);
                }
                lastIndex = add.index + add.count;
                AddBotConfig.TrySaveAddConfig(add);
            }
        }

        private void InitBotModules()
        {
            try
            {
                var saved = XmlUtil.LoadXML(Application.StartupPath + "/bot_modules.xml");
                if (saved != null)
                {
                    var cfg = XmlUtil.XmlToObject<BotModuleConfig>(saved);
                    foreach (var me in cfg.Modules)
                    {
                        var mt = ReflectionUtil.GetType(me.Key);
                        if (mt != null)
                        {
                            BotRunner.RunnerModule.SetModuleEnable(mt, me.Value);
                        }
                    }
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }

            foreach (var mt in BotRunner.RunnerModule.GetSubTypes())
            {
                var enable = BotRunner.RunnerModule.GetModuleEnable(mt);
                var item = new ToolStripMenuItem();
                item.CheckOnClick = true;
                item.Size = new System.Drawing.Size(152, 22);
                BotRunner.RunnerModule _module = ReflectionUtil.CreateInstance(mt) as BotRunner.RunnerModule;
                item.Text =  _module.module_name + "(" + mt.Name+ ")";
                item.Tag = mt;
                item.Checked = enable;
                item.CheckedChanged += moduleItem_CheckedChanged;
                if (mt.Name.EndsWith("Func"))
                {
                    var childItem = new ToolStripMenuItem();
                    childItem.Text = "修改配置";
                    childItem.Tag = mt;
                    childItem.CheckOnClick = true;
                    childItem.CheckedChanged += childItem_CheckedChanged;
                    item.DropDownItems.Add(childItem);
                    func_module.DropDownItems.Add(item);
                }
                else if (mt.Name.EndsWith("Warn"))
                {
                    var childItem = new ToolStripMenuItem();
                    childItem.Text = "修改配置";
                    childItem.Tag = mt;
                    childItem.CheckOnClick = true;
                    childItem.CheckedChanged += childItem_CheckedChanged;
                    item.DropDownItems.Add(childItem);
                    warn_module.DropDownItems.Add(item);
                }
                else if (mt.Name.EndsWith("Proto")) {
                    var childItem = new ToolStripMenuItem();
                    childItem.Text = "修改配置";
                    childItem.Tag = mt;
                    childItem.CheckOnClick = true;
                    childItem.CheckedChanged += childItem_CheckedChanged;
                    item.DropDownItems.Add(childItem);
                    proto_module.DropDownItems.Add(item);
                }
                else
                {
                    var childItem = new ToolStripMenuItem();
                    childItem.Text = "修改配置";
                    childItem.Tag = mt;
                    childItem.CheckOnClick = true;
                    childItem.CheckedChanged += childItem_CheckedChanged;
                    item.DropDownItems.Add(childItem);
                    group_Module.DropDownItems.Add(item);
                }

                
            }
        }
        private void SaveBotModules()
        {
            BotModuleConfig cfg = new BotModuleConfig();
            foreach (var mt in BotRunner.RunnerModule.GetSubTypes())
            {
                cfg.Modules.Add(mt.FullName, BotRunner.RunnerModule.GetModuleEnable(mt));
            }
            var save = XmlUtil.ObjectToXml(cfg);
            XmlUtil.SaveXML(Application.StartupPath + "/bot_modules.xml", save);
        }
        #endregion

        private void connect_Click(object sender, EventArgs e)
        {
            var selected = SelectedBotItem;
            if (selected != null)
            {
                selected.Runner.connect();
            }
        }

        private void tsi_leave_Click(object sender, EventArgs e)
        {
            var selected = SelectedBotItem;
            if (selected != null)
            {
                selected.Runner.leaveScene();
            }
        }
    }

    public class BotListViewItem : IBotListViewItem
	{
        public BotClient Client { get; private set; }
        public BotRunner Runner { get; private set; }
        public StringBuilder Events { get; private set; }
        public BattlePanelContainer BattleView { get; private set; }


        public BotListViewItem(string name, int index, AddBotConfig add, BotConfig cfg)
            : base(name)
        {
            var client = new BotClient(name, add.password, cfg, this);
            var bot = new BotRunner(client, index, add);
            this.Client = client;
            this.Runner = bot;
            this.Events = new StringBuilder();
            if (!cfg.NoBattleView)
            {
                this.BattleView = new BattlePanelContainer(client);
                this.BattleView.Dock = DockStyle.Fill;
            }
            this.Tag = bot;
            this.SubItems.Add(bot.NetState.ToString());
            this.SubItems.Add(bot.RoleName);
            this.SubItems.Add(bot.SceneName);
            this.SubItems.Add(bot.Status);
        }
	
        public override void Start()
        {
            Runner.Start();
        }
        public override void Stop()
        {
            Runner.Stop();
        }
        public override void Dispose()
        {
            Runner.Dispose();
        }
        public void Update(int intervalMS)
        {
            Client.Update(intervalMS);
            if (BattleView != null)
            {
                BattleView.UpdateBattle(intervalMS);
            }
        }

        private bool reconnect;
        private long disconnectTime;
        public void Refresh()
        {
            if (Events.Length > 65535)
            {
                Events.Clear();
            }
            using (var list = ListObjectPool<string>.AllocAutoRelease())
            {
                this.Runner.PopLogs(list);
                foreach (var e in list)
                {
                    Events.AppendLine(e);
                }
            }
            string st = Runner.NetState.ToString() + ":" + Runner.Bot.Client.EntryServerElapsedMS;
            if (this.SubItems[1].Text != st)
                this.SubItems[1].Text = st;
            if (this.SubItems[2].Text != Runner.RoleName)
                this.SubItems[2].Text = Runner.RoleName;
            if (this.SubItems[3].Text != Runner.SceneName)
                this.SubItems[3].Text = Runner.SceneName;
            if (this.SubItems[4].Text != Runner.Status)
                this.SubItems[4].Text = Runner.Status;
            if (Runner.Status != "" && Runner.SceneName == "")
            {
                if (CUtils.CurrentTimeMS > disconnectTime + 30000)
                {
                    if (reconnect)
                    {
                        //Console.WriteLine("AUTO HOOK RECONNECT...");
                        Runner.reconnect();
                        reconnect = false;
                    }
                    reconnect = !reconnect;
                    disconnectTime = CUtils.CurrentTimeMS;
                }
            }
            else
            {
                reconnect = false;
            }
        }


    }
}