using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Xml;
using System.Xml.Linq;
using DDUtilityApp.TIBRENDEZVOUS.DATA;
using JWH;
using JWH.DATA;
using JWH.TIB;
using Telerik.WinControls.UI;

namespace DDUtilityApp.TIBRENDEZVOUS
{

    public partial class FrmSimulator01 : Form
    {

        #region [ Event ] =====================================================
        #endregion

        #region [ Variable ] ==================================================

        private Color ButtonActiveColor = Color.LightPink;

        private List<Server> Servers { get; set; } = new List<Server>();

        private TibRendezvous TibRendezvous { get; set; } = null;

        private MessageParser Parser { get; set; } = null;

        private XLogger Logger { get; set; } = null;

        private string PathTibRendezvous { get; set; } = string.Empty;

        private string PathTibRendezvousMessage { get; set; } = string.Empty;

        private string PathTibRendezvousReply { get; set; } = string.Empty;

        private string PathTibRendezvousInfo { get; set; } = string.Empty;

        private string PathTibRendezvousValue { get; set; } = string.Empty;

        private Control BackColorChangeControl { get; set; } = null;

        private bool SendMultiMessage_Flag { get; set; } = true;

        private Color ColorOfNormal { get; set; } = ColorTranslator.FromHtml("#808080");

        private Color ColorOfSend { get; set; } = ColorTranslator.FromHtml("#000000");

        private Color ColorOfRecived { get; set; } = ColorTranslator.FromHtml("#000080");

        private Color ColorOfWarn { get; set; } = ColorTranslator.FromHtml("#FF0000");

        private Color ColorOfError { get; set; } = ColorTranslator.FromHtml("#800000");

        /// <summary>
        /// XLogger.Control에 로그를 출력하지 않을때 사용하는 색
        /// </summary>
        private Color ColorOfTransparent { get; set; } = Color.Transparent;

        #endregion

        #region [ Constructor ] ===============================================

        public FrmSimulator01()
        {
            InitializeComponent();
            this.tboxRcvMessage.ForeColor = this.ColorOfNormal;
            this.Logger = new XLogger($"TibSimulator");
            this.Logger.Control = this.tboxRcvMessage;
            this.Logger.ControlPatternLayout = "[{0}] {1} {2} {5}";

            string path = $"{GlobalVariable.Instance.DefaultPath}";
            this.PathTibRendezvous = path;
            this.PathTibRendezvousInfo = $"{path}TibRendezvousInfo.xml";
            this.PathTibRendezvousValue = $"{path}TibRendezvousValues.xml";
            this.PathTibRendezvousMessage = $@"{path}TibSimulator";
            this.PathTibRendezvousReply = $@"{this.PathTibRendezvousMessage}\Reply";

            this.Parser = new MessageParser();
            this.Parser.PathMessageReply = this.PathTibRendezvousReply;

            this.LoadTibRendezvousInfo(this.PathTibRendezvousInfo);
            this.LoadTibRendezvousValues(this.PathTibRendezvousValue);
            this.CreateMessageButtons(this.PathTibRendezvousMessage);
            this.CreateReplyButtons(this.PathTibRendezvousReply);

            this.SetLayout();
            this.SetEventHandler();

            if (this.cboxMessages.Items.Count > 0) this.cboxMessages.SelectedIndex = 0;
        }

        protected void SetLayout()
        {
            this.Text = $"TibSimulator - Ver. {Application.ProductVersion}";
#if DEBUG
            this.Text += $" : DEBUG";
#endif

            Font font = new Font("돋움체", 9.0F);

            #region [ Transport Area ]
            this.cboxServer.Font = font;
            this.cboxServer.DisplayMember = "Value";
            this.cboxServer.Items.Clear();
            this.cboxServer.Items.AddRange(this.Servers.ToArray());

            this.cboxTarget.Font = font;
            this.cboxTarget.DisplayMember = "Value";
            this.cboxMode.Font = font;
            this.cboxMode.DisplayMember = "Value";
            this.cboxService.Font = font;
            this.cboxService.DisplayMember = "Value";
            this.cboxNetwork.Font = font;
            this.cboxNetwork.DisplayMember = "Value";
            this.cboxDaemonA.Font = font;
            this.cboxDaemonA.DisplayMember = "Value";
            this.cboxDaemonS.Font = font;
            this.cboxDaemonS.DisplayMember = "Value";
            this.cboxTransportType.Font = font;
            this.cboxTransportType.Items.Add(TransportType.Reliable.ToString());
            this.cboxTransportType.Items.Add(TransportType.Certiry.ToString());
            this.cboxTransportType.Items.Add(TransportType.Distributed.ToString());
            this.cboxTransportType.SelectedIndex = 0;

            this.chkDisplaySendRequestTimeout.Checked = false;
            this.chkDisplaySendReplyTimeout.Checked = true;
            this.numSendRequestTime.Value = 30;
            this.numSendReplayTime.Value = 30;

            this.cboxMessages.Items.Clear();
            DirectoryInfo directory = new DirectoryInfo($"{GlobalVariable.Instance.DefaultPath}");
            foreach (DirectoryInfo subDirectory in directory.GetDirectories())
                if (subDirectory.Name.ToUpper().StartsWith("TIBSIMULATOR"))
                    this.cboxMessages.Items.Add(new KeyValuePair<string, string>(subDirectory.Name, subDirectory.FullName));
            this.cboxMessages.DisplayMember = "Key";
            #endregion

            #region [ Listener Area ]
            this.cboxSubject.Font = font;
            this.cboxSubject.DisplayMember = "Value";
            this.lviewSubject.Font = font;
            this.lviewSubject.ShowCheckBoxes = true;
            this.lviewSubject.AllowEdit = false;
            #endregion

            #region [ Control Area ]
            this.tboxSettingInfo.Font = font;
            this.tboxSettingInfo.ReadOnly = true;

            this.chkWordWarp.Checked = false;
            this.chkDisplay.Checked = true;
            this.chkGenerateReply.Checked = false;
            this.chkSendReply.Checked = false;
            this.chkSendAreYouThereReply.Checked = false;
            this.chkHideAreYouThere.Checked = true;
            #endregion

            #region [ Received Area ]
            this.chkAutoScroll.Checked = true;
            this.Logger.OnScrollToCaret = this.chkAutoScroll.Checked;

            this.tboxRcvMessage.Font = font;
            this.tboxRcvMessage.MaxLength = 1024 * 1024 * 5;
            this.tboxRcvMessage.ScrollBars = RichTextBoxScrollBars.Both;
            this.tboxRcvMessage.WordWrap = this.chkWordWarp.Checked;
            this.tboxRcvMessage.ForeColor = this.ColorOfNormal;
            #endregion

            #region [ Send Area ]
            this.cboxSendSubject.Font = font;
            this.cboxSendSubject.DisplayMember = "Value";

            this.tboxSendMessage.Font = font;
            this.tboxSendMessage.MaxLength = 1024 * 1024;
            #endregion

        }

        private void SetEventHandler()
        {
            this.FormClosed += this.FrmSimulator01_FormClosed;

            #region [ Transport Area ]
            this.cboxServer.SelectedValueChanged += CboxServer_SelectedValueChanged;
            this.cboxTarget.SelectedValueChanged += CboxTarget_SelectedValueChanged;
            this.cboxMode.SelectedValueChanged += CboxMode_SelectedValueChanged;
            this.cboxService.Validated += CboxService_Validated;
            this.cboxNetwork.Validated += CboxNetwork_Validated;
            this.cboxDaemonA.Validated += CboxDaemon_Validated;
            this.cboxMessages.SelectedValueChanged += CboxMessages_SelectedValueChanged;
            #endregion

            #region [ Listener Area ]
            this.cboxSubject.Validated += CboxSubject_Validated;
            this.btnAddListener.Click += this.BtnAddListener_Click;
            #endregion

            #region [ Control Area ]
            this.btnStart.Click += BtnStart_Click;
            this.btnStop.Click += BtnStop_Click;
            this.btnSettingReload.Click += BtnSettingReload_Click;
            this.btnSettingSave.Click += BtnSettingSave_Click;

            this.chkDisplay.CheckedChanged += this.ChkDisplay_CheckedChanged;
            this.chkWordWarp.CheckedChanged += this.ChkWordWarp_CheckedChanged;
            this.btnSMMBreak.Click += (sender, e) => { this.SendMultiMessage_Flag = false; };
            #endregion

            #region [ Received Area ]
            this.chkAutoScroll.CheckedChanged += this.ChkAutoScroll_CheckedChanged;
            #endregion

            #region [ Send Area ]
            this.cboxSendSubject.Validated += CboxSendSubject_Validated;
            this.btnSendRequest.Click += BtnSendRequest_Click;
            this.btnSend.Click += BtnSend_Click;
            this.btnSendReply.Click += this.BtnSendReply_Click;
            #endregion

        }

        private void FrmSimulator01_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                TibRendezvous.Close();
                this.SendMultiMessage_Flag = false;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
            }
        }

        #endregion

        #region [ Control Events ] ============================================

        private void CboxServer_SelectedValueChanged(object sender, EventArgs e)
        {
            this.cboxTarget.Items.Clear();

            this.cboxMode.Items.Clear();
            this.cboxService.Items.Clear();
            this.cboxNetwork.Items.Clear();
            this.cboxDaemonA.Items.Clear();
            this.cboxSubject.Items.Clear();
            this.lviewSubject.Items.Clear();
            this.cboxSendSubject.Items.Clear();

            Server server = this.cboxServer.SelectedItem as Server;
            if (server == null) return;

            this.cboxTarget.Items.AddRange(server.TargetServers.ToArray());
            if (this.cboxTarget.Items.Count > 0) this.cboxTarget.SelectedIndex = 0;
        }

        private void CboxTarget_SelectedValueChanged(object sender, EventArgs e)
        {
            this.cboxMode.Items.Clear();
            this.cboxService.Items.Clear();
            this.cboxNetwork.Items.Clear();
            this.cboxDaemonA.Items.Clear();
            this.cboxSubject.Items.Clear();
            this.lviewSubject.Items.Clear();
            this.cboxSendSubject.Items.Clear();

            TargetServer target = this.cboxTarget.SelectedItem as TargetServer;
            if (target == null) return;

            this.cboxMode.Items.AddRange(target.Modes.ToArray());
            if (this.cboxMode.Items.Count > 0) this.cboxMode.SelectedIndex = 0;
        }

        private void CboxMode_SelectedValueChanged(object sender, EventArgs e)
        {
            this.cboxService.Items.Clear();
            this.cboxNetwork.Items.Clear();
            this.cboxDaemonA.Items.Clear();
            this.cboxSubject.Items.Clear();
            this.lviewSubject.Items.Clear();
            this.cboxSendSubject.Items.Clear();

            Mode mode = this.cboxMode.SelectedItem as Mode;
            if (mode == null) return;

            this.cboxService.Items.AddRange(mode.Services.ToArray());
            if (this.cboxService.Items.Count > 0) this.cboxService.SelectedIndex = 0;

            this.cboxNetwork.Items.AddRange(mode.Networks.ToArray());
            if (this.cboxNetwork.Items.Count > 0) this.cboxNetwork.SelectedIndex = 0;

            this.cboxDaemonA.Items.AddRange(mode.Daemons.ToArray());
            if (this.cboxDaemonA.Items.Count > 0) this.cboxDaemonA.SelectedIndex = 0;

            this.cboxDaemonS.Items.AddRange(mode.Daemons.ToArray());
            if (this.cboxDaemonS.Items.Count > 1) this.cboxDaemonS.SelectedIndex = 1;
            else if (this.cboxDaemonS.Items.Count > 0) this.cboxDaemonS.SelectedIndex = 0;

            this.cboxSubject.Items.AddRange(mode.Subjects.ToArray());
            if (this.cboxSubject.Items.Count > 0) this.cboxSubject.SelectedIndex = 0;

            this.cboxSendSubject.Items.AddRange(mode.SendSubjects.ToArray());
            if (this.cboxSendSubject.Items.Count > 0) this.cboxSendSubject.SelectedIndex = 0;
        }

        private void CboxService_Validated(object sender, EventArgs e)
        {
            ComboBox cbox = sender as ComboBox;
            Service item = new Service("", cbox.Text);
            if (cbox.Items.Contains(item)) return;

            cbox.Items.Add(item);
            cbox.SelectedItem = item;

            Mode mode = this.cboxMode.SelectedItem as Mode;
            mode.Services.Insert(0, item);
        }

        private void CboxNetwork_Validated(object sender, EventArgs e)
        {
            ComboBox cbox = sender as ComboBox;
            Network item = new Network("", cbox.Text);
            if (cbox.Items.Contains(item)) return;

            cbox.Items.Add(item);
            cbox.SelectedItem = item;

            Mode mode = this.cboxMode.SelectedItem as Mode;
            mode.Networks.Insert(0, item);
        }

        private void CboxDaemon_Validated(object sender, EventArgs e)
        {
            ComboBox cbox = sender as ComboBox;
            Daemon item = new Daemon("", cbox.Text);
            if (cbox.Items.Contains(item)) return;

            cbox.Items.Add(item);
            cbox.SelectedItem = item;

            Mode mode = this.cboxMode.SelectedItem as Mode;
            mode.Daemons.Insert(0, item);
        }

        private void CboxSubject_Validated(object sender, EventArgs e)
        {
            ComboBox cbox = sender as ComboBox;
            Subject item = new Subject("", cbox.Text);
            if (cbox.Items.Contains(item)) return;

            cbox.Items.Add(item);
            cbox.SelectedItem = item;

            Mode mode = this.cboxMode.SelectedItem as Mode;
            mode.Subjects.Insert(0, item);
        }

        private void BtnAddListener_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.cboxSubject.Text)) return;

            ListViewDataItem item = new ListViewDataItem(this.cboxSubject.Text);
            item.CheckState = Telerik.WinControls.Enumerations.ToggleState.On;
            this.lviewSubject.Items.Add(item);
        }

        private void CboxSendSubject_Validated(object sender, EventArgs e)
        {
            ComboBox cbox = sender as ComboBox;
            SendSubject item = new SendSubject("", cbox.Text);
            if (cbox.Items.Contains(item)) return;

            cbox.Items.Add(item);
            cbox.SelectedItem = item;

            Mode mode = this.cboxMode.SelectedItem as Mode;
            mode.SendSubjects.Insert(0, item);
        }

        private void CboxMessages_SelectedValueChanged(object sender, EventArgs e)
        {
            KeyValuePair<string, string> pair = (KeyValuePair<string, string>)this.cboxMessages.SelectedItem;
            FileInfo fileInfo = null;

            this.PathTibRendezvousMessage = pair.Value;
            this.PathTibRendezvousReply = $@"{this.PathTibRendezvousMessage}\Reply";
            DirectoryInfo directoryInfo = new DirectoryInfo(this.PathTibRendezvousMessage);

            if (directoryInfo.Exists)
            {
                fileInfo = directoryInfo.GetFiles($"TibRendezvousInfo.xml").FirstOrDefault();
                if (fileInfo != null) this.PathTibRendezvousInfo = fileInfo.FullName;
                fileInfo = directoryInfo.GetFiles($"TibRendezvousValues.xml").FirstOrDefault();
                if (fileInfo != null) this.PathTibRendezvousValue = fileInfo.FullName;

                this.LoadTibRendezvousInfo(this.PathTibRendezvousInfo);
                this.LoadTibRendezvousValues(this.PathTibRendezvousValue);
                this.CreateMessageButtons(this.PathTibRendezvousMessage);
                this.CreateReplyButtons(this.PathTibRendezvousReply);
                this.Parser.PathMessageReply = this.PathTibRendezvousReply;
            }
        }


        private void BtnStart_Click(object sender, EventArgs e)
        {
            try
            {
                Mode mode = this.cboxMode.SelectedItem as Mode;
                if (mode == null) return;

                if (mode.Name == "PRO")
                {
                    TransportType transportType = this.cboxTransportType.Text.ToEnum<TransportType>(TransportType.Reliable);
                    if (transportType != TransportType.Reliable)
                    {
                        if (MessageBox.Show($"운영서버 연결을 시도하셨습니다.\r\n\r\n TransportType='{transportType.ToString()}' 연결을 허용하지 않습니다.", "주의",
                            MessageBoxButtons.OK, MessageBoxIcon.Warning) != DialogResult.OK) return;
#if !DEBUG
                        return;
#endif
                    }

                    if (this.chkSendAreYouThereReply.Checked || this.chkSendReply.Checked)
                    {
                        if (MessageBox.Show($"운영서버 연결을 시도하셨습니다.\r\n\r\nReply 기능들을 OFF하고 연결합니다.", "주의",
                            MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK) return;
                        this.chkGenerateReply.Checked = false;
                        this.chkSendReply.Checked = false;
                        this.chkSendAreYouThereReply.Checked = false;
                    }
                }

                this.TibRendezvous_Start();
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            this.TibRendezvous_Stop();
        }

        private void BtnSendRequest_Click(object sender, EventArgs e)
        {
            XmlMessage xmlMessage = new XmlMessage(this.tboxSendMessage.Text);
            xmlMessage.SendSubject = this.cboxSendSubject.Text;
            xmlMessage.SendRequestFlag = this.chkSendRequstFlag.Checked;

            this.TibRendezvous.SendRequest(xmlMessage);
            this.tboxSendMessage.Clear();
        }

        private void BtnSendReply_Click(object sender, EventArgs e)
        {
            XmlMessage xmlReply = this.tboxSendMessage.Tag as XmlMessage;
            XmlMessage xmlMessage = new XmlMessage(this.tboxSendMessage.Text);
            xmlMessage.SendSubject = this.cboxSendSubject.Text;
            xmlMessage.RequestMessage = xmlReply.RequestMessage;

            this.TibRendezvous.SendReply(xmlMessage);
            this.tboxSendMessage.Clear();
        }

        private void BtnSend_Click(object sender, EventArgs e)
        {
            XmlMessage xmlMessage = new XmlMessage(this.tboxSendMessage.Text);
            xmlMessage.SendSubject = this.cboxSendSubject.Text;
            xmlMessage.SendRequestFlag = this.chkSendRequstFlag.Checked;

            this.TibRendezvous.Send(xmlMessage);
            this.tboxSendMessage.Clear();
        }

        private void ChkAutoScroll_CheckedChanged(object sender, EventArgs e)
        {
            if (this.TibRendezvous != null) 
                this.TibRendezvous.Logger.OnScrollToCaret = this.chkAutoScroll.Checked;
            if (this.Logger != null)
                this.Logger.OnScrollToCaret = this.chkAutoScroll.Checked;
        }



        private void BtnSettingReload_Click(object sender, EventArgs e)
        {
            if (TibRendezvous != null)
            {
                this.TibRendezvous.Stop();
                Thread.Sleep(1000);
            }

            this.LoadTibRendezvousInfo(this.PathTibRendezvousInfo);
            this.LoadTibRendezvousValues(this.PathTibRendezvousValue);
            this.CreateMessageButtons(this.PathTibRendezvousMessage);
            this.CreateReplyButtons(this.PathTibRendezvousReply);
            this.Parser.PathMessageReply = this.PathTibRendezvousReply;

            this.cboxServer.Items.Clear();
            this.cboxServer.Items.AddRange(this.Servers.ToArray());
            this.cboxServer.SelectedIndex = 0;
        }

        private void BtnSettingSave_Click(object sender, EventArgs e)
        {
            string path = $"{GlobalVariable.Instance.DefaultPath}";
            this.SaveTibRendezvousInfo(this.PathTibRendezvousInfo);
            this.SaveTibRendezvousValues(this.PathTibRendezvousValue);
        }

        private void ChkDisplay_CheckedChanged(object sender, EventArgs e)
        {
            if (this.TibRendezvous != null && this.TibRendezvous.Logger != null)
            {
                if (this.chkDisplay.Checked) this.TibRendezvous.Logger.Control = this.tboxRcvMessage;
                else this.TibRendezvous.Logger.Control = null;
            }

            if (this.Logger != null)
            {
                if (this.chkDisplay.Checked) this.Logger.Control = this.tboxRcvMessage;
                else this.Logger.Control = null;
            }
        }

        private void ChkWordWarp_CheckedChanged(object sender, EventArgs e)
        {
            this.tboxRcvMessage.WordWrap = this.chkWordWarp.Checked;
        }

        #endregion

        #region [ Public Method ] =============================================
        #endregion

        #region [ Method ] ====================================================

        private void SendButtons_Focus(Control control)
        {
            this.btnSendRequest.BackColor = SystemColors.Control;
            this.btnSendReply.BackColor = SystemColors.Control;
            this.btnSend.BackColor = SystemColors.Control;

            control.BackColor = this.ButtonActiveColor;
            control.Focus();
        }

        private string GetComboxValue(ComboBox sender, string propertyName)
        {
            DataTableBase data = sender.SelectedItem as DataTableBase;
            if (data == null) return sender.Text;

            PropertyInfo propertyInfo = data.GetType().GetProperty(propertyName);
            if (propertyInfo == null) return sender.Text;

            return propertyInfo.GetValue(data).ToString();
        }

        #endregion

        #region [ TibRendezvous ] =============================================

        private void LoadTibRendezvousInfo(string fileName)
        {
            try
            {
                this.Servers.Clear();

                XmlDocument document = new XmlDocument();
                document.Load(fileName);
                XmlNode[] nodeArray = null;

                nodeArray = document.GetNodesByName("Server");
                foreach (XmlNode node in nodeArray)
                    this.Servers.Add(new Server(node));
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void SaveTibRendezvousInfo(string fileName)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"<TibInfo>");
            sb.AppendLine($"  <Servers>");
            foreach (Server server in this.cboxServer.Items)
                sb.AppendLine(server.ToXmlString("  "));
            sb.AppendLine($"  </Servers>");
            sb.AppendLine($"</TibInfo>");

            using (StreamWriter stream = new StreamWriter($"{fileName}"))
                stream.Write(sb.ToString());
        }

        private void LoadTibRendezvousValues(string fileName)
        {
            try
            {
                this.Parser.MessageValues.Clear();

                XmlDocument document = new XmlDocument();
                document.Load(fileName);
                XmlNode[] nodeArray = null;

                nodeArray = document.GetNodesByName("Item");
                foreach (XmlNode node in nodeArray)
                    this.Parser.MessageValues.Add(new MessageValue(node));

                this.gridValue.DataSource = null;
                this.gridValue.TableElement.RowHeight = 20;
                this.gridValue.EnableGrouping = false;
                this.gridValue.AllowAddNewRow = true;
                this.gridValue.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;

                this.gridValue.Columns.Clear();
                this.gridValue.AddColumn("Name", "", false).MinWidth = 100;
                this.gridValue.AddColumn("Format", "", false).MinWidth = 100;
                this.gridValue.AddColumn("Value", "", false);
                this.gridValue.DataSource = this.Parser.MessageValues;
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void SaveTibRendezvousValues(string fileName)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"<TibRendezvousValues>");
            foreach (MessageValue msgValue in this.Parser.MessageValues)
            {
                sb.Append($"    <Item Name=\"{msgValue.Name}\"");
                if (!string.IsNullOrEmpty(msgValue.Format)) sb.Append($" Format=\"{msgValue.Format}\"");
                sb.Append($">{msgValue.Value}");
                sb.AppendLine($"</Item>");
            }
            sb.AppendLine($"</TibRendezvousValues>");

            this.Logger.Debug(sb.ToString());
            using (StreamWriter stream = new StreamWriter($"{fileName}"))
                stream.Write(sb.ToString());
        }


        private void CreateMessageButtons(string path)
        {
            try
            {
                {
                    foreach (Control control in this.pnlMessage.Controls)
                        control.Click -= this.MessageButton_Click;
                    this.pnlMessage.Controls.Clear();

                    this.SendMultiMessage_Flag = true;
                    DirectoryInfo directoryInfo = new DirectoryInfo(path);
                    if (directoryInfo.Exists)
                    {
                        foreach (FileInfo fileInfo in directoryInfo.GetFiles($"*.xml"))
                        {
                            if (new string[] {
                                "TibRendezvousInfo.xml".ToLower(),
                                "TibRendezvousValues.xml".ToLower()
                            }.Contains(fileInfo.Name.ToLower())) continue;

                            Button button = new Button();
                            button.Text = Path.GetFileNameWithoutExtension(fileInfo.Name);
                            button.Tag = fileInfo.FullName;
                            button.AutoSize = true;
                            button.MinimumSize = new Size(160, 23);
                            button.Margin = new Padding(2, 2, 1, 1);
                            button.Click += this.MessageButton_Click;
                            button.MouseUp += this.MessageButton_MouseUp;
                            this.pnlMessage.Controls.Add(button);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void MessageButton_Click(object sender, EventArgs e)
        {
            StreamReader reader = null;
            try
            {
                this.tboxSendMessage.Text = string.Empty;

                Button button = sender as Button;
                if (button == null || button.Tag == null) return;
                string filename = button.Tag as string;
                if (string.IsNullOrEmpty(filename)) return;

                reader = new StreamReader(filename, Encoding.UTF8, true);
                string xmlData = reader.ReadToEnd();
                XmlMessage xmlMessage = this.Parser.CreateSendMessage(xmlData);
                this.tboxSendMessage.Text = xmlMessage.XmlData.GetText();
                this.tboxSendMessage.Tag = xmlMessage;

                if (xmlMessage.SendRequestFlag)
                {
                    this.chkSendRequstFlag.Checked = true;
                    this.SendButtons_Focus(this.btnSend);
                }
                else
                {
                    this.chkSendRequstFlag.Checked = false;
                    this.SendButtons_Focus(this.btnSend);
                }
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }

        private void MessageButton_MouseUp(object sender, MouseEventArgs e)
        {
            string filename = string.Empty;
            string sendSubject = this.cboxSendSubject.Text;
            bool sendRequestFlag = this.chkSendRequstFlag.Checked;
            int nCount = (int)this.numSMMCount.Value;
            int nInterval = (int)this.numSMMInterval.Value;

            if (e.Button != MouseButtons.Right) return;
            Button button = sender as Button;
            if (button == null || button.Tag == null) return;
            filename = button.Tag as string; ;
            if (string.IsNullOrEmpty(filename)) return;

            DialogResult result = MessageBox.Show($"{nCount}건의 메시지를 전송합니다. ({nInterval} msec)", "Test", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result != DialogResult.Yes) return;

            new Thread(delegate() {
                if (this.tboxRcvMessage.InvokeRequired)
                {
                    this.tboxRcvMessage.BeginInvoke(new Action<string, string, bool,  int, int>(SendMultiMessage), filename, sendSubject, sendRequestFlag, nCount, nInterval);
                }
                else
                {
                    this.SendMultiMessage(filename, sendSubject, sendRequestFlag, nCount, nInterval);
                }
            }).Start();
        }

        private async void SendMultiMessage(string filename, string sendSubject, bool sendRequestFlag, int count = 100, int interval = 10)
        {
            if (string.IsNullOrEmpty(filename)) return;
            this.SendMultiMessage_Flag = true;
            for (int i = 0; i < count; i++)
            {
                StreamReader reader = null;
                try
                {
                    if (!this.SendMultiMessage_Flag) break;

                    reader = new StreamReader(filename, Encoding.UTF8, true);
                    string xmlData = reader.ReadToEnd();
                    XmlMessage xmlMessage = this.Parser.CreateSendMessage(xmlData);
                    xmlMessage.SendSubject = sendSubject;
                    xmlMessage.SendRequestFlag = sendRequestFlag;
                    if (sendRequestFlag) this.TibRendezvous.SendRequest(xmlMessage);
                    else this.TibRendezvous.Send(xmlMessage);

                    await Task.Delay(interval);
                }
                catch (Exception ex)
                {
                    this.Logger.Fatal(ex);
                }
                finally
                {
                    if (reader != null) reader.Close();
                }
            }
        }

        private void CreateReplyButtons(string path)
        {
            try
            {
                {
                    foreach (Control control in this.pnlReply.Controls)
                        control.Click -= this.ReplyButton_Click;
                    this.pnlReply.Controls.Clear();

                    DirectoryInfo directoryInfo = new DirectoryInfo($@"{path}");
                    if (directoryInfo.Exists)
                    {
                        foreach (FileInfo fileInfo in directoryInfo.GetFiles($"*.xml"))
                        {
                            Button button = new Button();
                            button.Text = Path.GetFileNameWithoutExtension(fileInfo.Name);
                            button.Tag = fileInfo.FullName;
                            button.AutoSize = true;
                            button.MinimumSize = new Size(160, 23);
                            button.Margin = new Padding(2, 2, 1, 1);
                            button.Click += this.ReplyButton_Click;
                            //button.MouseDown += this.BtnTibRendezousReply_MouseDown;
                            this.pnlReply.Controls.Add(button);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void ReplyButton_Click(object sender, EventArgs e)
        {
            StreamReader reader = null;
            try
            {
                this.tboxSendMessage.Text = string.Empty;

                Button button = sender as Button;
                if (button == null || button.Tag == null) return;
                string filename = button.Tag as string;
                if (string.IsNullOrEmpty(filename)) return;

                reader = new StreamReader(filename, Encoding.UTF8, true);
                //reader = new StreamReader(filename, Encoding.Default, true);
                string xmlData = reader.ReadToEnd();
                XmlMessage xmlMessage = this.Parser.CreateSendMessage(xmlData);
                this.tboxSendMessage.Text = xmlMessage.XmlData.GetText();

                if (xmlMessage.SendRequestFlag) this.SendButtons_Focus(this.btnSendRequest);
                else this.SendButtons_Focus(this.btnSend);
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }


        private void TibRendezvous_Start()
        {
            try
            {
                this.Logger.Info($"Start");

                XLogger logger = new XLogger("TibRendzvous");
                logger.ControlPatternLayout = "[{0}] {1} {2}.{4}() {5}";
                logger.OnScrollToCaret = this.chkAutoScroll.Checked;
                if (this.chkDisplay.Checked) logger.Control = this.tboxRcvMessage;

                this.TibRendezvous_Stop();
                this.TibRendezvous = new TibRendezvous();
                this.TibRendezvous.Logger = logger;
                this.TibRendezvous.Parent = this;
                this.TibRendezvous.SendRequestTime = (int)this.numSendRequestTime.Value;
                this.TibRendezvous.SendReplyTime = (int)this.numSendReplayTime.Value;

                #region [ Set ServerInfo ]
                ServerInfo serverInfo = null;

                TransportType transportType = this.cboxTransportType.Text.ToEnum<TransportType>(TransportType.Reliable);
                // Active-Server
                serverInfo = new ServerInfo();
                serverInfo.Name = $"{this.GetComboxValue(this.cboxServer, "Value")} to {this.GetComboxValue(this.cboxTarget, "Value")}";
                serverInfo.TransportType = transportType;
                serverInfo.Service = this.GetComboxValue(cboxService, "Value");
                serverInfo.Daemon = this.GetComboxValue(cboxDaemonA, "Value");
                serverInfo.Network = this.GetComboxValue(cboxNetwork, "Value");
                serverInfo.CMName = this.cboxCMName.Text;
                serverInfo.LedgerName = this.cboxLedger.Text;
                serverInfo.RequestOld = this.chkRequestOld.Checked;
                serverInfo.SyncLedger = this.chkSyncLedger.Checked;
                this.TibRendezvous.ServerInfos.Add(serverInfo);

                // StandBy-Server
                serverInfo = new ServerInfo();
                serverInfo.Name = $"{this.GetComboxValue(this.cboxServer, "Value")} to {this.GetComboxValue(this.cboxTarget, "Value")}";
                serverInfo.TransportType = transportType;
                serverInfo.Service = this.GetComboxValue(cboxService, "Value");
                serverInfo.Daemon = this.GetComboxValue(this.cboxDaemonS, "Value");
                serverInfo.Network = this.GetComboxValue(cboxNetwork, "Value");
                serverInfo.CMName = this.cboxCMName.Text;
                serverInfo.LedgerName = this.cboxLedger.Text;
                serverInfo.RequestOld = this.chkRequestOld.Checked;
                serverInfo.SyncLedger = this.chkSyncLedger.Checked;
                this.TibRendezvous.ServerInfos.Add(serverInfo);
                #endregion

                #region [ Set ListenerInfo ]
                ListenerInfo listener = null;

                foreach(object item in this.lviewSubject.Items)
                {
                    ListViewDataItem lviewItem = item as ListViewDataItem;
                    if (lviewItem.CheckState != Telerik.WinControls.Enumerations.ToggleState.On) continue;

                    listener = new ListenerData();
                    listener.Subject = lviewItem.Text;
                    this.TibRendezvous.Listeners.Add(listener);
                }

                if (this.TibRendezvous.Listeners.Count < 1)
                {
                    listener = new ListenerData();
                    listener.Subject = this.cboxSubject.Text;
                    this.TibRendezvous.Listeners.Add(listener);
                }
                #endregion

                this.TibRendezvous.OnStarted += TibRendezvous_OnStarted;
                this.TibRendezvous.OnStoped += TibRendezvous_OnStoped;
                this.TibRendezvous.OnSend += TibRendezvous_OnSend;
                this.TibRendezvous.OnSent += TibRendezvous_OnSent;
                this.TibRendezvous.OnReceived += this.TibRendezvous_OnReceived;
                this.TibRendezvous.OnEvent += this.TibRendezvous_OnEvent;
                this.TibRendezvous.OnTimeout += this.TibRendezvous_OnTimeout;

                this.TibRendezvous.Start();
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void TibRendezvous_Stop()
        {
            try
            {
                if (this.TibRendezvous == null) return;

                this.Logger.Info($"Stop");
                this.TibRendezvous.Stop();
                this.TibRendezvous.OnStarted -= TibRendezvous_OnStarted;
                this.TibRendezvous.OnStoped -= TibRendezvous_OnStoped;
                this.TibRendezvous.OnSend -= TibRendezvous_OnSend;
                this.TibRendezvous.OnSent += this.TibRendezvous_OnSent;
                this.TibRendezvous.OnReceived -= TibRendezvous_OnReceived;
                this.TibRendezvous.OnTimeout -= this.TibRendezvous_OnTimeout;
                this.TibRendezvous = null;
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        #endregion

        #region [ TibRendezvous Events ]  =====================================

        private void TibRendezvous_OnStarted(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"{this.TibRendezvous.Transport.GetType().Name}={{");
            sb.Append($"Daemon='{this.TibRendezvous.ServerInfo.Daemon}', ");
            sb.Append($"Service='{this.TibRendezvous.ServerInfo.Service}', ");
            sb.Append($"Network='{this.TibRendezvous.ServerInfo.Network}'");

            sb.Append($", Listener={{");
            string separator = "";
            foreach (ListenerInfo info in this.TibRendezvous.Listeners)
            {
                sb.Append($"{separator}'{info?.Listener.Subject}'");
                separator = ", ";
            }
            sb.Append($"}}}}");

            this.Logger.Info($"Started");
            this.tboxSettingInfo.Text = sb.ToString();
            this.Text = $"TibSimulator - {this.cboxServer.Text} :: {this.cboxTarget.Text}";

            List<Control.ControlCollection> lstControls = new List<Control.ControlCollection>();
            lstControls.Add(this.gboxTransport.Controls);
            lstControls.Add(this.gboxListener.Controls);
            foreach(Control.ControlCollection collection in lstControls)
            {
                foreach (Control ctrl in collection)
                {
                    if (ctrl.GetType() == typeof(Label)) continue;
                    else if (ctrl.GetType() == typeof(TextBox)) ((TextBox)ctrl).ReadOnly = true;
                    else if (ctrl.GetType() == typeof(ComboBox)) ((ComboBox)ctrl).Enabled = false;
                    else ctrl.Enabled = false;
                }
            }
            this.btnStart.BackColor = Color.GreenYellow;

            // MessageValue Setting
            MessageValue messageValue = null;
            messageValue = this.Parser.MessageValues.Where(item => string.Compare(item.Name, "SendSubjectName", true) == 0).FirstOrDefault();
            if (messageValue != null) messageValue.Value = this.cboxSendSubject.Text;
            messageValue = this.Parser.MessageValues.Where(item => string.Compare(item.Name, "ReplySubjectName", true) == 0).FirstOrDefault();
            if (messageValue != null) messageValue.Value = this.cboxSubject.Text;

            this.gridValue.DataSource = null;
            this.gridValue.DataSource = this.Parser.MessageValues;
        }

        private void TibRendezvous_OnStoped(object sender, EventArgs e)
        {
            this.Logger.Info($"Stoped");

            List<Control.ControlCollection> lstControls = new List<Control.ControlCollection>();
            lstControls.Add(this.gboxTransport.Controls);
            lstControls.Add(this.gboxListener.Controls);
            foreach (Control.ControlCollection collection in lstControls)
            {
                foreach (Control ctrl in collection)
                {
                    if (ctrl.GetType() == typeof(Label)) continue;
                    else if (ctrl.GetType() == typeof(TextBox)) ((TextBox)ctrl).ReadOnly = false;
                    else if (ctrl.GetType() == typeof(ComboBox)) ((ComboBox)ctrl).Enabled = true;
                    else ctrl.Enabled = true;
                }
            }
            this.btnStart.BackColor = SystemColors.Control;
            this.Text = $"TibSimulator - Ver. {Application.ProductVersion}";
        }

        private void TibRendezvous_OnSend(TibRendezvous sender, XmlMessage xmlMessage)
        {
            try
            {
                string sendSubject = string.Empty;
                string msgName = string.Empty;
                string msgSendName = string.Empty;
                string msgReplyName = string.Empty;

                sendSubject = xmlMessage?.SendSubject;
                msgName = xmlMessage?.XmlData?.MessageName;
                msgSendName = xmlMessage?.XmlData?.SendSubjectName;
                msgReplyName = xmlMessage?.XmlData?.ReplySubjectName;

                bool isOnlyHeader = false;
                if (msgName.ToLower().StartsWith("AreYouThereRequest".ToLower()) && this.chkHideAreYouThere.Checked) isOnlyHeader = true;

                if (isOnlyHeader) this.Logger.Info($"[S] {sendSubject} - {msgName}", this.ColorOfSend);
                else this.Logger.Info($"[S] {sendSubject}{Environment.NewLine}{xmlMessage.XmlData.GetText()}", this.ColorOfSend);
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void TibRendezvous_OnSent(TibRendezvous sender, XmlMessage xmlMessage)
        {
        }

        private void TibRendezvous_OnReceived(ListenerInfo sender, XmlMessage xmlReceived)
        {
            try
            {
                if (xmlReceived == null) return;

                string listenerSubject = string.Empty;
                string msgName = string.Empty;
                string msgSendName = string.Empty;
                string msgReplyName = string.Empty;

                if (sender != null) listenerSubject = sender.Subject;
                msgName = xmlReceived?.XmlData?.MessageName;
                msgSendName = xmlReceived?.XmlData?.SendSubjectName;
                msgReplyName = xmlReceived?.XmlData?.ReplySubjectName;

                // Log Write
                bool isOnlyHeader = false;
                if (msgName.ToLower().StartsWith("AreYouThereRequest".ToLower()) && this.chkHideAreYouThere.Checked) isOnlyHeader = true;
                
                if (isOnlyHeader) this.Logger.Info($"[R] {listenerSubject} - {msgName}", this.ColorOfRecived);
                else this.Logger.Info($"[R] {listenerSubject}{Environment.NewLine}{xmlReceived.XmlData.GetText()}", this.ColorOfRecived);

                // Reply Message
                bool isReply = false;
                if (this.chkGenerateReply.Checked == false) return;
                if (this.TibRendezvous.SendRequestMessages.ContainsKey(xmlReceived.XmlData.TransactionID)) isReply = true;
                if (xmlReceived.SendRequestFlag) isReply = true;
                if (xmlReceived.XmlData.MessageName.ToUpper().EndsWith("REQUEST")) isReply = true;
                if (isReply == false) return;

                // Create Reply Message
                XmlMessage[] xmlReplys = this.Parser.CreateSendReplyMessages(xmlReceived);
                if (xmlReplys == null || xmlReplys.Length <= 0) return;

                foreach (XmlMessage xmlReply in xmlReplys)
                {
                    this.cboxSendSubject.Text = xmlReply.XmlData.SendSubjectName;
                    this.tboxSendMessage.Text = xmlReply.XmlData.GetText();
                    this.tboxSendMessage.Tag = xmlReply;

                    if (xmlReply.IsReply && xmlReply.RequestMessage != null)
                        if (!string.IsNullOrEmpty(xmlReply.RequestMessage.ReplySubject)) this.SendButtons_Focus(this.btnSendReply);
                        else this.SendButtons_Focus(this.btnSend);
                    //else if (xmlReply.SendRequestFlag) this.SendButtons_Focus(this.btnSendRequest);
                    else this.SendButtons_Focus(this.btnSend);

                    if (xmlReceived.XmlData.MessageName.ToLower().StartsWith("AreYouThereRequest".ToLower()))
                    {
                        if (this.chkSendAreYouThereReply.Checked)
                        {
                            this.TibRendezvous.SendMessage(xmlReply);
                            this.tboxSendMessage.Clear();
                            this.tboxSendMessage.Tag = null;
                            return;
                        }
                    }
                    else
                    {
                        if (this.chkSendReply.Checked)
                        {
                            this.TibRendezvous.SendMessage(xmlReply);
                            this.tboxSendMessage.Clear();
                            this.tboxSendMessage.Tag = null;
                            return;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        private void TibRendezvous_OnEvent(ListenerInfo sender, TibRendezvousEventArgs e)
        {
            if (e.Class.Equals("INFO"))
            {
                this.Logger.Info($"[R] {e.SendSubject}{Environment.NewLine}{e.ToString()}");
            }
            else if (e.Class.Equals("WARN"))
            {
                Color color = this.ColorOfWarn;
                if (e.Name.Equals("LICENSE.EXPIRE")) color = this.ColorOfTransparent;

                this.Logger.Warn($"[R] {e.SendSubject}{Environment.NewLine}{e.ToString()}", color);
            }
            else if (e.Class.Equals("ERROR"))
            {
                this.Logger.Error($"[R] {e.SendSubject}{Environment.NewLine}{e.ToString()}", this.ColorOfError);
                if (e.Name.Equals("LICENSE.EXPIRE")) this.TibRendezvous.Restart();
            }
            else
            {
                this.Logger.Info($"[R] {e.SendSubject}{Environment.NewLine}{e.ToString()}");
            }
        }

        private void TibRendezvous_OnTimeout(TibRendezvous sender, XmlMessage xmlMessage, Exception exception)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"MessageName: {xmlMessage.XmlData.MessageName}");
                sb.AppendLine($"TransactionID: {xmlMessage.XmlData.TransactionID}");
                sb.Append(exception.ToString());
                if (exception.Source == "S10001" /*Source.S10001*/)
                {
                    Color color = this.ColorOfError;
                    if (!this.chkDisplaySendRequestTimeout.Checked) color = this.ColorOfTransparent;

                    this.Logger.Warn($"[W] {xmlMessage.XmlData.SendSubjectName}{Environment.NewLine}{sb.ToString()}", color);
                }
                else if (exception.Source == "S10002" /*Source.S10002*/)
                {
                    Color color = this.ColorOfError;
                    if (!this.chkDisplaySendReplyTimeout.Checked) color = this.ColorOfTransparent;

                    this.Logger.Warn($"[W] {xmlMessage.XmlData.SendSubjectName}{Environment.NewLine}{sb.ToString()}", color);
                }
                else
                {
                    this.Logger.Warn($"[W] {xmlMessage.XmlData.SendSubjectName}{Environment.NewLine}{sb.ToString()}", this.ColorOfError);
                }
            }
            catch (Exception ex)
            {
                this.Logger.Fatal(ex);
            }
        }

        #endregion

    }

}
