using Permission;
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.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PlcUiControl
{
    public enum ButtonType
    {
        保持型=1,
        交替型,
        设On,
        设Off,
        设值
    }
    public class PlcUiButton : MyUiButton, ISingleInputComponent,IWithMinMaxValue,ISingleOutputComponent, IPlcBindableButton, IPlcUiControl
    {
        [Category("PLC")]
        [Description("读写地址相同")]
        [DefaultValue(true)]

        public bool IsReadWriteNodeSame { get; set; } = true;

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

        [Category("PLC")]
        [Description("显示名称")]
        public override string Text { get => base.Text; set { base.Text = value;if (string.IsNullOrEmpty(OnText)) OnText = value; if (string.IsNullOrEmpty(OffText)) OffText = value; } }

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


        [Category("PLC")]
        [Description("PLC写地址")]
        [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;

        [Category("PLC")]
        [Description("按钮类型")]
        public ButtonType ButtonType { get; set; } = ButtonType.保持型;

        [Category("PLC")]
        [Description("对应寄存器为True时显示的文字")]
        public string OnText { get; set; }

        [Category("PLC")]
        [Description("对应寄存器为False时显示的文字")]
        public string OffText { get; set; }

        [Category("PLC")]
        [Description("对应寄存器为True时显示的颜色")]
        public Color OnColor { get; set; } = Color.Green;

        [Category("PLC")]
        [Description("对应寄存器为False时显示的颜色")]
        public Color OffColor { get; set; } = Color.LightSeaGreen;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Editor(typeof(Sunny.UI.UIImagePropertyEditor), typeof(UITypeEditor))]
        [DefaultValue(361452)]
        [Description("状态为True时的图案")]
        [Category("PLC")]
        public int OnSymbol { get => onSymbol; set { onSymbol = value; Symbol = value; } }
        int onSymbol= 361452;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Editor(typeof(Sunny.UI.UIImagePropertyEditor), typeof(UITypeEditor))]
        [DefaultValue(361452)]
        [Description("状态为False时的图案")]
        [Category("PLC")]
        public int OffSymbol { get => offSymbol; set { offSymbol = value; Symbol = value; } }
        int offSymbol=361452;

        [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);
        }


        [Category("PLC")]
        [Description("设值时的最小值")]
        public double Minimum { get; set; }

        [Category("PLC")]
        [Description("设值时的最大值")]
        public double Maximum { get; set; }

        [Category("PLC")]
        [Description("限值变化时")]
        public event MinMaxValueChanged MinMaxValueChanged;

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

        public virtual void UpdateControlApperance()
        {
            Text = State ? OnText : OffText;
            Color color = State ? OnColor : OffColor;
           
            FillSelectedColor = color;
            FillHoverColor = color;
            FillColor = color;
            Symbol = State ? OnSymbol : OffSymbol;
        }

        bool isFirstUpdate = true;
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool State 
        { 
            get => state;
            set
            {
                if (!isFirstUpdate)
                {
                    if (state != value)
                    {
                        state = value;
                        UpdateControlApperance();
                        OnPLCRegisterValueChanged?.Invoke(this,ReadNode, state);
                    }
                }
                else
                {
                    isFirstUpdate = false;
                    if (state != value)
                    {
                        state = value;
                        OnPLCRegisterValueChanged?.Invoke(this,ReadNode, state);
                    }
                    UpdateControlApperance();
                }
            }
        }
        private bool state;

        [Category("PLC")]
        [Description("按钮按下开始执行之前")]
        public event Action<object> BeforeMouseDown;
        [Category("PLC")]
        [Description("按钮按下执行完成后")]
        public event Action<object> AfterMouseDown;
        [Category("PLC")]
        [Description("按钮松开开始执行之前")]
        public event Action<object> BeforeMouseUp;
        [Category("PLC")]
        [Description("按钮松开执行完成后")]
        public event Action<object> AfterMouseUp;



        [Category("PLC")]
        [Description("开始写入数据前，用来确认是否写入")]
        public event EventHandler<CancelEventArgs> BeforeWrite;

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

        [Category("PLC")]
        [Description("用户设置了按键的值")]
        public event UserOperateControlHandler OnUserSettedValue;


        public PlcUiButton()
        {
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            UpdateControlApperance();
        }
        (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);
            }
        }
        DateTime timeMouseDown;

        [DllImport("user32.dll")]
        public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
        private const uint WM_LBUTTONUP = 0x0202; // 左键抬起

        bool bHaveWritten = false;
        protected override void OnCustomMouseDown(MouseEventArgs e)
        {
            BeforeMouseDown?.Invoke(this);
            switch (ButtonType)
            {
                case ButtonType.保持型:
                    
                    if (IfContinue() && IfWrite())
                    {
                        WriteValue(true);
                        if (!NeedUserEnsure)
                        {
                            timeMouseDown = DateTime.Now;
                        }
                        ButtonOperation(OperationType.MouseDown,null, true);
                        bHaveWritten = true;
                    }
                    if (NeedUserEnsure)
                    {
                        // 弹出后，手动发送 MouseUp 消息
                        IntPtr hWnd = this.Handle;
                        // 构造 lParam：鼠标位置（可以使用 e.Location，或直接用 0,0）
                        int x = e.X;  // 相对于按钮的坐标
                        int y = e.Y;
                        int lParam = (y << 16) | (x & 0xFFFF);

                        PostMessage(hWnd, WM_LBUTTONUP, 0, lParam);
                    }
                    break;

                case ButtonType.交替型:
                case ButtonType.设On:
                case ButtonType.设Off:
                case ButtonType.设值:
                    break;
            }
            AfterMouseDown?.Invoke(this);
        }
        protected override async void OnCustomMouseUp(MouseEventArgs e)
        {
            BeforeMouseUp?.Invoke(this);
            switch (ButtonType)
            {
                case ButtonType.保持型:
                    if (bHaveWritten)
                    {
                        bHaveWritten = false;
                        if (DateTime.Now - timeMouseDown < TimeSpan.FromMilliseconds(5))
                            await Task.Delay(TimeSpan.FromMilliseconds(5));
                        WriteValue(false);
                        ButtonOperation(OperationType.MouseUp, null, false);
                    }
                    break;
                case ButtonType.交替型:
                    if (IfContinue() && IfWrite())
                    {
                        WriteValue(!state);
                        ButtonOperation(OperationType.ButtonValueReverse, state, !state);
                    }
                    break;
                case ButtonType.设On:
                    if (IfContinue() && IfWrite())
                    {
                        WriteValue(true);
                        ButtonOperation(OperationType.ButtonSetValue,null, true);
                    }
                    break;
                case ButtonType.设Off:
                    if (IfContinue() && IfWrite())
                    {
                        WriteValue(false);
                        ButtonOperation(OperationType.ButtonSetValue,null, false);
                    }
                    break;
                case ButtonType.设值:
                    if (IfContinue())
                    {
                        if (WriteNode == null)
                            return;
                        var rt = Protocol.ReadValue(WriteNode.Value, out object result);
                        if (rt == 0)
                        {
                            var kb = new NumberKeybordForm(Maximum, Minimum, result);
                            var dr = kb.ShowDialog();
                            if (dr == DialogResult.OK)
                            {
                                if (kb.ValueChanged && IfWrite())
                                {
                                    var value = Convert.ToDouble(kb.Value.ToString());
                                    WriteValue(value);
                                    ButtonOperation(OperationType.ButtonSetValue, result, value);
                                }
                                if (kb.LimitChanged)
                                {
                                    this.Minimum = kb.Minimum;
                                    this.Maximum = kb.Maximum;
                                    MinMaxValueChanged?.Invoke(this, kb.Minimum, kb.Maximum);
                                }
                            }

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

                            //currentValue = result ?? "";
                            //if ( FormInput.GetInputValue(currentValue.ToString(), out double newValue, Maximum, Minimum))
                            //{
                            //    WriteValue(newValue);
                            //}
                        }
                        else
                        {
                            OnReadRegisterFailed?.Invoke(this, WriteNode, rt);
                        }
                    }
                    break;
            }
            AfterMouseUp?.Invoke(this);
        }

        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:
                        State = Convert.ToBoolean(value);
                        break;
                    case NodeType.BYTE:
                        State = Convert.ToByte(value) != 0;
                        break;
                    case NodeType.WORD:
                        State = Convert.ToUInt16(value) != 0;
                        break;
                    case NodeType.DWORD:
                        State = Convert.ToUInt32(value) != 0;
                        break;
                    case NodeType.LWORD:
                        State = Convert.ToUInt64(value) != 0;
                        break;
                    case NodeType.SINT:
                        State = Convert.ToSByte(value) != 0;
                        break;
                    case NodeType.USINT:
                        State = Convert.ToByte(value) != 0;
                        break;
                    case NodeType.INT:
                        State = Convert.ToInt16(value) != 0;
                        break;
                    case NodeType.UINT:
                        State = Convert.ToUInt16(value) != 0;
                        break;
                    case NodeType.DINT:
                        State = Convert.ToInt32(value) != 0;
                        break;
                    case NodeType.UDINT:
                        State = Convert.ToUInt32(value) != 0;
                        break;
                    case NodeType.LINT:
                        State = Convert.ToInt64(value) != 0;
                        break;
                    case NodeType.ULINT:
                        State = Convert.ToUInt64(value) != 0;
                        break;
                    case NodeType.REAL:
                        State = Convert.ToSingle(value) != 0;
                        break;
                    case NodeType.LREAL:
                        State = Convert.ToDouble(value) != 0;
                        break;
                    case NodeType.TIME:
                        State = Convert.ToUInt16(value) != 0;
                        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);
            }
        }
        void ButtonOperation(OperationType operationType,object oldValue, object newValue)
        {
            if (newValue is bool b)
            {
                OnUserSettedValue?.Invoke(this, operationType, WriteNode, oldValue, b ? "True:" + OnText : "False:" + OffText);
            }
            else
                OnUserSettedValue?.Invoke(this, operationType, WriteNode, oldValue, newValue.ToString());
        }
        public bool IfWrite()
        {
            if (BeforeWrite != null)
            {
                var args = new CancelEventArgs();
                BeforeWrite?.Invoke(this, args);
                return !args.Cancel;
            }
            return true;
        }
    }
}
