using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WjwLib.API.System;
using WjwLib.Frame.FormBase;
using WjwLib.Frame.Log;
using GatewayTool.Models;
using GatewayTool.Businesses;
using Newtonsoft.Json;
using System.IO;
using System.Diagnostics;
using GatewayTool.UserControls;
using System.Reflection;
using WjwLib.API.Algorithm;
using GatewayTool.Protocols;

namespace GatewayTool
{
    public partial class FormMain : FormBase
    {
        private FormPing _pingForm = null;
        private FormParity _parityForm = null;
        private FormJson _jsonForm = null;
        private bool _isServiceStart = false;

        private CommandDataItemCollection _lastCommandData = new CommandDataItemCollection();
        private ServiceOptions _serviceOptions = new ServiceOptions();
        private SoftwareConfigs _softwareConfigs = new SoftwareConfigs();

        public FormMain()
        {
            InitializeComponent();
            this.Icon = Resource.favicon;
            FormConfig.ConfigToModel(ref _softwareConfigs);
            ApplyConfig();
        }

        private void ApplyConfig()
        {
            this.ucLogWindow1.MaxLines = _softwareConfigs.ShowLogMaxLine;
        }

        private void menuAbout_Click(object sender, EventArgs e)
        {
            FormAbout formAbout = new FormAbout();
            formAbout.ShowDialog(this);
        }

        private void menuLanguageItem_Click(object sender, EventArgs e)
        {
            if (sender == menuSimplifiedChinese)
            {
                StrMsg = new StringMsg(Language.SIMPLIFIED_CHINESE);
            }
            else if (sender == menuEnglish)
            {
                StrMsg = new StringMsg(Language.ENGLISH);
            }
            foreach (ToolStripMenuItem menu in menuLanguage.DropDownItems)
            {
                menu.Checked = menu == sender;
            }
            InitStrings();
        }

        private void RegisterMsgBox()
        {
            MessageBoxManager.OK = StrMsg.MessageBoxButtonOK;
            MessageBoxManager.Yes = StrMsg.MessageBoxButtonYes;
            MessageBoxManager.No = StrMsg.MessageBoxButtonNo;
            MessageBoxManager.Cancel = StrMsg.MessageBoxButtonCancel;
        }

        private void InitStrings()
        {
            this.Title = StrMsg.SoftwareName;

            menuSystem.Text = StrMsg.System;
            menuLanguage.Text = StrMsg.Language;
            menuSettings.Text = StrMsg.Settings;

            menuTool.Text = StrMsg.Tool;
            menuParity.Text = StrMsg.ParityCalculator;
            menuJson.Text = StrMsg.JsonFileViewer;

            menuView.Text = StrMsg.View;
            menuLog.Text = StrMsg.Log;
            menuNail.Text = StrMsg.Nail;

            menuHelp.Text = StrMsg.Help;
            menuUsage.Text = StrMsg.Usage;
            menuAbout.Text = StrMsg.About;

            btnServiceCtrl.Text = _isServiceStart ? StrMsg.StopService : StrMsg.StartService;
            btnRefreshList.Text = StrMsg.RefreshList;
            btnOpenLog.Text = StrMsg.BrowseLog;

            this.ucDeviceList.ModuleName = StrMsg.DeviceList;
            this.ucDeviceList.ID = StrMsg.ID;
            this.ucDeviceList.SN = StrMsg.SN;
            this.ucDeviceList.Source = StrMsg.Source;
            this.ucDeviceList.FirmwareVersion = StrMsg.FirmwareVersion;

            this.ucLogWindow1.MenuSelectAllText = StrMsg.MenuSelectAll;
            this.ucLogWindow1.MenuCopyText = StrMsg.MenuCopy;
            this.ucLogWindow1.MenuClearText = StrMsg.MenuClear;

            lbStatusCopyright.Text = StrMsg.SoftwareCopyright;

            RegisterMsgBox();

            if (pnlMain.Controls.Count != 0)
            {
                Control ctrl = pnlMain.Controls[0];
                if (ctrl is UcDTUPanel)
                {
                    UcDTUPanel dTUPanel = ctrl as UcDTUPanel;
                    dTUPanel.StrMsg = StrMsg;
                }
            }
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            InitStrings();

            btnServiceCtrl_Click(sender, e);
            _serviceOptions.ExecDeviceListUpdated += _serviceOptions_ExecDeviceListUpdated;
            _serviceOptions.ExecReadSystemState += _serviceOptions_ExecReadSystemState;
            _serviceOptions.ExecReadDeviceConfig += _serviceOptions_ExecReadDeviceConfig;
            _serviceOptions.ExecWriteDeviceConfig += _serviceOptions_ExecWriteDeviceConfig;
            _serviceOptions.ExecUpgradeProgressUpdated += ExecUpgradeProgressUpdated;
            _serviceOptions.ExecUpgradeProgressFinished += _serviceOptions_ExecUpgradeProgressFinished;
            _serviceOptions.ExecServiceDataInteractive += _serviceOptions_ExecServiceDataInteractive;
            _serviceOptions.StartNetSniff(_softwareConfigs.SniffRemotePortNum);
            _serviceOptions.StartComSniff(_softwareConfigs.SniffBaudRate, _softwareConfigs.SniffDataBits, _softwareConfigs.SniffStopBits, _softwareConfigs.SniffParity);

            this.ucDeviceList.ExecItemClicked += UcDeviceList_ExecItemClicked;
        }        

        private void _serviceOptions_ExecDeviceListUpdated(object sender, EventArgs e)
        {
            this.Invoke((EventHandler)delegate
            {
                this.ucDeviceList.UpdateDeviceListUI(_serviceOptions.FoundDeviceList);
            });
        }

        private void _serviceOptions_ExecReadSystemState(object sender, GatewayBase e)
        {
            this.Invoke((EventHandler)delegate
            {
                if (this.pnlMain.Controls.Count == 0) return;
                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
                if (dtuPanel != null && e != null)
                {
                    DTU dtu = e as DTU;
                    if (dtu != null)
                    {
                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
                        {
                            dtuPanel.DeviceSystemStateObj = dtu.SystemState;
                        }
                    }
                }
            });
        }

        private void _serviceOptions_ExecReadDeviceConfig(object sender, GatewayBase e)
        {
            this.Invoke((EventHandler)delegate
            {
                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
                if (dtuPanel != null && e != null)
                {
                    DTU dtu = e as DTU;
                    if (dtu != null)
                    {
                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
                        {
                            dtuPanel.DeviceConfigObj = dtu.Config;
                        }
                    }
                }
            });
        }

        private void _serviceOptions_ExecWriteDeviceConfig(object sender, bool e)
        {
            if (e)
            {
                MessageBox.Show("配置写入设备成功！", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                MessageBox.Show("配置写入设备失败！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ExecUpgradeProgressUpdated(object sender, GatewayBase e)
        {
            this.Invoke((EventHandler)delegate
            {
                UcDTUPanel dtuPanel = this.pnlMain.Controls[0] as UcDTUPanel;
                if (dtuPanel != null && e != null)
                {
                    DTU dtu = e as DTU;
                    if (dtu != null)
                    {
                        if (dtuPanel.BindDTU.SniffInfo.SN.Equals(e.SniffInfo.SN))
                        {
                            dtuPanel.UpgradeProgress = dtu.UpgradeInfo.UpgradeProgress; //更新进度
                        }
                    }
                }
            });
        }

        private void _serviceOptions_ExecUpgradeProgressFinished(object sender, GatewayBase e)
        {
            MessageBox.Show("固件更新完毕！", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void _serviceOptions_ExecServiceDataInteractive(object sender, ServiceInteractiveData e)
        {
            string action = string.Empty;
            switch (e.DataType)
            {
                case InteractiveType.SEND:
                    action = "发送";
                    break;
                case InteractiveType.RECV:
                    action = "收到";
                    break;
                default:
                    break;
            }
            string content = e.IsConvertGB2312 ? Encoding.GetEncoding("gb2312").GetString(e.Data) : BitConverter.ToString(e.Data);
            HandleLog($"{SoftwareConfigs.Now} {action} {e.GatewaySN} ：{content}");
        }

        private void UcDeviceList_ExecItemClicked(object sender, ListViewItem e)
        {
            SniffDeviceInfo info = new SniffDeviceInfo() { SN = e.SubItems[1].Text, Source = e.SubItems[2].Text };

            UcDTUPanel dtuPanel = this.pnlMain.Controls.Count == 0 ? null : (UcDTUPanel)this.pnlMain.Controls[0];
            DoReadDTUSystemState(dtuPanel);
            if (dtuPanel != null)
            {
                //若当前面板对应的正是双击的列表项，则跳过初始化
                if (dtuPanel.BindDTU.SniffInfo.SN.Equals(info.SN)) return;
            }
            else
            {
                this.pnlMain.Controls.Clear();
                GatewayBase dev = _serviceOptions.GetExistDevice(info);
                LoadDTUPanel(dev as DTU);
            }
        }

        private void LoadDTUPanel(DTU dtu)
        {
            UcDTUPanel dtuPanel = new UcDTUPanel(dtu);
            dtuPanel.StrMsg = StrMsg;
            dtuPanel.Load += DtuPanel_Load;
            dtuPanel.ExecRefreshState += DtuPanel_ExecRefreshState;
            dtuPanel.ExecPropertyChanged += DtuPanel_ExecPropertyChanged;
            dtuPanel.ExecBindObjChanged += DtuPanel_ExecBindObjChanged;
            dtuPanel.ExecReadConfig += DtuPanel_ExecReadConfig;
            dtuPanel.ExecDownloadConfig += DtuPanel_ExecDownloadConfig;
            dtuPanel.ExecSelectTabIndexChanged += DtuPanel_ExecSelectTabIndexChanged;
            dtuPanel.ExecUpgrade += DtuPanel_ExecUpgrade;
            dtuPanel.Dock = DockStyle.Fill;
            this.pnlMain.Controls.Add(dtuPanel);
        }

        private void DtuPanel_Load(object sender, EventArgs e)
        {
            DoReadDTUSystemState(sender);
        }

        private void DtuPanel_ExecReadConfig(object sender, EventArgs e)
        {
            DoReadDTUConfig(sender);
        }

        private void DtuPanel_ExecRefreshState(object sender, EventArgs e)
        {
            DoReadDTUSystemState(sender);
        }

        private void DtuPanel_ExecSelectTabIndexChanged(object sender, int e)
        {
            if (e == 0)
            {
                DoReadDTUSystemState(sender);
            }
        }

        private void DtuPanel_ExecUpgrade(object sender, string e)
        {
            DTU dtu = GetDtuFromSender(sender);
            if (dtu == null) return;
            dtu.UpgradeInfo.FirmwareFilePath = e;
            dtu.Communication.SendData(_serviceOptions.PrepareToUpgradeCommand);
            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 准备升级命令： {BitConverter.ToString(_serviceOptions.PrepareToUpgradeCommand)}");
        }

        private DTU GetDtuFromSender(object sender)
        {
            UcDTUPanel dtuPanel = sender as UcDTUPanel;
            if (dtuPanel == null) return null;
            DTU dtu = dtuPanel.BindDTU;
            if (dtu == null || dtu.Communication == null || !dtu.Communication.IsOpen) return null;
            return dtu;
        }

        private void DoReadDTUSystemState(object sender)
        {
            DTU dtu = GetDtuFromSender(sender);
            if (dtu == null) return;
            dtu.Communication.SendData(_serviceOptions.ReadDTUSystemStateCommand);
            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 读系统状态命令： {BitConverter.ToString(_serviceOptions.ReadDTUSystemStateCommand)}");
        }

        private void DoReadDTUConfig(object sender)
        {
            DTU dtu = GetDtuFromSender(sender);
            if (dtu == null) return;
            dtu.Communication.SendData(_serviceOptions.ReadDTUDeviceConfigCommand);
            HandleLog($"{SoftwareConfigs.Now} 发送 {dtu.SniffInfo.SN} 读设备配置命令： {BitConverter.ToString(_serviceOptions.ReadDTUDeviceConfigCommand)}");
        }

        private void HandleLog(string log)
        {
            this.ucLogWindow1.ShowText(log);
            LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
        }

        private void DtuPanel_ExecDownloadConfig(object sender, string e)
        {
            if (e != null)
            {
                UcDTUPanel dtuPanel = sender as UcDTUPanel;
                if (dtuPanel != null)
                {
                    DTU dtu = dtuPanel.BindDTU;
                    if (dtu == null || dtu.Communication == null)
                    {
                        MessageBox.Show("设备或通讯实例为空！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (!dtu.Communication.IsOpen)
                    {
                        MessageBox.Show("设备通讯端口未打开！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (dtu.Communication.SendData(_serviceOptions.WriteDTUDeviceConfigCommand(e)))
                    {
                        MessageBox.Show("命令发送成功！", StrMsg.Prompt, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    else
                    {
                        MessageBox.Show("命令发送失败！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void DtuPanel_ExecBindObjChanged(object sender, EventArgs e)
        {
            var grid = sender as PropertyGrid;
            DTUConfig config = (DTUConfig)grid.SelectedObject;
            HandlePropertiesWith645(config);
        }

        private void HandlePropertiesWith645(DTUConfig config)
        {
            if (config == null) return;
            bool isTempAdd = false;
            bool isProtocol645 = config.DataSource == EnumDataSource.Protocol_645;
            PropertyGridHelper.SetPropertyVisibility(config, "Version645", isProtocol645);
            PropertyGridHelper.SetPropertyVisibility(config, "CommandData", !isProtocol645);

            if (isProtocol645)
            {
                _lastCommandData.Clear();
                for (int i = config.CommandData.Count - 1; i >= 0; i--)
                {
                    _lastCommandData.Add(config.CommandData[i]);
                }
                config.CommandData.Clear();
            }
            else
            {
                if (_lastCommandData != null)
                {
                    for (int i = _lastCommandData.Count - 1; i >= 0; i--)
                    {
                        config.CommandData.Add(_lastCommandData[i]);
                    }
                }
            }
            if (config.SensorData != null)
            {
                if (config.SensorData.Count == 0)
                {
                    config.SensorData.Add(new SensorDataItem());
                    isTempAdd = true;
                }
                foreach (var item in config.SensorData)
                {
                    PropertyInfo[] pInfo = item.GetType().GetProperties();
                    foreach (PropertyInfo p in pInfo)
                    {
                        foreach (CustomAttributeData attrData in p.CustomAttributes)
                        {
                            if (attrData.AttributeType.Equals(typeof(CategoryAttribute)))
                            {
                                string category = attrData.ConstructorArguments[0].Value.ToString().ToUpper();
                                if (category.Contains("MODBUS"))
                                {
                                    PropertyGridHelper.SetPropertyVisibility(item, p.Name, !isProtocol645);
                                }
                                else if (category.Contains("645"))
                                {
                                    PropertyGridHelper.SetPropertyVisibility(item, p.Name, isProtocol645);
                                }
                                break;
                            }
                        }
                    }
                }
            }
            if (isTempAdd)
            {
                config.SensorData.Clear();
                isTempAdd = false;
            }
            if (config.CommandData != null)
            {
                foreach (var item in config.CommandData)
                {
                    PropertyInfo[] pInfo = item.GetType().GetProperties();
                    foreach (PropertyInfo p in pInfo)
                    {
                        PropertyGridHelper.SetPropertyVisibility(item, p.Name, !isProtocol645);
                    }
                }
            }
        }

        private void DtuPanel_ExecPropertyChanged(object sender, GridPropertyChangedArgs e)
        {
            var grid = e.Obj as PropertyGrid;
            var obj = grid.SelectedObject;
            var val = e.Args.ChangedItem.Value;
            string changeItemName = e.Args.ChangedItem.PropertyDescriptor.Name;
            if (changeItemName.Equals("DataSource"))
            {
                HandlePropertiesWith645((DTUConfig)obj);
            }
            else if (changeItemName.Equals("Host"))
            {
                bool res = Validation.IsIP(val.ToString());
                if (!res)
                {
                    MessageBox.Show("非法IP地址", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    (obj as DTUConfig).Host = e.Args.OldValue?.ToString(); //恢复先前值
                }
            }

            grid.SelectedObject = obj;
        }

        private void menuToolItem_Click(object sender, EventArgs e)
        {
            if (sender == menuPing)
            {
                _pingForm = _pingForm == null ? new FormPing() : _pingForm;
                _pingForm.DomainText = StrMsg.Domain;
                _pingForm.ShowDialog(this);
            }
            else if (sender == menuParity)
            {
                _parityForm = _parityForm == null ? new FormParity() : _parityForm;
                _parityForm.Title = StrMsg.ParityCalculator;
                _parityForm.ShowDialog(this);
            }
            else if (sender == menuJson)
            {
                _jsonForm = _jsonForm == null ? new FormJson() : _jsonForm;
                _jsonForm.Title = StrMsg.JsonFileViewer;
                _jsonForm.ShowDialog(this);
            }
        }

        private void menuViewItem_Click(object sender, EventArgs e)
        {
            if (sender == menuLog)
            {
                splitContainer1.Panel2Collapsed = !menuLog.Checked;
            }
            else if (sender == menuNail)
            {
                this.TopMost = menuNail.Checked;
            }
        }

        private void btnServiceCtrl_Click(object sender, EventArgs e)
        {
            string log = "";
            if (btnServiceCtrl.Text.Equals(StrMsg.StartService))
            {
                _isServiceStart = _serviceOptions.StartService(_softwareConfigs.ServicePortNum);
                log = $"{SoftwareConfigs.Now} {_softwareConfigs.ServicePortNum}服务端口打开{(_isServiceStart ? "成功" : "失败")}";
                this.ucLogWindow1.ShowText(log);
                LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
            }
            else
            {
                _serviceOptions.StopService();
                _isServiceStart = false;
                log = $"{SoftwareConfigs.Now} 服务端口关闭";
                this.ucLogWindow1.ShowText(log, Color.Red);
                LogHelper.Log(LogTarget.Txt, _softwareConfigs.LogFilePath, log);
            }
            btnServiceCtrl.Text = _isServiceStart ? StrMsg.StopService : StrMsg.StartService;
            btnServiceCtrl.Image = _isServiceStart ? Resource.stop : Resource.start;
        }

        private void btnRefreshList_Click(object sender, EventArgs e)
        {
            _serviceOptions.StartNetSniff(_softwareConfigs.SniffRemotePortNum);
            _serviceOptions.StartComSniff(_softwareConfigs.SniffBaudRate, _softwareConfigs.SniffDataBits, _softwareConfigs.SniffStopBits, _softwareConfigs.SniffParity);
        }

        private void btnOpenLog_Click(object sender, EventArgs e)
        {
            try
            {
                if (Directory.Exists(_softwareConfigs.LogFolderPath))
                {
                    Process.Start(_softwareConfigs.LogFolderPath);
                }
                else
                {
                    MessageBox.Show("未找到日志文件夹，可能被移动或删除！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch
            {
                MessageBox.Show("打开日志文件夹异常！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void menuUsage_Click(object sender, EventArgs e)
        {
            try
            {
                if (File.Exists(_softwareConfigs.HelpFilePath))
                {
                    Process.Start(_softwareConfigs.HelpFilePath);
                }
                else
                {
                    MessageBox.Show("未找到帮助文档，可能被移动或删除！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch
            {
                MessageBox.Show("打开帮助文档异常！", StrMsg.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void menuSettings_Click(object sender, EventArgs e)
        {
            FormConfig formConfig = new FormConfig(ref _softwareConfigs);
            formConfig.ExecConfigSaved += FormConfig_ExecConfigSaved;
            formConfig.ShowDialog(this);
        }

        private void FormConfig_ExecConfigSaved(object sender, EventArgs e)
        {
            ApplyConfig();
        }
    }
}