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 PlcUiNumberInput : MyUiNumberInput, ISingleInputComponent, IWithMinMaxValue,ISingleOutputComponent, IPlcBindableNumber, 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) { SetInputType(value.Type); 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) { SetInputType(value.Type); NodeName = StringExtensions.AfterLast(value.Value, '.'); } if (IsReadWriteNodeSame) readNode = value; } }
        Node writeNode;

        [Category("PLC")]
        [Description("允许输入的最小值")]
        public new double Minimum { get => base.Minimum; set { base.Minimum = value;  } }

        [Category("PLC")]
        [Description("允许输入的最大值")]
        public new double Maximum { get => base.Maximum; set => base.Maximum = value; }

        [Category("PLC")]
        [Description("显示的整数位")]
        [Browsable(true)]
        public int IntegerDigits { get => integerDigits; set { integerDigits = value; Text = CommonLib.FormatNumber(State, integerDigits, DecimalPlaces, Gain,out double result); } }
        private int integerDigits = 0;

        [Category("PLC")]
        [Description("显示的小数位")]
        [Browsable(true)]
        public new int DecimalPlaces { get => base.DecimalPlaces; set { if (value < 0) value = 0; if (value > 6) value = 6; base.DecimalPlaces = value; Text = CommonLib.FormatNumber(State, integerDigits, base.DecimalPlaces, out double result); } }

        [Category("PLC")]
        [Description("增益")]
        [Browsable(true)]
        public double Gain { get; set; } = 1;

        [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 isValueOverMaxOrMin;
        UIEditType savedType;
        Color savedForeColor;
        public virtual void UpdateControlApperance()
        {
            if (Type == UIEditType.Integer)
                DecimalPlaces = 0;
            string str = CommonLib.FormatNumber(state, IntegerDigits, DecimalPlaces, Gain, out double result);
            
            if (result <= Maximum && result >= Minimum)
            {
                Text = str;

                if (isValueOverMaxOrMin)
                {
                    isValueOverMaxOrMin = false;
                    this.Type = savedType;
                    ForeColor = savedForeColor;
                }
            }
            else
            {
                savedType = this.Type;
                savedForeColor = ForeColor;
                isValueOverMaxOrMin = true;

                this.Type = UIEditType.String;
                Text = "超限：" + str;
                ForeColor = Color.Red;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double State
        {
            get => state;
            set
            {
               
                if (state != value)
                {
                    state = value;
                    UpdateControlApperance();
                    OnPLCRegisterValueChanged?.Invoke(this,readNode, state);
                }
            }
        }

        private double state;

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

        [Category("PLC")]
        [Description("用户设置了值")]
        public event UserOperateControlHandler OnUserSettedValue;
        public PlcUiNumberInput()
        {
            Type = UIEditType.Double;
            TextAlignment = ContentAlignment.MiddleCenter;
        }
        private void SetInputType(NodeType nodeType)
        {
            switch (nodeType)
            {
                case NodeType.BOOL:
                    break;
                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.TIME:
                    Type = UIEditType.Integer;
                    break;

                case NodeType.REAL:
                case NodeType.LREAL:
                    Type = UIEditType.Double;
                    break;
               
                case NodeType.STRING:
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(nodeType), nodeType, null);
            }
        }
        protected override void OnCunstomClick(EventArgs e)
        {
            if (writeNode == null)
                return;
            switch (WriteNode.Type)
            {
                case NodeType.BOOL:
                    break;
                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.TIME:
                    var kb = new NumberKeybordForm((int)Maximum, (int)Minimum, DoubleValue);
                    var dr = kb.ShowDialog();
                    if (dr == DialogResult.OK)
                    {

                        if (kb.LimitChanged)
                        {
                            this.Minimum = kb.Minimum;
                            this.Maximum = kb.Maximum;
                            OnMinMaxValueChanged(kb.Minimum, kb.Maximum);
                            UpdateControlApperance();
                        }

                        if (kb.ValueChanged)
                        {
                            var value = Convert.ToInt32(kb.Value);
                            WriteValue(value);
                        }

                    }

                    //var keyboard = new NumKeybordForm((int)Maximum, (int)Minimum);
                    //var dr=keyboard.ShowDialog() ;
                    //if(dr== DialogResult.OK)
                    //{
                    //    IntValue =(int)Convert.ToDouble( keyboard.Tag.ToString());
                    //    WriteValue(IntValue);
                    //}

                    //if (FormInput.GetInputValue(IntValue.ToString(), out int intValue, (int)Maximum, (int)Minimum))
                    //{
                    //    IntValue = intValue;
                    //    WriteValue(intValue);
                    //}
                    break;

                case NodeType.REAL:
                case NodeType.LREAL:
                    var kb2 = new NumberKeybordForm((double)Maximum, (double)Minimum, DoubleValue);
                    var dr2 = kb2.ShowDialog();
                    if (dr2 == DialogResult.OK)
                    {
                        if (kb2.LimitChanged)
                        {
                            this.Minimum = kb2.Minimum;
                            this.Maximum = kb2.Maximum;
                            OnMinMaxValueChanged(kb2.Minimum, kb2.Maximum);
                            UpdateControlApperance();
                        }

                        if (kb2.ValueChanged)
                        {
                            var value = Convert.ToDouble(kb2.Value);
                            WriteValue(value);
                        }
                    }

                    //var keyboard2 = new NumKeybordForm(Maximum, Minimum);
                    //var dr2 = keyboard2.ShowDialog();
                    //if (dr2==DialogResult.OK)
                    //{
                    //    DoubleValue = Convert.ToDouble(keyboard2.Tag.ToString());
                    //    WriteValue(DoubleValue);
                    //}


                    //if (FormInput.GetInputValue(DoubleValue.ToString(), out double doubleValue, Maximum, Minimum))
                    //{
                    //    DoubleValue = doubleValue;
                    //    WriteValue(doubleValue);
                    //}
                    break;

                case NodeType.STRING:
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(WriteNode.Type), WriteNode.Type, null);
            }
        }

        (bool inputOK, double result) GetInputNum(object currentValue)
        {
            NumKeybordForm frm = new NumKeybordForm(Maximum, Minimum, currentValue.ToString());
            DialogResult dr = frm.ShowDialog();
            if (dr == DialogResult.OK)
            {
                double d = Convert.ToDouble(frm.Tag.ToString());
                return (true, d);
            }
            else
            {
                return (false, 0);
            }
        }
        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.ToDouble(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;

            if (Gain != 0)
                value = Convert.ToDouble(value) / Gain;

            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.TextChange, writeNode, Text, value);
        }
    }
}
