﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Timers;


namespace AutobotManager
{
  public partial class Form1 : Form
  {
      #region Data
        public String[] m_sCommandArgs;
        public String[] m_sGamesList;

        private bool m_bInternalUpdate;
        private bool m_bBotFound;
        private bool m_bInitialized;
        public BotManager m_BotManager;
        private System.Xml.XmlTextWriter m_Xmltw;
        public ATcpClient m_TcpClient;

    #endregion // Data
    #region Form Events
        public Form1()
        {
            m_bInitialized = false;
            InitializeComponent();
            m_bBotFound = false;
            m_bInternalUpdate = true;

            m_BotManager = new BotManager(this);
            m_sGamesList = m_BotManager.GetGamesList();

            if (m_sGamesList.Count() != 0)
            {
                cb_game.Items.AddRange(m_sGamesList);
                cb_game.SelectedIndex = 0;
                UpdateGameSelection();
            }

            m_BotManager.BindingRequiredItemsUi(dg_required_style);
            m_TcpClient = new ATcpClient(this);

            CreateNewXmlWriter();

            launch_timer.Enabled = true;
            launch_timer.Interval = (int)(n_launchDelay.Value * 1000);

            cycle_timer.Enabled = true;
            cycle_timer.Interval = (int)(n_Cycle_Bots_Freq.Value * 1000);

            flush_timer.Enabled = true;

            m_bInternalUpdate = false;
            m_bInitialized = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bool bAutoStartCycle = false;
            bool bStartDeathTimer = false;
            //Update from the command Line Parameters
            if (null != m_sCommandArgs)
            {
                m_BotManager.UpdateFromCommandLine(m_sCommandArgs);
                for (int i = 0; i < m_sCommandArgs.Length; i++)
                {
                    if (0 == String.Compare(m_sCommandArgs[i], "--logfilter", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        tb_log_filter.Text = m_sCommandArgs[i + 1];
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--launchdelay", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        n_launchDelay.Value = Convert.ToDecimal(m_sCommandArgs[i + 1].ToUpper().Trim());
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--cycledelay", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        n_Cycle_Bots_Freq.Value = Convert.ToDecimal(m_sCommandArgs[i + 1].ToUpper().Trim());
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--cyclecount", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        int iCount = Convert.ToInt32(m_sCommandArgs[i + 1].ToUpper().Trim());
                        n_Cycle_Bots.Value = Convert.ToDecimal(iCount);
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--deathtimer", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        int iCount = Convert.ToInt32(m_sCommandArgs[i + 1].ToUpper().Trim());
                        n_Death_Time.Value = Convert.ToDecimal(iCount);
                        bStartDeathTimer = true;
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--startcycle", true))
                    {
                        bAutoStartCycle = true;
                    }
                    else if (0 == String.Compare(m_sCommandArgs[i], "--botargs", true) && ((i + 1) < m_sCommandArgs.Length))
                    {
                        tb_args.Text = m_sCommandArgs[i + 1].ToUpper().Trim();
                    }
                }
            }

            UpdateInternal();
            checkBox_deathtimer.Checked = bStartDeathTimer;
            checkBox_Cycle.Checked = bAutoStartCycle;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            checkBox_deathtimer.Checked = false;
            checkBox_Cycle.Checked = false;
            m_BotManager.KillAllBots();

            if(m_BotManager.GetBotCount() > 0)
            {
                CloseDialog oClose = new CloseDialog(this);
                if (oClose.ShowDialog() == DialogResult.Cancel)
                {
                    e.Cancel = true;
                    return;
                }
            }

            m_Xmltw.WriteEndElement();
            m_Xmltw.Close();
        }
    #endregion
    #region Helpers
    // Append text of the given color.
    void AppendText(RichTextBox box, Color color, string text)
    {
        int iTextLength = text.Length;
        if (0 == iTextLength)
        {
            return;
        }
        int start = box.TextLength;
        box.AppendText(text);
        int end = box.TextLength;
        box.Select(start, end - start);
        box.SelectionColor = color;

        int iExcess = end - 10000;
        if (iExcess > 0)
        {
            box.Select(0, iExcess + 3);
            box.SelectedText = "...";
        }

        box.Select(box.Text.Length - 1, 0);
        box.ScrollToCaret();
    }

    void SetText(TextBox textbox, Label label, bool bValid, string sNewText)
    {
      if (!bValid)
      {
        textbox.Text = sNewText;
        textbox.ForeColor = Color.Red;
        label.ForeColor = Color.Red;
      }
      else if (sNewText != textbox.Text)
      {
        textbox.Text = sNewText;
        textbox.ForeColor = Color.Blue;
        label.ForeColor = Color.Blue;
      }
      else
      {
        textbox.ForeColor = Color.Black;
        label.ForeColor = Color.Black;
      }

    }

    bool IsFiltered(string s_Data, string s_filter)
    {
        String[] saResult = s_filter.Trim().ToUpper().Split(',');
        if (null == saResult || 0 == saResult.Count())
        {
            return true;
        }

        s_Data = s_Data.ToUpper();
        foreach (var stext in saResult)
        {
            if (s_Data.Contains(stext.Trim()))
            {
                return true;
            }
        }

        return false;
    }

    public void MyLog(BotClass a_Bot, String a_sEntry, bool a_bError, bool a_bForceLog)
    {
        if(a_sEntry.Trim().Length > 0)
        {
            // Display on our Gui Console
            bool bAllow = false;
            if(    (checkBox_display_out.Checked && !a_bError)
                || (checkBox_ErrorStream.Checked && a_bError)
                || ((checkBox_ErrorStream.Checked || checkBox_display_out.Checked) && a_bForceLog)
                )

            {
                bAllow = IsFiltered(a_sEntry, tb_output_filter.Text) && IsSelectedBot(a_Bot.BotInctanceId);
                if(bAllow)
                {
                    String sDisplay = "\n[Bot_" + a_Bot.BotInctanceId.ToString() + "] " + a_sEntry;
                    Color oColor = (a_bError) ? Color.Red : Color.MidnightBlue;
                    AppendText(this.rt_Output, oColor, sDisplay);
                }
            }

            // Write to Log
            if (a_bForceLog || IsFiltered(a_sEntry, tb_log_filter.Text))
            {
                String sType = a_bError ? "Error" : "Log";
                double fTimeElapsed = DateTime.Now.Subtract(a_Bot.StartTime).TotalSeconds;
                fTimeElapsed = Math.Truncate(fTimeElapsed * 100) / 100;
                string sTimeElapsed = string.Format("{0:N2}s", fTimeElapsed);

                m_Xmltw.WriteStartElement("Bot_" + a_Bot.BotInctanceId.ToString());
                m_Xmltw.WriteAttributeString("id", a_Bot.BotId.ToString());
                m_Xmltw.WriteAttributeString("type", sType);
                m_Xmltw.WriteAttributeString("timestamp", DateTime.Now.ToString());
                m_Xmltw.WriteAttributeString("timeelapsed", sTimeElapsed);
                m_Xmltw.WriteString(a_sEntry);
                m_Xmltw.WriteEndElement();
            }
            
        }
    }
    #endregion


    #region Functions
        void UpdateInternal()
        {
            if (m_bInternalUpdate)
            {
                return;
            }

            m_bInternalUpdate = true;

            String sAbsApp = "";
            m_bBotFound = false;
            try
            {
                sAbsApp = System.IO.Path.GetFullPath(tb_application.Text);
                m_bBotFound = System.IO.File.Exists(sAbsApp);
                if (!m_bBotFound)
                {
                    String sAppPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\" + tb_application.Text;
                    sAbsApp = System.IO.Path.GetFullPath(sAppPath);
                    m_bBotFound = System.IO.File.Exists(sAbsApp);
                }
            }
            catch
            {
                m_bBotFound = false;
            }
            bt_Launch.Enabled = m_bBotFound;

            lbl_AppPath.Text = sAbsApp;
            lbl_AppPath.ForeColor = (m_bBotFound) ? Color.Blue : Color.Red;


            //Update BotStyleManager
            m_BotManager.UpdateArguments(sAbsApp, tb_args.Text, Convert.ToInt32(n_Prefix.Value));
            
            tb_finalArgs.Text = m_BotManager.GetPreviewArgs(cb_simulation.SelectedIndex);


            m_bInternalUpdate = false;
        }

        bool IsSelectedBot(int a_iD)
        {
            foreach (DataGridViewRow row in dg_botList.SelectedRows)
            {
                BotClass oBot = row.DataBoundItem as BotClass;
                if (oBot.BotInctanceId == a_iD)
                {
                    return true;
                }
            }
            return false;
        }

      void CreateNewXmlWriter()
      {
        String sDateTimeNow = DateTime.Now.ToString();
        String sLogFileName = "BotLog_" + sDateTimeNow;
        Regex oRegex = new Regex("[^0-9A-Za-z]");
        sLogFileName = oRegex.Replace(sLogFileName, "_");

        String sLogAppPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\" + sLogFileName + ".log.xml";
        this.Text = "Autobot Manager - " + sLogAppPath;

        m_Xmltw = new System.Xml.XmlTextWriter(sLogAppPath, Encoding.UTF8);
        m_Xmltw.Formatting = System.Xml.Formatting.Indented;
        m_Xmltw.Indentation = 2;
        m_Xmltw.WriteStartDocument();
        m_Xmltw.WriteStartElement("Bots");
        m_Xmltw.WriteAttributeString("timestamp", sDateTimeNow);
      }

    #endregion


    private void tb_application_TextChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void tb_args_TextChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void numeric_Count_ValueChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void cb_simulation_SelectedIndexChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void n_Prefix_ValueChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void bt_Simulate_Click(object sender, EventArgs e)
    {
        bool bStartImmediately = (m_BotManager.GetBotCount() == 0);

        for (int i = 0; i < numeric_Count.Value; ++i)
        {
            int iSim = cb_simulation.SelectedIndex;
            m_BotManager.CreateBotofType(iSim);
        }

        if (checkBox_Cycle.Checked == false && bStartImmediately)
        {
            StartNextBot();
            launch_timer.Stop();
            launch_timer.Start();
        }

    }

    private void StartNextBot()
    {
        BotClass oBot = m_BotManager.StartNextBot();
        if (null != oBot)
        {
            MyLog(oBot, "STARTING", false, true);
        }
    }

    private void btSelect_Click(object sender, EventArgs e)
    {
        dg_botList.SelectAll();
    }

    private void btUnSelect_Click(object sender, EventArgs e)
    {
        dg_botList.ClearSelection();
    }

    private void bt_killBots_Click(object sender, EventArgs e)
    {
        BindingList<BotClass> lSelectedItems = new BindingList<BotClass>();
        foreach (DataGridViewRow row in dg_botList.SelectedRows)
        {
            BotClass oBot = row.DataBoundItem as BotClass;
            lSelectedItems.Add(oBot);
        }
        m_BotManager.KillBots(lSelectedItems, false);
    }

    public void Kill_All_Bots()
    {
        m_BotManager.KillAllBots();
    }

    private void bt_killAll_Click(object sender, EventArgs e)
    {
        Kill_All_Bots();
    }


    private void n_launchDelay_ValueChanged(object sender, EventArgs e)
    {
        launch_timer.Interval = (int)(n_launchDelay.Value * 1000);
    }

    private void cycle_timer_Tick(object sender, EventArgs e)
    {
        if (checkBox_Cycle.Checked == true)
        {
            QueueNextCyclicBot();
        }
    }

    private void launch_timer_Tick(object sender, EventArgs e)
    {
        StartNextBot();
    }

    private void QueueNextCyclicBot()
    {
        //0. Early out 
        if(!m_bBotFound)
        {
            return;
        }
        //1. Kill a few bots
        int iTotalRequired = m_BotManager.GetTotalRequiredCount();
        int iTotalRunning = m_BotManager.GetStateTotalCount(BotState.BotState_Running);
        int iCycleLevel = Convert.ToInt32(n_Cycle_Bots.Value);
        int iMinRequiredBots = iTotalRequired - iCycleLevel;
        int iTotalKillable = (iTotalRunning > iMinRequiredBots) ? (iTotalRunning - iMinRequiredBots) : 0;
        int iTotalKilled = m_BotManager.KillRandomRunningBots(iTotalKillable);

        //2. Create Random bots from the requirements
        int iCreateThisFrame = iTotalRequired - (m_BotManager.GetBotCount() - iTotalKilled);
        while (--iCreateThisFrame >= 0)
        {
            int iSim = m_BotManager.GetNextRandomSimToLaunch();
            m_BotManager.CreateBotofType(iSim);
        }
    }


    public void SetAutoCycle(bool a_bSet)
    {
        checkBox_Cycle.Checked = a_bSet;
    }
    private void checkBox_Cycle_CheckedChanged(object sender, EventArgs e)
    {
        UpdateInternal();
        if (checkBox_Cycle.Checked)
        {
            gb_auto_cycle.Text = "[ Auto Cycle - RUNNING ]";
            checkBox_Cycle.ForeColor = Color.Red;
            if (0 == m_BotManager.GetBotCount())
            {
                QueueNextCyclicBot();
            }
        }
        else
        {
            gb_auto_cycle.Text = "[ Auto Cycle - Paused ]";
            checkBox_Cycle.ForeColor = Color.Blue;
        }
    }

    private void autolaunch_dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        UpdateInternal();
    }

    private void n_Cycle_Bots_Count_ValueChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void n_Cycle_Bots_ValueChanged(object sender, EventArgs e)
    {
        UpdateInternal();
    }

    private void n_Cycle_Bots_Freq_ValueChanged(object sender, EventArgs e)
    {
        cycle_timer.Interval = (int)(n_Cycle_Bots_Freq.Value * 1000);
    }

    private void flush_timer_Tick(object sender, EventArgs e)
    {
        m_Xmltw.Flush();
    }

    private void btn_NewLog_Click(object sender, EventArgs e)
    {
        m_Xmltw.WriteEndElement();
        m_Xmltw.Flush();
        m_Xmltw.Close();
        CreateNewXmlWriter();
    }

    private void dg_required_style_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {
        e.Control.KeyPress -= new KeyPressEventHandler(NumericColumn_KeyPress);
        if (m_BotManager.IsNumericColumn(dg_required_style)) //Desired Column
        {
            TextBox tb = e.Control as TextBox;
            if (tb != null)
            {
                tb.KeyPress += new KeyPressEventHandler(NumericColumn_KeyPress);
            }
        }
    }

    private void NumericColumn_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
        {
            e.Handled = true;
        }
    }

    private void checkBox_display_out_CheckedChanged(object sender, EventArgs e)
    {

    }

    private void death_timer_Tick(object sender, EventArgs e)
    {
        if(checkBox_deathtimer.Checked)
        {
            decimal fVal = Convert.ToDecimal(death_timer.Interval) / 1000;
            n_Death_Time.Value = n_Death_Time.Value - fVal;
            if(n_Death_Time.Value <= 0)
            {
                checkBox_deathtimer.Checked = false;
                death_timer.Enabled = false;
                death_timer.Stop();
                checkBox_deathtimer.ForeColor = Color.Black;
                n_Death_Time.ForeColor = Color.Black;
                this.Close();
            }
        }
    }

    private void checkBox_deathtimer_CheckedChanged(object sender, EventArgs e)
    {
        if(checkBox_deathtimer.Checked)
        {
            death_timer.Enabled = true;
            death_timer.Start();
            checkBox_deathtimer.ForeColor = Color.Red;
            n_Death_Time.ForeColor = Color.Red;
        }
        else
        {
            death_timer.Enabled = false;
            death_timer.Stop();
            checkBox_deathtimer.ForeColor = Color.Black;
            n_Death_Time.ForeColor = Color.Black;
        }
    }

    private void ui_refresh_timer_Tick(object sender, EventArgs e)
    {
        if (m_bInitialized)
        {
            m_BotManager.RefreshStatisticsUiBindings(dg_stats, dg_luabotstatus, dg_global_luaStatus, dg_metrics, dg_botList);
        }
    }

    public void ReBindUi(bool a_bSet)
    {
    }

    private void checkBox_BindUi_CheckedChanged(object sender, EventArgs e)
    {
        btSelect.Enabled = checkBox_BindUi.Checked;
        btUnSelect.Enabled = checkBox_BindUi.Checked;
        bt_killBots.Enabled = checkBox_BindUi.Checked;
        if(checkBox_BindUi.Checked)
        {
            m_BotManager.BindingBotList(dg_botList);
        }
        else
        {
            dg_botList.DataSource = null;
        }

    }

    private void tcp_refresh_timer_Tick(object sender, EventArgs e)
    {
        if (m_bInitialized)
        {
            m_TcpClient.UpdateServer();
        }
    }


    public void Log_server_msg(String a_sMsg)
    {
        AppendText(rt_server_log, Color.Violet, DateTime.Now.ToString() + " : ");
        AppendText(rt_server_log, Color.Blue, a_sMsg);
        AppendText(rt_server_log, Color.Blue, "\n");
    }

    public void IncomingChatMsg(String a_sMsg)
    {
        AppendText(rt_server_chat, Color.Blue, a_sMsg + "\n");
    }

    private void btn_server_chat_Click(object sender, EventArgs e)
    {
        String sDateTime = DateTime.Now.ToString() + " : ";
        AppendText(rt_server_chat, Color.Brown, sDateTime);
        AppendText(rt_server_chat, Color.Black, rt_server_input.Text + "\n");
        m_TcpClient.SendMessage("chat " + sDateTime + rt_server_input.Text);
        rt_server_input.Text = "";
    }

    public void SetDeathTimer(int a_iSecs)
    {
        n_Death_Time.Value = a_iSecs;
    }

    public void SetBotSpawnTimer(int a_iSecs)
    {
        n_launchDelay.Value = a_iSecs;
    }
      
    public void ActivateDeathTimer(bool a_bSet)
    {
        checkBox_deathtimer.Checked = a_bSet;
    }

    public void RestartSimulation()
    {
        bool a_bPrevBinded = checkBox_BindUi.Checked;

        dg_stats.DataSource = null;
        dg_luabotstatus.DataSource = null;
        dg_global_luaStatus.DataSource = null;
        dg_metrics.DataSource = null;
        dg_botList.DataSource = null;
        dg_required_style.DataSource = null;
        dg_botList.DataSource = null;

        m_BotManager.Reset();

        m_BotManager.RefreshStatisticsUiBindings(dg_stats, dg_luabotstatus, dg_global_luaStatus, dg_metrics, dg_botList);
        m_BotManager.BindingRequiredItemsUi(dg_required_style);

        if (a_bPrevBinded)
        {
            m_BotManager.BindingBotList(dg_botList);
        }
    }

    private void btn_restart_Click(object sender, EventArgs e)
    {
        RestartSimulation();
    }

    private void cb_game_SelectedIndexChanged(object sender, EventArgs e)
    {
        UpdateGameSelection();
    }

    private void UpdateGameSelection()
    {
        m_BotManager.m_GameName = m_sGamesList[cb_game.SelectedIndex];
        m_BotManager.UpdateGameStyles();

        BotManager.GameSettingsDefinition settings = m_BotManager.GetGamesSettings();
        if (settings != null)
        {
            tb_application.Text = settings.m_BotPath;
            tb_args.Text = settings.m_BotArgs;
            cb_simulation.Items.Clear();
            cb_simulation.Items.AddRange(settings.m_BotStyles);
            cb_simulation.SelectedIndex = 0;
        }
    }

  }

}


