using PlcCom;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Permission;
namespace PlcUiControl
{
    public class PlcUiComboBox : MyUIComboBox, ISingleInputComponent, ISingleOutputComponent, IPlcUiControl
    {
        [Category("PLC")]
        [Description("读写地址相同")]
        [DefaultValue(true)]
        public bool IsReadWriteNodeSame { get; set; } = true;

        [Category("PLC")]
        [Browsable(false)]
        [Description("地址名称")]
        public string NodeName { get; private set; }

        [Category("PLC")]
        [Description("PLC读地址")]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        public Node ReadNode { get => readNode; set { readNode = value; if (value != null) NodeName = StringExtensions.AfterLast(value.Value, '.'); if (IsReadWriteNodeSame) writeNode = value; } }
        Node readNode;

        [Category("PLC")]
        [Description("PLC写地址")]
        [Browsable(true)]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        public Node WriteNode { get => writeNode; set { writeNode = value; if (value != null) NodeName = StringExtensions.AfterLast(value.Value, '.'); if (IsReadWriteNodeSame) readNode = value; } }
        Node writeNode;

        //[Browsable(false)]
        //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Category("PLC")]
        [Description("状态集合")]
        [Browsable(true)]
        public new ListBox.ObjectCollection Items
        {
            get => base.Items;
        }
        [Category("PLC")]
        [Browsable(true)]
        [DefaultValue(1)]
        [Description("Items属性的第一个元素对应寄存器的值")]
        public int IndexBase { get; set; } = 1;

        char[] separators = { ',', '，', '.', '。', ':', '：','-','_' };

        [Category("PLC")]
        [Browsable(true)]
        [DefaultValue(false)]
        [Description("使用Item的数值前缀作为输入，以\" , ， . 。 : ： - _ \"作为分隔符。示例：“2：星期二”则会写入“2”")]
        public bool UseItemNumberPreffix { get; set; }

        [Category("PLC")]
        [Description("致使控件不可见的变量")]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        [Browsable(true)]
        public Node InvisibilityTriggerNode { get; set; }

        [Category("PLC")]
        [Description("致使控件不可见的变量的目标值")]
        [Browsable(true)]
        [DefaultValue(true)]
        public bool ValueForInvisibility { get; set; } = true;

        [Category("PLC")]
        [Description("致使控件不可用的变量")]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        [Browsable(true)]
        public Node DisabilityTriggerNode { get; set; }

        [Category("PLC")]
        [Description("致使控件不可用的变量的目标值")]
        [Browsable(true)]
        [DefaultValue(true)]
        public bool ValueForDisability { get; set; } = true;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool Invisibility { get => invisibility; set { invisibility = value; HandlePermissionChange(); } }
        private bool invisibility;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool Disability { get => disability; set { disability = value; HandlePermissionChange(); } }
        private bool disability;

        protected override void HandlePermissionChange()
        {
            CommonLib.ApplyPermissionState(this, this, this);
        }


        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new string Text
        {
            get => base.Text;
            set => base.Text = value;
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IPlcComProtocol Protocol { get; set; }

        bool isPlcChangeSelectIndex;
        Color savedColor=Color.Black;
        string lastText = string.Empty;
        public virtual void UpdateControlApperance()
        {
            if (!UseItemNumberPreffix)
            {
                if (Items.Count > 0 && state >= IndexBase && (state - IndexBase) < Items.Count)
                {
                    isPlcChangeSelectIndex = true;
                    SelectedIndex = state - IndexBase;
                    isPlcChangeSelectIndex = false;
                    ForeColor = savedColor;
                }
                else
                {
                    Text = $"{state}：未定义";
                    savedColor = ForeColor;
                    ForeColor = Color.Red;
                }
            }
            else
            {
               var itemStrings= Items
                .Cast<object>()
                .Select(item => item?.ToString() ?? string.Empty)
                .ToList();
                var index=FindIndexByFirstSegment(itemStrings, state.ToString());
                if(index>=0)
                {
                    isPlcChangeSelectIndex = true;
                    SelectedIndex = index;
                    isPlcChangeSelectIndex = false;
                    ForeColor = savedColor;
                }
                else
                {
                    Text = $"{state}：未定义";
                    savedColor = ForeColor;
                    ForeColor = Color.Red;
                }
            }
            lastText = Text;
        }
        public int FindIndexByFirstSegment(List<string> items, string strNumber)
        {
            if (string.IsNullOrEmpty(strNumber) || items == null)
                return -1;

            for (int i = 0; i < items.Count; i++)
            {
                string item = items[i] ?? string.Empty;
                string firstPart = item.Split(separators, StringSplitOptions.RemoveEmptyEntries)
                                       .FirstOrDefault()?.Trim() ?? string.Empty;

                if (string.Equals(firstPart, strNumber, StringComparison.Ordinal))
                {
                    return i; // 找到，返回索引
                }
            }

            return -1; // 未找到
        }
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int State
        {
            get => state;
            set
            {
                if (!object.Equals(state,value))
                {
                    state = value;
                    UpdateControlApperance();
                    OnPLCRegisterValueChanged?.Invoke(this,ReadNode, state);
                }
            }
        }
        private int state=-1;


        [Category("PLC")]
        [Description("PLC寄存器数据发生了变化")]
        public event RegisterValueChangedHandler OnPLCRegisterValueChanged;

        [Category("PLC")]
        [Description("用户更改了状态")]
        public event UserOperateControlHandler OnUserSettedValue;
        public PlcUiComboBox()
        {
            Text = "请设置Items属性的值";
            SelectedIndexChanged += PlcUiComboBox_SelectedIndexChanged;
            DropDownStyle = UIDropDownStyle.DropDownList;
            //UpdateControlApperance();
        }
        private void PlcUiComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!isPlcChangeSelectIndex)
            {
                if (!UseItemNumberPreffix)
                    WriteValue(SelectedIndex + IndexBase);
                else
                {
                    string str = this.Items[SelectedIndex].ToString();
                   
                    var s = str.Split(separators, StringSplitOptions.RemoveEmptyEntries)
                                .FirstOrDefault()?.Trim() ?? string.Empty;
                    var b = int.TryParse(s, out int result);
                    if (b)
                        WriteValue(result);
                    else
                        MessageBox.Show($"在自定义数据输入模式下，“{str}”必须以整数值开头","错误", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);

                }
            }
        }

        public object Read()
        {
            if (Protocol != null && ReadNode != null)
            {
                object result;
                var rt = Protocol.ReadValue(ReadNode.Value, out result);
                if (rt == 0)
                {
                    UpdateValue(result);
                    return result;
                }
                else
                {
                    OnReadRegisterFailed?.Invoke(this, ReadNode, rt);
                }
            }
            return null;
        }
        public void UpdateValue(object value)
        {
            try
            {
                switch (ReadNode.Type)
                {
                    case NodeType.BOOL:
                    case NodeType.BYTE:
                    case NodeType.WORD:
                    case NodeType.DWORD:
                    case NodeType.LWORD:
                    case NodeType.SINT:
                    case NodeType.USINT:
                    case NodeType.INT:
                    case NodeType.UINT:
                    case NodeType.DINT:
                    case NodeType.UDINT:
                    case NodeType.LINT:
                    case NodeType.ULINT:
                    case NodeType.REAL:
                    case NodeType.LREAL:
                    case NodeType.TIME:
                        State=Convert.ToInt32(value);
                        break;
                    case NodeType.STRING:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(ReadNode.Type), ReadNode.Type, null);
                }
            }
            catch (Exception ex)
            {
                OnUpdateComponentStateFailed?.Invoke(this,ReadNode, value, ex.Message);
            }
        }

        public event WriteRegisterFailedHandler OnWriteRegisterFailed;
        public event ReadRegisterFailedHandler OnReadRegisterFailed;
        public event UpdateComponentStateFailedHandler OnUpdateComponentStateFailed;

        public void WriteValue(object value)
        {
            if (Protocol == null || WriteNode == null)
                return;

            object obj = CommonLib.Convert2NodeTypeObject(WriteNode.Type, value);
            if (obj == null)
                return;
            uint result = 0;
            var rt = Protocol.WriteValue(WriteNode.Value, obj, ref result);
            if (rt != 0)
            {
                OnWriteRegisterFailed?.Invoke(this, WriteNode, rt);
            }
            OnUserSettedValue?.Invoke(this, OperationType.ItemSelected, WriteNode, lastText, Items[SelectedIndex].ToString());
        }
    }
}
