// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Drawing;

namespace System.Windows.Forms.Design;

public sealed partial class AnchorEditor
{
    /// <summary>
    ///  User Interface for the AnchorEditor.
    /// </summary>
    private class AnchorUI : Control
    {
        private readonly SpringControl _bottom;
        private readonly ContainerPlaceholder _container = new();
        private readonly ControlPlaceholder _control = new();
        private readonly SpringControl _left;
        private readonly SpringControl _right;
        private readonly SpringControl[] _tabOrder;
        private readonly SpringControl _top;
        private IWindowsFormsEditorService? _editorService;
        private AnchorStyles _oldAnchor;

        public AnchorUI()
        {
            _left = new SpringControl(this) { AccessibleRole = AccessibleRole.CheckButton };
            _right = new SpringControl(this) { AccessibleRole = AccessibleRole.CheckButton };
            _top = new SpringControl(this) { AccessibleRole = AccessibleRole.CheckButton };
            _bottom = new SpringControl(this) { AccessibleRole = AccessibleRole.CheckButton };
            _tabOrder = [_left, _top, _right, _bottom];

            InitializeComponent();
        }

        public object? Value { get; private set; }

        public void End()
        {
            _editorService = null;
            Value = null;
        }

        public virtual AnchorStyles GetSelectedAnchor()
        {
            AnchorStyles baseVar = 0;
            if (_left.IsSolid)
            {
                baseVar |= AnchorStyles.Left;
            }

            if (_top.IsSolid)
            {
                baseVar |= AnchorStyles.Top;
            }

            if (_bottom.IsSolid)
            {
                baseVar |= AnchorStyles.Bottom;
            }

            if (_right.IsSolid)
            {
                baseVar |= AnchorStyles.Right;
            }

            return baseVar;
        }

        internal virtual void InitializeComponent()
        {
            int XBORDER = ScaleHelper.ScaleToInitialSystemDpi(SystemInformation.Border3DSize.Width);
            int YBORDER = ScaleHelper.ScaleToInitialSystemDpi(SystemInformation.Border3DSize.Height);
            SuspendLayout();

            var pixel_10 = ScaleHelper.ScaleToInitialSystemDpi(10);
            var pixel_30 = ScaleHelper.ScaleToInitialSystemDpi(30);
            var pixel_40 = ScaleHelper.ScaleToInitialSystemDpi(40);
            var pixel_60 = ScaleHelper.ScaleToInitialSystemDpi(60);
            var pixel_90 = ScaleHelper.ScaleToInitialSystemDpi(90);

            SetBounds(0, 0, pixel_90, pixel_90);

            AccessibleName = SR.AnchorEditorAccName;

            _container.Location = new Point(0, 0);
            _container.Size = new Size(pixel_90, pixel_90);
            _container.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;

            _control.Location = new Point(pixel_30, pixel_30);
            _control.Size = new Size(pixel_30, pixel_30);
            _control.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;

            _right.Location = new Point(pixel_60, pixel_40);
            _right.Size = new Size(pixel_30 - XBORDER, pixel_10);
            _right.TabIndex = 2;
            _right.TabStop = true;
            _right.Anchor = AnchorStyles.Right;
            _right.AccessibleName = SR.AnchorEditorRightAccName;

            _left.Location = new Point(XBORDER, pixel_40);
            _left.Size = new Size(pixel_30 - XBORDER, pixel_10);
            _left.TabIndex = 0;
            _left.TabStop = true;
            _left.Anchor = AnchorStyles.Left;
            _left.AccessibleName = SR.AnchorEditorLeftAccName;

            _top.Location = new Point(pixel_40, YBORDER);
            _top.Size = new Size(pixel_10, pixel_30 - YBORDER);
            _top.TabIndex = 1;
            _top.TabStop = true;
            _top.Anchor = AnchorStyles.Top;
            _top.AccessibleName = SR.AnchorEditorTopAccName;

            _bottom.Location = new Point(pixel_40, pixel_60);
            _bottom.Size = new Size(pixel_10, pixel_30 - YBORDER);
            _bottom.TabIndex = 3;
            _bottom.TabStop = true;
            _bottom.Anchor = AnchorStyles.Bottom;
            _bottom.AccessibleName = SR.AnchorEditorBottomAccName;

            Controls.Clear();
            Controls.AddRange(
            [
                _container
            ]);

            _container.Controls.Clear();
            _container.Controls.AddRange(
            [
                _control,
                _top,
                _left,
                _bottom,
                _right
            ]);
            ResumeLayout(false);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            _top.Focus();
        }

        private void SetValue()
        {
            Value = GetSelectedAnchor();
        }

        public void Start(IWindowsFormsEditorService edSvc, object? value)
        {
            _editorService = edSvc;
            Value = value;

            if (value is AnchorStyles anchorStyles)
            {
                _left.IsSolid = (anchorStyles & AnchorStyles.Left) == AnchorStyles.Left;
                _top.IsSolid = (anchorStyles & AnchorStyles.Top) == AnchorStyles.Top;
                _bottom.IsSolid = (anchorStyles & AnchorStyles.Bottom) == AnchorStyles.Bottom;
                _right.IsSolid = (anchorStyles & AnchorStyles.Right) == AnchorStyles.Right;
                _oldAnchor = anchorStyles;
            }
            else
            {
                _oldAnchor = AnchorStyles.Top | AnchorStyles.Left;
            }
        }

        private void Teardown(bool saveAnchor)
        {
            if (!saveAnchor)
            {
                Value = _oldAnchor;
            }

            _editorService!.CloseDropDown();
        }

        private class ContainerPlaceholder : Control
        {
            public ContainerPlaceholder()
            {
                BackColor = SystemColors.Window;
                ForeColor = SystemColors.WindowText;
                TabStop = false;
            }

            protected override void OnPaint(PaintEventArgs e)
            {
                Rectangle rc = ClientRectangle;
                ControlPaint.DrawBorder3D(e.Graphics, rc, Border3DStyle.Sunken);
            }
        }

        private class ControlPlaceholder : Control
        {
            public ControlPlaceholder()
            {
                BackColor = SystemColors.Control;
                TabStop = false;
                SetStyle(ControlStyles.Selectable, false);
            }

            protected override void OnPaint(PaintEventArgs e)
            {
                Rectangle rc = ClientRectangle;
                ControlPaint.DrawButton(e.Graphics, rc, ButtonState.Normal);
            }
        }

        private class SpringControl : Control
        {
            private readonly AnchorUI _picker;
            internal bool _focused;
            internal bool _solid;

            public SpringControl(AnchorUI picker)
            {
                _picker = picker ?? throw new ArgumentNullException(nameof(picker));
                TabStop = true;
            }

            protected override AccessibleObject CreateAccessibilityInstance()
            {
                return new SpringControlAccessibleObject(this);
            }

            public bool IsSolid
            {
                get => _solid;
                set
                {
                    if (_solid != value)
                    {
                        _solid = value;
                        _picker.SetValue();
                        Invalidate();
                    }
                }
            }

            protected override void OnGotFocus(EventArgs e)
            {
                if (!_focused)
                {
                    _focused = true;
                    Invalidate();
                }

                base.OnGotFocus(e);
            }

            protected override void OnLostFocus(EventArgs e)
            {
                if (_focused)
                {
                    _focused = false;
                    Invalidate();
                }

                base.OnLostFocus(e);
            }

            protected override void OnMouseDown(MouseEventArgs e)
            {
                IsSolid = !_solid;
                Focus();
            }

            protected override void OnPaint(PaintEventArgs e)
            {
                Rectangle rc = ClientRectangle;

                if (_solid)
                {
                    e.Graphics.FillRectangle(SystemBrushes.ControlDark, rc);
                    e.Graphics.DrawRectangle(SystemPens.WindowFrame, rc.X, rc.Y, rc.Width - 1, rc.Height - 1);
                }
                else
                {
                    ControlPaint.DrawFocusRectangle(e.Graphics, rc);
                }

                if (_focused)
                {
                    rc.Inflate(-2, -2);
                    ControlPaint.DrawFocusRectangle(e.Graphics, rc);
                }
            }

            protected override bool ProcessDialogChar(char charCode)
            {
                if (charCode == ' ')
                {
                    IsSolid = !_solid;
                    return true;
                }

                return base.ProcessDialogChar(charCode);
            }

            protected override bool ProcessDialogKey(Keys keyData)
            {
                if ((keyData & Keys.KeyCode) == Keys.Return && (keyData & (Keys.Alt | Keys.Control)) == 0)
                {
                    _picker.Teardown(true);
                    return true;
                }

                if ((keyData & Keys.KeyCode) == Keys.Escape && (keyData & (Keys.Alt | Keys.Control)) == 0)
                {
                    _picker.Teardown(false);
                    return true;
                }

                if ((keyData & Keys.KeyCode) == Keys.Tab && (keyData & (Keys.Alt | Keys.Control)) == 0)
                {
                    for (int i = 0; i < _picker._tabOrder.Length; i++)
                    {
                        if (_picker._tabOrder[i] == this)
                        {
                            i += (keyData & Keys.Shift) == 0 ? 1 : -1;
                            i = i < 0 ? i + _picker._tabOrder.Length : i % _picker._tabOrder.Length;
                            _picker._tabOrder[i].Focus();
                            break;
                        }
                    }

                    return true;
                }

                return base.ProcessDialogKey(keyData);
            }

            private class SpringControlAccessibleObject : ControlAccessibleObject
            {
                public SpringControlAccessibleObject(SpringControl owner) : base(owner)
                {
                }

                public override string DefaultAction => ((SpringControl)Owner!).IsSolid
                    ? SR.AccessibleActionUncheck
                    : SR.AccessibleActionCheck;

                public override AccessibleStates State
                {
                    get
                    {
                        AccessibleStates state = base.State;

                        if (((SpringControl)Owner!).IsSolid)
                        {
                            state |= AccessibleStates.Checked;
                        }

                        return state;
                    }
                }
            }
        }
    }
}
