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

using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.Layout;

namespace System.Windows.Forms;

/// <summary>
///  Defines a base class for controls that can parent other controls.
/// </summary>
public class ContainerControl : ScrollableControl, IContainerControl
{
    private Control? _activeControl;

    /// <summary>
    ///  The current focused control. Do not directly edit this value.
    /// </summary>
    private Control? _focusedControl;

    /// <summary>
    ///  The last control that requires validation. Do not directly edit this value.
    /// </summary>
    private Control? _unvalidatedControl;

    /// <summary>
    ///  Indicates whether automatic validation is turned on.
    /// </summary>
    private AutoValidate _autoValidate = AutoValidate.Inherit;

    private EventHandler? _autoValidateChanged;

    private SizeF _autoScaleDimensions = SizeF.Empty;

    private SizeF _currentAutoScaleDimensions = SizeF.Empty;

    private AutoScaleMode _autoScaleMode = AutoScaleMode.Inherit;

    /// <summary>
    ///  Top-level window is scaled by suggested rectangle received from windows WM_DPICHANGED message event.
    ///  We use this flag to indicate it is top-level window and is already scaled.
    /// </summary>
    private bool _isScaledByDpiChangedEvent;

    private BitVector32 _state;

    /// <summary>
    ///  True if we need to perform scaling when layout resumes
    /// </summary>
    private static readonly int s_stateScalingNeededOnLayout = BitVector32.CreateMask();

    /// <summary>
    ///  Indicates whether we're currently state[stateValidating].
    /// </summary>
    private static readonly int s_stateValidating = BitVector32.CreateMask(s_stateScalingNeededOnLayout);

    /// <summary>
    ///  Indicates whether we or one of our children is currently processing a mnemonic.
    /// </summary>
    private static readonly int s_stateProcessingMnemonic = BitVector32.CreateMask(s_stateValidating);

    /// <summary>
    ///  True while we are scaling a child control
    /// </summary>
    private static readonly int s_stateScalingChild = BitVector32.CreateMask(s_stateProcessingMnemonic);

    /// <summary>
    ///  Flagged when a parent changes so we can adapt our scaling logic to match.
    /// </summary>
    private static readonly int s_stateParentChanged = BitVector32.CreateMask(s_stateScalingChild);

    private static readonly int s_propAxContainer = PropertyStore.CreateKey();

    private const string FontMeasureString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /// <summary>
    ///  Child Container control that inherit <see cref="AutoScaleMode"/> (and does not store their own) would need
    ///  <see cref="AutoScaleFactor"/> from parent to scale them during Dpi changed events. We can not use
    ///  <see cref="AutoScaleFactor"/> property as it get computed with already updated Font and Dpi of their parent.
    /// </summary>
    internal SizeF _currentAutoScaleFactor = new(1F, 1F);

    /// <summary>
    ///  Initializes a new instance of the <see cref="ContainerControl"/> class.
    /// </summary>
    public ContainerControl() : base()
    {
        SetStyle(ControlStyles.AllPaintingInWmPaint, false);

        // This class overrides GetPreferredSizeCore, let Control automatically cache the result
        SetExtendedState(ExtendedStates.UserPreferredSizeCache, true);
    }

    /// <summary>
    ///  AutoScaleDimensions represents the Dpi or Font setting that the control has been scaled
    ///  to or designed at. Specifically, at design time this property will be set by the
    ///  designer to the value that the developer is designing at. Then, at runtime, when the
    ///  form loads if the CurrentAutoScaleDimensions are different from the AutoScaleDimensions,
    ///  PerformAutoScale will be called and AutoScaleDimensions will be set to the new value to
    ///  match the CurrentAutoScaleDimensions by PerformAutoScale.
    /// </summary>
    [Localizable(true)]
    [Browsable(false)]
    [SRCategory(nameof(SR.CatLayout))]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public SizeF AutoScaleDimensions
    {
        get => _autoScaleDimensions;
        set
        {
            if (value.Width < 0 || value.Height < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(value), value, SR.ContainerControlInvalidAutoScaleDimensions);
            }

            _autoScaleDimensions = value;
            if (!_autoScaleDimensions.IsEmpty)
            {
                LayoutScalingNeeded();
            }
        }
    }

    /// <summary>
    ///  AutoScaleFactor represents the scaling factor difference between
    ///  CurrentAutoScaleDimensions and AutoScaleDimensions. This value is
    ///  calculated on the fly. Eg: If CurrentAutoScaleDimensions is 192, 192
    ///  and AutoScaleDimensions is 96, 96 then the AutoScaleFactor is 2.0, 2.0
    /// </summary>
    protected SizeF AutoScaleFactor
    {
        get
        {
            SizeF current = CurrentAutoScaleDimensions;
            SizeF saved = AutoScaleDimensions;

            // If no one has configured auto scale dimensions yet, the scaling factor
            // is the unit scale.
            _currentAutoScaleFactor = GetCurrentAutoScaleFactor(current, saved);

            return _currentAutoScaleFactor;
        }
    }

    internal static SizeF GetCurrentAutoScaleFactor(SizeF currentAutoScaleDimensions, SizeF savedAutoScaleDimensions)
        => savedAutoScaleDimensions.IsEmpty
            ? new(1.0F, 1.0F)
            : new(currentAutoScaleDimensions.Width / savedAutoScaleDimensions.Width, currentAutoScaleDimensions.Height / savedAutoScaleDimensions.Height);

    /// <summary>
    ///  Determines the scaling mode of this control. The default is no scaling.
    ///  Scaling by Font is useful if you wish to have a control
    ///  or form stretch or shrink according to the size of the fonts in the system, and should
    ///  be used when the control or form's size itself does not matter.
    ///  Scaling by Dpi is useful when you wish to keep a control or form a specific size
    ///  independent of font. for example, a control displaying a chart or other graphic
    ///  may want to use Dpi scaling to increase in size to account for higher Dpi monitors.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ContainerControlAutoScaleModeDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public AutoScaleMode AutoScaleMode
    {
        get => _autoScaleMode;
        set
        {
            SourceGenerated.EnumValidator.Validate(value);

            bool scalingNeeded = false;
            if (value != _autoScaleMode)
            {
                // Invalidate any current scaling factors. If we are changing AutoScaleMode to
                // anything other than its default, we should clear out autoScaleDimensions as
                // it is nonsensical.
                if (_autoScaleMode != AutoScaleMode.Inherit)
                {
                    _autoScaleDimensions = SizeF.Empty;
                }

                _currentAutoScaleDimensions = SizeF.Empty;
                _autoScaleMode = value;
                scalingNeeded = true;
            }

            OnAutoScaleModeChanged();
            if (scalingNeeded)
            {
                LayoutScalingNeeded();
            }
        }
    }

    /// <summary>
    ///  Indicates whether controls in this container will be automatically validated when the focus changes.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [AmbientValue(AutoValidate.Inherit)]
    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ContainerControlAutoValidate))]
    public virtual AutoValidate AutoValidate
    {
        get
        {
            if (_autoValidate != AutoValidate.Inherit)
            {
                return _autoValidate;
            }

            return GetAutoValidateForControl(this);
        }
        set
        {
            if (value is < AutoValidate.Inherit or > AutoValidate.EnableAllowFocusChange)
            {
                throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(AutoValidate));
            }

            if (_autoValidate == value)
            {
                return;
            }

            _autoValidate = value;
            OnAutoValidateChanged(EventArgs.Empty);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ContainerControlOnAutoValidateChangedDescr))]
    public event EventHandler? AutoValidateChanged
    {
        add => _autoValidateChanged += value;
        remove => _autoValidateChanged -= value;
    }

    /// <summary>
    ///  The binding manager for the container control.
    /// </summary>
    [Browsable(false)]
    [SRDescription(nameof(SR.ContainerControlBindingContextDescr))]
    public override BindingContext? BindingContext
    {
        get
        {
            if (!Binding.IsSupported)
            {
                throw new NotSupportedException(SR.BindingNotSupported);
            }

            BindingContext? bm = base.BindingContext;
            if (bm is null)
            {
                bm = [];
                BindingContext = bm;
            }

            return bm;
        }

        set => base.BindingContext = value;
    }

    /// <summary>
    ///  Container controls support ImeMode only to allow child controls to inherit it from their parents.
    /// </summary>
    protected override bool CanEnableIme => false;

    /// <summary>
    ///  Indicates the current active control on the container control.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ContainerControlActiveControlDescr))]
    public Control? ActiveControl
    {
        get => _activeControl;
        set => SetActiveControl(value);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CONTROLPARENT;
            return cp;
        }
    }

    /// <summary>
    ///  Represent the actual Dpi or Font settings of the display at runtime. If the AutoScaleMode
    ///  is set to 'None' then the CurrentAutoScaleDimensions is equal to the ActualScaleDimensions.
    /// </summary>
    [Browsable(false)]
    [SRCategory(nameof(SR.CatLayout))]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public SizeF CurrentAutoScaleDimensions
    {
        get
        {
            if (_currentAutoScaleDimensions.IsEmpty)
            {
                _currentAutoScaleDimensions = GetCurrentAutoScaleDimensions(FontHandle);
            }

            return _currentAutoScaleDimensions;
        }
    }

    internal SizeF GetCurrentAutoScaleDimensions(HFONT fontHandle)
    {
        SizeF currentAutoScaleDimensions;
        switch (AutoScaleMode)
        {
            case AutoScaleMode.Font:
                currentAutoScaleDimensions = GetFontAutoScaleDimensions(fontHandle);
                break;

            case AutoScaleMode.Dpi:
                // Screen Dpi
                if (ScaleHelper.IsThreadPerMonitorV2Aware)
                {
                    currentAutoScaleDimensions = new SizeF(DeviceDpiInternal, DeviceDpiInternal);
                }
                else
                {
                    // This Dpi value comes from the primary monitor.
                    currentAutoScaleDimensions = new SizeF(ScaleHelper.InitialSystemDpi, ScaleHelper.InitialSystemDpi);
                }

                break;

            default:
                currentAutoScaleDimensions = AutoScaleDimensions;
                break;
        }

        return currentAutoScaleDimensions;
    }

    /// <summary>
    ///  Gets or sets whether the container needs to be scaled when <see cref="DpiChangedEventHandler" />,
    ///  irrespective whether the font was inherited or set explicitly.
    /// </summary>
    internal bool IsDpiChangeScalingRequired { get; set; }

    /// <summary>
    ///  Indicates the form that the scrollable control is assigned to. This property is read-only.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ContainerControlParentFormDescr))]
    public Form? ParentForm
    {
        get
        {
            if (ParentInternal is not null)
            {
                return ParentInternal.FindForm();
            }

            if (this is Form)
            {
                return null;
            }

            return FindForm();
        }
    }

    /// <summary>
    ///  Activates the specified control.
    /// </summary>
    bool IContainerControl.ActivateControl(Control control)
    {
        return ActivateControl(control, originator: true);
    }

    internal bool ActivateControl(Control control)
    {
        return ActivateControl(control, originator: true);
    }

    internal bool ActivateControl(Control? control, bool originator)
    {
        // Recursive function that makes sure that the chain of active controls is coherent.
        bool ret = true;
        bool updateContainerActiveControl = false;
        ContainerControl? containerControl = null;
        Control? parent = ParentInternal;
        if (parent is not null)
        {
            containerControl = parent.GetContainerControl() as ContainerControl;
            if (containerControl is not null)
            {
                updateContainerActiveControl = (containerControl.ActiveControl != this);
            }
        }

        if (control != _activeControl || updateContainerActiveControl)
        {
            if (updateContainerActiveControl)
            {
                if (containerControl is not null && !containerControl.ActivateControl(this, false))
                {
                    return false;
                }
            }

            ret = AssignActiveControlInternal((control == this) ? null : control);
        }

        if (originator)
        {
            ScrollActiveControlIntoView();
        }

        return ret;
    }

    /// <summary>
    ///  Used for UserControls - checks if the control has a focusable control inside or not
    /// </summary>
    private bool HasFocusableChild()
    {
        Control? ctl = null;
        do
        {
            ctl = GetNextControl(ctl, true);
            if (ctl is not null && ctl.CanSelect && ctl.TabStop)
            {
                break;
            }
        }
        while (ctl is not null);

        return ctl is not null;
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void AdjustFormScrollbars(bool displayScrollbars)
    {
        base.AdjustFormScrollbars(displayScrollbars);

        if (!GetScrollState(ScrollStateUserHasScrolled))
        {
            ScrollActiveControlIntoView();
        }
    }

    /// <summary>
    ///  Cleans up form state after a control has been removed.
    /// </summary>
    internal virtual void AfterControlRemoved(Control control, Control oldParent)
    {
        ContainerControl? cc;
        Debug.Assert(control is not null);

        if (control == _activeControl || control.Contains(_activeControl))
        {
            bool selected = SelectNextControl(control, true, true, true, true);
            if (selected && _activeControl != control)
            {
                // Add the check. If it is set to true, do not call into FocusActiveControlInternal().
                // The TOP MDI window could be gone and CreateHandle method will fail
                // because it try to create a parking window Parent for the MDI children
                if (_activeControl?.Parent is not null && !_activeControl.Parent.IsTopMdiWindowClosing)
                {
                    FocusActiveControlInternal();
                }
            }
            else
            {
                SetActiveControl(null);
            }
        }
        else if (_activeControl is null && ParentInternal is not null)
        {
            // The last control of an active container was removed. Focus needs to be given to the next
            // control in the Form.
            cc = ParentInternal.GetContainerControl() as ContainerControl;
            if (cc is not null && cc.ActiveControl == this)
            {
                Form? f = FindForm();
                f?.SelectNextControl(this, true, true, true, true);
            }
        }

        // Two controls in UserControls that don't take focus via UI can have bad behavior if ...
        // When a control is removed from a container, not only do we need to clear the unvalidatedControl of that
        // container potentially, but the unvalidatedControl of all its container parents, up the chain, needs to
        // now point to the old parent of the disappearing control.
        cc = this;
        while (cc is not null)
        {
            Control? parent = cc.ParentInternal;
            if (parent is null)
            {
                break;
            }
            else
            {
                cc = parent.GetContainerControl() as ContainerControl;
            }

            if (cc is not null
                && cc._unvalidatedControl is not null
                && (cc._unvalidatedControl == control || control.Contains(cc._unvalidatedControl)))
            {
                cc._unvalidatedControl = oldParent;
            }
        }

        if (control == _unvalidatedControl || control.Contains(_unvalidatedControl))
        {
            _unvalidatedControl = null;
        }
    }

    private bool AssignActiveControlInternal(Control? value)
    {
#if DEBUG
        if (value is null || (value is not null && value.ParentInternal is not null && !value.ParentInternal.IsContainerControl))
        {
            Debug.Assert(value is null || (value.ParentInternal is not null && this == value.ParentInternal.GetContainerControl()));
        }
#endif

        if (_activeControl != value)
        {
            try
            {
                value?.BecomingActiveControl = true;

                _activeControl = value;
                UpdateFocusedControl();
            }
            finally
            {
                value?.BecomingActiveControl = false;
            }

            if (_activeControl == value)
            {
                Form? form = FindForm();
                form?.UpdateDefaultButton();
            }
        }
        else
        {
            _focusedControl = _activeControl;
        }

        return _activeControl == value;
    }

    /// <summary>
    ///  Used to notify the AxContainer that the form has been created. This should only be called
    ///  if there is an AX container.
    /// </summary>
    private void AxContainerFormCreated() => Properties.GetValueOrDefault<AxHost.AxContainer>(s_propAxContainer)?.FormCreated();

    /// <summary>
    ///  Specifies whether this control can process the mnemonic or not.
    /// </summary>
    internal override bool CanProcessMnemonic() => _state[s_stateProcessingMnemonic] || base.CanProcessMnemonic();

    internal AxHost.AxContainer CreateAxContainer()
    {
        if (!Properties.TryGetValue(s_propAxContainer, out AxHost.AxContainer? container))
        {
            container = Properties.AddValue(s_propAxContainer, new AxHost.AxContainer(this));
        }

        return container;
    }

    /// <summary>
    ///  Disposes of the resources (other than memory) used by the <see cref="ContainerControl"/>.
    /// </summary>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _activeControl = null;
        }

        base.Dispose(disposing);

        _focusedControl = null;
        _unvalidatedControl = null;
    }

    /// <summary>
    ///  Recursively enables required scaling from the given control
    /// </summary>
    private static void EnableRequiredScaling(Control start, bool enable)
    {
        start.RequiredScalingEnabled = enable;
        foreach (Control c in start.Controls)
        {
            EnableRequiredScaling(c, enable);
        }
    }

    /// <summary>
    ///  Assigns focus to the active Control. If there is no active Control then focus is given to the Form.
    /// </summary>
    internal void FocusActiveControlInternal()
    {
        // Things really get ugly if you try to pop up an assert dialog here
        Debug.WriteLineIf(_activeControl is not null
            && !Contains(_activeControl), "ActiveControl is not a child of this ContainerControl");

        if (_activeControl is not null && _activeControl.Visible)
        {
            // Avoid focus loops, especially with ComboBoxes.
            HWND focusHandle = PInvoke.GetFocus();
            if (focusHandle.IsNull || FromChildHandle(focusHandle) != _activeControl)
            {
                PInvoke.SetFocus(_activeControl);
            }
        }
        else
        {
            // Determine and focus closest visible parent
            ContainerControl? containerControl = this;
            while (containerControl is not null && !containerControl.Visible)
            {
                Control? parent = containerControl.ParentInternal;
                if (parent is not null)
                {
                    containerControl = parent.GetContainerControl() as ContainerControl;
                }
                else
                {
                    break;
                }
            }

            if (containerControl is not null && containerControl.Visible)
            {
                PInvoke.SetFocus(containerControl);
            }
        }
    }

    private SizeF GetParentAutoScaleFactor()
    {
        Control? parentControl = Parent;

        // Traverse through parent hierarchy until we get a ContainerControl whose AutoScaleMode is not Inherit.
        // AutoscaleFactor from this parent is used to scale the child controls within its hierarchy.
        while (parentControl is not null
            && (parentControl is not ContainerControl containerControl
                || containerControl.AutoScaleMode == AutoScaleMode.Inherit))
        {
            parentControl = parentControl.Parent;
        }

        return parentControl is ContainerControl container ? container._currentAutoScaleFactor : new SizeF(1F, 1F);
    }

    internal override Size GetPreferredSizeCore(Size proposedSize)
    {
        // Translating 0,0 from ClientSize to actual Size tells us how much space
        // is required for the borders.
        Size borderSize = SizeFromClientSize(Size.Empty);
        Size totalPadding = borderSize + Padding.Size;
        return LayoutEngine.GetPreferredSize(this, proposedSize - totalPadding) + totalPadding;
    }

    internal override Rectangle GetToolNativeScreenRectangle()
    {
        if (GetTopLevel())
        {
            // Get window's client rectangle (i.e. without chrome) expressed in screen coordinates
            PInvokeCore.GetClientRect(this, out RECT clientRectangle);
            Point topLeftPoint = default;
            PInvoke.ClientToScreen(this, ref topLeftPoint);
            return new Rectangle(topLeftPoint.X, topLeftPoint.Y, clientRectangle.right, clientRectangle.bottom);
        }

        return base.GetToolNativeScreenRectangle();
    }

    /// <summary>
    ///  This method calculates the auto scale dimensions based on the control's current font.
    /// </summary>
    private unsafe SizeF GetFontAutoScaleDimensions(HFONT fontHandle)
    {
        SizeF retval = SizeF.Empty;

        // Windows uses CreateCompatibleDC(NULL) to get a memory DC for
        // the monitor the application is currently on.

        using CreateDcScope dc = new(default);
        if (dc.IsNull)
        {
            throw new Win32Exception();
        }

        // We clone the Windows scaling function here as closely as
        // possible. They use textmetric for height, and textmetric
        // for width of fixed width fonts. For variable width fonts
        // they use GetTextExtentPoint32 and pass in a long a-Z string.
        // We must do the same here if our dialogs are to scale in a
        // similar fashion.

        using SelectObjectScope fontSelection = new(dc, fontHandle);

        TEXTMETRICW tm = default;
        PInvoke.GetTextMetrics(dc, &tm);

        retval.Height = tm.tmHeight;

        if ((tm.tmPitchAndFamily & TMPF_FLAGS.TMPF_FIXED_PITCH) != 0)
        {
            Size size = default;
            fixed (char* ps = FontMeasureString)
            {
                PInvoke.GetTextExtentPoint32W(dc, ps, FontMeasureString.Length, (SIZE*)(void*)&size);
            }

            // Note: intentional integer round off here for Win32 compat
            retval.Width = (int)Math.Round(size.Width / ((float)FontMeasureString.Length));
        }
        else
        {
            retval.Width = tm.tmAveCharWidth;
        }

        return retval;
    }

    /// <summary>
    ///  This method is called when one of the auto scale properties changes, indicating that we
    ///  should scale controls on the next layout.
    /// </summary>
    private void LayoutScalingNeeded()
    {
        EnableRequiredScaling(this, true);
        _state[s_stateScalingNeededOnLayout] = true;

        // If layout is not currently suspended, then perform a layout now,
        // as otherwise we don't know when one will happen.
        if (!IsLayoutSuspended)
        {
            LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds);
        }
    }

    /// <summary>
    ///  To maintain backwards compat with AutoScale on form, we need to keep the
    ///  two models from conflicting. This method is only here for Form to override
    ///  it and update its AutoScale property appropriately.
    /// </summary>
    private protected virtual void OnAutoScaleModeChanged()
    {
    }

    /// <summary>
    ///  Raises the AutoValidateChanged event.
    /// </summary>
    protected virtual void OnAutoValidateChanged(EventArgs e) => _autoValidateChanged?.Invoke(this, e);

    private protected override void OnFrameWindowActivate(bool fActivate)
    {
        if (fActivate)
        {
            if (ActiveControl is null)
            {
                SelectNextControl(ctl: null, forward: true, tabStopOnly: true, nested: true, wrap: false);
            }

            InnerMostActiveContainerControl.FocusActiveControlInternal();
        }
    }

    /// <summary>
    ///  Called when a child is about to resume its layout. The default implementation
    ///  calls OnChildLayoutResuming on the parent.
    /// </summary>
    internal override void OnChildLayoutResuming(Control child, bool performLayout)
    {
        base.OnChildLayoutResuming(child, performLayout);

        // do not scale children if AutoScaleMode is set to Dpi
        if (AutoScaleMode == AutoScaleMode.Dpi)
        {
            return;
        }

        // We need to scale children before their layout engines get to them.
        // We don't have a lot of opportunity for that because the code
        // generator always generates a PerformLayout() right after a
        // ResumeLayout(false), so this seems to be the most opportune place
        // for this.

        // Skip Scale() when AutoscaleFactor is 100% (evaluated to 1.0F for both width and height) as it is a no-op.
        // It means the form is designed on the monitor that has same settings as the monitor that
        // it is being run.
        if (!_state[s_stateScalingChild]
            && !performLayout
            && AutoScaleMode != AutoScaleMode.None && AutoScaleMode != AutoScaleMode.Inherit
            && _state[s_stateScalingNeededOnLayout]
            && (AutoScaleFactor.Width != 1.0F || AutoScaleFactor.Height != 1.0F))
        {
            _state[s_stateScalingChild] = true;
            try
            {
                child.Scale(AutoScaleFactor, SizeF.Empty, this);
            }
            finally
            {
                _state[s_stateScalingChild] = false;
            }
        }
    }

    /// <summary>
    ///  Raises the CreateControl event.
    /// </summary>
    protected override void OnCreateControl()
    {
        base.OnCreateControl();

        if (Properties.ContainsKey(s_propAxContainer))
        {
            AxContainerFormCreated();
        }

        OnBindingContextChanged(EventArgs.Empty);
    }

    /// <summary>
    ///  We override this to clear the current autoscale cache.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void OnFontChanged(EventArgs e)
    {
        // Font may be updated for container controls that are set
        // to scale in Dpi mode (during WM_DPICHANGED event).
        // This may require scaling/relayout of the form. AutoScaleFactor will take
        // AutoScaleMode into account while scaling the controls.
        if (AutoScaleMode != AutoScaleMode.None && IsHandleCreated)
        {
            _currentAutoScaleDimensions = SizeF.Empty;

            // If the font changes and we are going to autoscale
            // as a result, do it now, and wrap the entire
            // transaction in a suspend layout to prevent
            // the layout engines from conflicting with our
            // work.
            SuspendAllLayout(this);

            try
            {
                // Parameter 'causedByFontChanged' helps to differentiate the scaling between ResumeLayout and FontChanged event.
                PerformAutoScale(!RequiredScalingEnabled, excludedBounds: true, causedByFontChanged: true);
            }
            finally
            {
                ResumeAllLayout(this, performLayout: false);
            }
        }

        base.OnFontChanged(e);
    }

    /// <summary>
    ///  Container controls scale during layout.
    /// </summary>
    protected override void OnLayout(LayoutEventArgs e)
    {
        PerformNeededAutoScaleOnLayout();
        base.OnLayout(e);
    }

    /// <summary>
    ///  Called when the last resume layout call is made. If performLayout is true a layout will
    ///  occur as soon as this call returns. Layout is still suspended when this call is made.
    ///  The default implementation calls OnChildLayoutResuming on the parent, if it exists.
    /// </summary>
    internal override void OnLayoutResuming(bool performLayout)
    {
        PerformNeededAutoScaleOnLayout();
        base.OnLayoutResuming(performLayout);
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        ResetToolTip();
    }

    /// <summary>
    ///  Called when the parent changes. Container controls prefer to have their parents scale
    ///  themselves, but when a parent is first changed, and as a result the font changes as
    ///  well, a container control should scale itself. We save off this state so a later
    ///  font change can trigger a scale of us. We only set this state if required scaling is
    ///  disabled:  if it is enabled we are still initializing and parent changes are normal.
    /// </summary>
    protected override void OnParentChanged(EventArgs e)
    {
        _state[s_stateParentChanged] = !RequiredScalingEnabled;
        base.OnParentChanged(e);
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        ResetToolTip();
    }

    /// <summary>
    ///  Performs scaling of this control. Scaling works by scaling all children of this control.
    ///  Those children that are ContainerControls will have their PerformAutoScale method called
    ///  so they can scale their children.
    /// </summary>
    public void PerformAutoScale() => PerformAutoScale(includedBounds: true, excludedBounds: true);

    /// <summary>
    ///  Performs scaling of this control. Scaling works by scaling all children of this control.
    ///  PerformAutoScale is automatically called during OnLayout. The parameters to
    ///  PerformAutoScale are passed as follows:
    ///  1. If AutoScaleDimensions are set, includedBounds is set to true.
    ///  2. If a font change occurred, excludedBounds is set to true.
    /// </summary>
    /// <param name="includedBounds">If includedBounds is true those controls whose bounds have changed since
    ///  they were last scaled will be auto scaled.</param>
    ///  <param name="excludedBounds">
    ///   If excludedBounds is true those controls whose bounds have not changed
    ///   since they were last scaled will be auto scaled.
    ///  </param>
    ///  <param name="causedByFontChanged">
    ///   Helps to distinguish the scaling by ResumeLayout or <see cref="OnFontChanged(EventArgs)"/> event.
    ///   Scaling by <see cref="OnFontChanged(EventArgs)"/> event does not need to scale child container control as
    ///   they receive their own <see cref="OnFontChanged(EventArgs)"/> event.
    ///  </param>
    private void PerformAutoScale(bool includedBounds, bool excludedBounds, bool causedByFontChanged = false)
    {
        bool suspended = false;

        try
        {
            if (AutoScaleMode != AutoScaleMode.None)
            {
                SuspendAllLayout(this);
                suspended = true;

                SizeF autoScaleFactor = AutoScaleFactor;

                // Container controls at child level that inherit autoscale mode but does not store
                // AutoScaleDimensions, we would need to scale those controls with their parent AutoScaleFactor.
                if (AutoScaleMode == AutoScaleMode.Inherit)
                {
                    autoScaleFactor = GetParentAutoScaleFactor();
                }

                if (autoScaleFactor.Width != 1.0F || autoScaleFactor.Height != 1.0F)
                {
                    // Walk each control recursively and scale. We search the control
                    // for its own set of scaling data; if we don't find it, we use the current
                    // container control's scaling data. Once we scale a control, we set
                    // its scaling factors to unity. As we walk out of a container control,
                    // we set its scaling factor to unity too.
                    SizeF included = includedBounds ? autoScaleFactor : SizeF.Empty;
                    SizeF excluded = excludedBounds ? autoScaleFactor : SizeF.Empty;
                    Scale(included, excluded, this, causedByFontChanged);
                }

                _autoScaleDimensions = CurrentAutoScaleDimensions;
            }
        }
        finally
        {
            if (includedBounds)
            {
                _state[s_stateScalingNeededOnLayout] = false;
                EnableRequiredScaling(this, enable: false);
            }

            _state[s_stateParentChanged] = false;

            if (suspended)
            {
                ResumeAllLayout(this, performLayout: false);
            }
        }
    }

    /// <summary>
    ///  Checks to see if we need to perform an autoscale in response to a layout.
    /// </summary>
    private void PerformNeededAutoScaleOnLayout()
    {
        if (_state[s_stateScalingNeededOnLayout])
        {
            PerformAutoScale(_state[s_stateScalingNeededOnLayout], false);
        }
    }

    private void ResetToolTip()
    {
        if (GetTopLevel())
        {
            KeyboardToolTipStateMachine.Reset();
        }
    }

    /// <summary>
    ///  Recursively resumes all layout.
    /// </summary>
    internal static void ResumeAllLayout(Control start, bool performLayout)
    {
        ControlCollection controlsCollection = start.Controls;
        // This may have changed the sizes of our children.
        // PERFNOTE: This is more efficient than using Foreach. Foreach
        // forces the creation of an array subset enum each time we
        // enumerate
        for (int i = 0; i < controlsCollection.Count; i++)
        {
            ResumeAllLayout(controlsCollection[i], performLayout);
        }

        start.ResumeLayout(performLayout);
    }

    /// <summary>
    ///  Recursively suspends all layout.
    /// </summary>
    internal static void SuspendAllLayout(Control start)
    {
        start.SuspendLayout();
        CommonProperties.xClearPreferredSizeCache(start);

        ControlCollection controlsCollection = start.Controls;
        // This may have changed the sizes of our children. For performance, this is more
        // efficient than using Foreach. Foreach forces the creation of an array subset enum
        // each time we enumerate
        for (int i = 0; i < controlsCollection.Count; i++)
        {
            SuspendAllLayout(controlsCollection[i]);
        }
    }

    /// <summary>
    ///  Overrides the default scaling mechanism to account for autoscaling. This override
    ///  behaves as follows: any unchanged controls are always scaled according to the container
    ///  control's <see cref="AutoScaleFactor"/>. Any changed controls are scaled according to the provided
    ///  scaling factor.
    /// </summary>
    internal override void Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, bool causedByFontChanged = false)
    {
        // If we're inheriting our scaling from our parent, Scale is really easy:  just do the
        // base class implementation.
        if (AutoScaleMode == AutoScaleMode.Inherit)
        {
            base.Scale(includedFactor, excludedFactor, requestingControl, causedByFontChanged);
        }
        else
        {
            // We scale our controls based on our own auto scaling
            // factor, not the one provided to us. We only do this for
            // controls that are not required to be scaled (excluded controls).
            SizeF ourExcludedFactor = excludedFactor;
            SizeF childIncludedFactor = includedFactor;

            if (!ourExcludedFactor.IsEmpty)
            {
                ourExcludedFactor = AutoScaleFactor;
            }

            // If we're not supposed to be scaling, don't scale the internal ones either.
            if (AutoScaleMode == AutoScaleMode.None)
            {
                childIncludedFactor = AutoScaleFactor;
            }

            // When we scale, we are establishing new baselines for the
            // positions of all controls. Therefore, we should resume(false).
            using (new LayoutTransaction(this, this, PropertyNames.Bounds, false))
            {
                // Our own container control poses a problem. We want
                // an outer control to be responsible for scaling it,
                // because the outer control knows the container's dimensions.
                // We detect this by checking who is requesting that the
                // scaling occur.
                SizeF ourExternalContainerFactor = ourExcludedFactor;

                if (!excludedFactor.IsEmpty && ParentInternal is not null)
                {
                    ourExternalContainerFactor = SizeF.Empty;

                    bool scaleUs = (requestingControl != this || _state[s_stateParentChanged] || causedByFontChanged);

                    // For design time support:  we may be parented within another form
                    // that is not part of the designer.
                    if (!scaleUs)
                    {
                        bool dt = Site?.DesignMode ?? false;
                        bool parentDt = ParentInternal.Site?.DesignMode ?? false;
                        if (dt && !parentDt)
                        {
                            scaleUs = true;
                        }
                    }

                    if (scaleUs)
                    {
                        ourExternalContainerFactor = excludedFactor;
                    }
                }

                // Top-level window may be already scaled by WM_DPICHANGE message. So, we skip it in such case.
                if (!_isScaledByDpiChangedEvent)
                {
                    ScaleControl(includedFactor, ourExternalContainerFactor);
                }

                if (!_doNotScaleChildren)
                {
                    ScaleChildControls(childIncludedFactor, ourExcludedFactor, requestingControl, causedByFontChanged);
                }
            }
        }
    }

    /// <summary>
    ///  Scales container's properties Min and Max size with the scale factor provided.
    /// </summary>
    /// <param name="xScaleFactor">The scale factor to be applied on width of the property being scaled.</param>
    /// <param name="yScaleFactor">The scale factor to be applied on height of the property being scaled.</param>
    /// <param name="updateContainerSize">
    ///  <see langword="true"/> to resize of the container control along with properties being scaled;
    ///  otherwise, <see langword="false"/>.
    /// </param>
    protected virtual void ScaleMinMaxSize(float xScaleFactor, float yScaleFactor, bool updateContainerSize = true)
    { }

    /// <summary>
    ///  Process an arrowKey press by selecting the next control in the group that the activeControl
    ///  belongs to.
    /// </summary>
    private bool ProcessArrowKey(bool forward)
    {
        Control? group = this;
        if (_activeControl is not null)
        {
            group = _activeControl.ParentInternal;
        }

        return group?.SelectNextControl(_activeControl, forward, false, false, true) ?? false;
    }

    /// <summary>
    ///  Processes a dialog character. Overrides Control.processDialogChar(). This method calls
    ///  the ProcessMnemonic() method to check if the character is a mnemonic for one of the
    ///  controls on the form. If processMnemonic() does not consume the character, then
    ///  base.ProcessDialogChar() is called.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override bool ProcessDialogChar(char charCode)
    {
        // If we're the top-level form or control, we need to do the mnemonic handling
        if (GetContainerControl() is ContainerControl && charCode != ' ' && ProcessMnemonic(charCode))
        {
            return true;
        }

        return base.ProcessDialogChar(charCode);
    }

    protected override bool ProcessDialogKey(Keys keyData)
    {
        LastKeyData = keyData;
        if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None)
        {
            Keys keyCode = keyData & Keys.KeyCode;
            switch (keyCode)
            {
                case Keys.Tab:
                    if (ProcessTabKey((keyData & Keys.Shift) == Keys.None))
                    {
                        return true;
                    }

                    break;
                case Keys.Left:
                case Keys.Right:
                case Keys.Up:
                case Keys.Down:
                    if (ProcessArrowKey(keyCode is Keys.Right or Keys.Down))
                    {
                        return true;
                    }

                    break;
            }
        }

        return base.ProcessDialogKey(keyData);
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (base.ProcessCmdKey(ref msg, keyData))
        {
            return true;
        }

        if (ParentInternal is null)
        {
            // Unfortunately, we have to stick this here for the case where we're hosted without
            // a form in the chain. This would be something like a context menu strip with shortcuts
            // hosted within Office, VS or IE.
            //
            // this is an optimized search O(number of ToolStrips in thread)
            // that happens only if the key routing makes it to the top.
            return ToolStripManager.ProcessCmdKey(ref msg, keyData);
        }

        return false;
    }

    protected internal override bool ProcessMnemonic(char charCode)
    {
        if (!CanProcessMnemonic())
        {
            return false;
        }

        if (Controls.Count == 0)
        {
            return false;
        }

        // Start with the active control.
        Control? start = ActiveControl;

#if DEBUG
        int count = 0;
#endif

        // Set the processing mnemonic flag so child controls don't check for it when checking if they
        // can process the mnemonic.
        _state[s_stateProcessingMnemonic] = true;

        bool processed = false;

        try
        {
            // Safety flag to avoid infinite loop when testing controls in a container.
            bool wrapped = false;

            Control? ctl = start;

            do
            {
                // Loop through the controls starting at the control next to the current Active control in the Tab order
                // till we find someone willing to process this mnemonic.
                // We don't start the search on the Active control to allow controls in the same container with the same
                // mnemonic (bad UI design but supported) to be processed sequentially
#if DEBUG
                count++;
                Debug.Assert(count <= 999, "Infinite loop trying to find controls which can ProcessMnemonic()!!!");
#endif
                ctl = GetNextControl(ctl, true);

                if (ctl is not null)
                {
                    // Processing the mnemonic can change the value of CanProcessMnemonic.
                    if (ctl.ProcessMnemonic(charCode))
                    {
                        processed = true;
                        break;
                    }
                }
                else
                {
                    if (wrapped)
                    {
                        // This avoids infinite loops
                        break;
                    }

                    wrapped = true;
                }
            }
            while (ctl != start);
        }
        finally
        {
            _state[s_stateProcessingMnemonic] = false;
        }

        return processed;
    }

    /// <summary>
    ///  Selects the next available control and makes it the active control.
    /// </summary>
    protected virtual bool ProcessTabKey(bool forward)
    {
        return SelectNextControl(_activeControl, forward, tabStopOnly: true, nested: true, wrap: false);
    }

    private static ScrollableControl? FindScrollableParent(Control ctl)
    {
        Control? current = ctl.ParentInternal;
        while (current is not null and not ScrollableControl)
        {
            current = current.ParentInternal;
        }

        return (ScrollableControl?)current;
    }

    private void ScrollActiveControlIntoView()
    {
        Control? last = _activeControl;
        if (last is not null)
        {
            ScrollableControl? scrollParent = FindScrollableParent(last);

            while (scrollParent is not null)
            {
                scrollParent.ScrollControlIntoView(_activeControl);
                scrollParent = FindScrollableParent(scrollParent);
            }
        }
    }

    protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew)
    {
        if (deviceDpiNew == deviceDpiOld)
        {
            return;
        }

        base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);

        // Check if font is inherited from parent and is not being scaled by Parent (e.g. WinForms designer
        // in Visual Studio). In this case we need to scale Control explicitly with respect to new scaled Font.
        if (TryGetExplicitlySetFont(out _))
        {
            return;
        }

        using (new LayoutTransaction(ParentInternal, this, PropertyNames.Font))
        {
            OnFontChanged(EventArgs.Empty);
        }
    }

    internal void ScaleContainerForDpi(int deviceDpiNew, int deviceDpiOld, Rectangle suggestedRectangle)
    {
        CommonProperties.xClearAllPreferredSizeCaches(this);
        SuspendAllLayout(this);
        try
        {
            if (AppContextSwitches.ScaleTopLevelFormMinMaxSizeForDpi)
            {
                // AutoscaleFactor is not updated until after the OnFontChanged event is raised. Hence, computing
                // factor based on the change in bounds of the Form, which aligns with AutoscaleFactor for both
                // AutoscaleMode is Font and/or Dpi. Especially after adding support for non-linear Form size in PMv2.
                float xScaleFactor = (float)suggestedRectangle.Width / Width;
                float yScaleFactor = (float)suggestedRectangle.Height / Height;
                ScaleMinMaxSize(xScaleFactor, yScaleFactor, updateContainerSize: false);
            }

            // If this container is a top-level window, we would receive WM_DPICHANGED message that
            // has SuggestedRectangle for the control. We are forced to use this in such cases to
            // make the control placed in right location with respect to the new monitor that triggered
            // WM_DPICHANGED event. Failing to apply SuggestedRectangle will result in a circular WM_DPICHANGED
            // events on the control.

            // Bounds are being scaled for the top-level window via SuggestedRectangle. We would need to skip scaling of
            // this control further by the 'OnFontChanged' event.
            _isScaledByDpiChangedEvent = true;

            Font fontForDpi = GetScaledFont(Font, deviceDpiNew, deviceDpiOld);
            ScaledControlFont = fontForDpi;
            if (IsFontSet())
            {
                SetScaledFont(fontForDpi);
            }
            else
            {
                using (new LayoutTransaction(ParentInternal, this, PropertyNames.Font))
                {
                    OnFontChanged(EventArgs.Empty);
                }
            }

            if (IsHandleCreated)
            {
                PInvoke.SetWindowPos(
                    this,
                    HWND.HWND_TOP,
                    suggestedRectangle.X,
                    suggestedRectangle.Y,
                    suggestedRectangle.Width,
                    suggestedRectangle.Height,
                    SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
            }
        }
        finally
        {
            // We want to perform layout for dpi-changed high Dpi improvements - setting the second parameter to 'true'
            ResumeAllLayout(this, true);
            _isScaledByDpiChangedEvent = false;
        }
    }

    protected override void Select(bool directed, bool forward)
    {
        bool correctParentActiveControl = true;
        if (ParentInternal is not null)
        {
            IContainerControl? c = ParentInternal.GetContainerControl();
            if (c is not null)
            {
                c.ActiveControl = this;
                correctParentActiveControl = (c.ActiveControl == this);
            }
        }

        if (directed && correctParentActiveControl)
        {
            SelectNextControl(null, forward, tabStopOnly: true, nested: true, wrap: false);
        }
    }

    /// <summary>
    ///  Implements ActiveControl property setter.
    /// </summary>
    internal void SetActiveControl(Control? value)
    {
        if (_activeControl == value && (value is null || value.Focused))
        {
            return;
        }

        if (value is not null && !Contains(value))
        {
            throw new ArgumentException(SR.CannotActivateControl, nameof(value));
        }

        bool result;
        ContainerControl? containerControl = this;

        if (value is not null)
        {
            containerControl = value.ParentInternal?.GetContainerControl() as ContainerControl;
        }

        if (containerControl is not null)
        {
            // Call to the recursive function that corrects the chain of active controls
            result = containerControl.ActivateControl(value, false);
        }
        else
        {
            result = AssignActiveControlInternal(value);
        }

        if (containerControl is not null && result)
        {
            ContainerControl ancestor = this;
            while (ancestor.ParentInternal?.GetContainerControl() is ContainerControl parentContainer)
            {
                ancestor = parentContainer;
            }

            if (ancestor.ContainsFocus
                && (value is null || value is not UserControl userControl || !userControl.HasFocusableChild()))
            {
                containerControl.FocusActiveControlInternal();
            }
        }
    }

    private protected ContainerControl InnerMostActiveContainerControl
    {
        get
        {
            ContainerControl result = this;
            while (result.ActiveControl is ContainerControl control)
            {
                result = control;
            }

            return result;
        }
    }

    private ContainerControl InnerMostFocusedContainerControl
    {
        get
        {
            ContainerControl result = this;
            while (result._focusedControl is ContainerControl control)
            {
                result = control;
            }

            return result;
        }
    }

    /// <summary>
    ///  Updates the default button based on current selection, and the acceptButton property.
    /// </summary>
    protected virtual void UpdateDefaultButton()
    {
    }

    /// <summary>
    ///  Updates the focusedControl variable by walking towards the activeControl variable, firing
    ///  enter and leave events and validation as necessary.
    /// </summary>
    internal void UpdateFocusedControl()
    {
        // Capture the current focusedControl as the unvalidatedControl if we don't have one/are not validating.
        EnsureUnvalidatedControl(_focusedControl);
        Control? pathControl = _focusedControl;

        while (_activeControl != pathControl)
        {
            if (pathControl is null || pathControl.IsDescendant(_activeControl))
            {
                // Heading down. Find next control on path.
                Control? nextControlDown = _activeControl;
                while (true)
                {
                    Control? parent = nextControlDown!.ParentInternal;
                    if (parent == this || parent == pathControl)
                    {
                        break;
                    }

                    nextControlDown = nextControlDown.ParentInternal;
                }

                Control? priorFocusedControl = _focusedControl = pathControl;
                EnterValidation(nextControlDown);
                // If validation changed position, then jump back to the loop.
                if (_focusedControl != priorFocusedControl)
                {
                    pathControl = _focusedControl;
                    continue;
                }

                pathControl = nextControlDown;
                if (NativeWindow.WndProcShouldBeDebuggable)
                {
                    pathControl.OnEnter(EventArgs.Empty);
                }
                else
                {
                    try
                    {
                        pathControl.OnEnter(EventArgs.Empty);
                    }
                    catch (Exception e)
                    {
                        Application.OnThreadException(e);
                    }
                }
            }
            else
            {
                // Heading up.
                ContainerControl innerMostFCC = InnerMostFocusedContainerControl;
                Control? stopControl = null;

                if (innerMostFCC._focusedControl is not null)
                {
                    pathControl = innerMostFCC._focusedControl;
                    stopControl = innerMostFCC;

                    if (innerMostFCC != this)
                    {
                        innerMostFCC._focusedControl = null;
                        if (innerMostFCC.ParentInternal is not (not null and MdiClient))
                        {
                            // Don't reset the active control of a MDIChild that loses the focus
                            innerMostFCC._activeControl = null;
                        }
                    }
                }
                else
                {
                    pathControl = innerMostFCC;
                    // innerMostFCC.ParentInternal can be null when the ActiveControl is deleted.
                    if (innerMostFCC.ParentInternal is not null)
                    {
                        ContainerControl? containerControl = innerMostFCC.ParentInternal.GetContainerControl() as ContainerControl;
                        stopControl = containerControl;
                        if (containerControl is not null && containerControl != this)
                        {
                            containerControl._focusedControl = null;
                            containerControl._activeControl = null;
                        }
                    }
                }

                do
                {
                    Control leaveControl = pathControl;

                    if (pathControl is not null)
                    {
                        pathControl = pathControl.ParentInternal;
                    }

                    if (pathControl == this)
                    {
                        pathControl = null;
                    }

                    if (leaveControl is not null)
                    {
                        if (NativeWindow.WndProcShouldBeDebuggable)
                        {
                            leaveControl.OnLeave(EventArgs.Empty);
                        }
                        else
                        {
                            try
                            {
                                leaveControl.OnLeave(EventArgs.Empty);
                            }
                            catch (Exception e)
                            {
                                Application.OnThreadException(e);
                            }
                        }
                    }
                }
                while (pathControl is not null && pathControl != stopControl && !pathControl.IsDescendant(_activeControl));
            }
        }

#if DEBUG
        if (_activeControl is null
            || (_activeControl?.ParentInternal is not null && !_activeControl.ParentInternal.IsContainerControl))
        {
            Debug.Assert(_activeControl is null || _activeControl.ParentInternal.GetContainerControl() == this);
        }
#endif
        _focusedControl = _activeControl;
        if (_activeControl is not null)
        {
            EnterValidation(_activeControl);
        }
    }

    /// <summary>
    ///  Make sure we have a valid choice of last unvalidated control if at all possible.
    /// </summary>
    private void EnsureUnvalidatedControl(Control? candidate)
    {
        // Don't change the unvalidated control while in the middle of validation (reentrancy)
        if (_state[s_stateValidating])
        {
            return;
        }

        // Don't change the existing unvalidated control
        if (_unvalidatedControl is not null)
        {
            return;
        }

        // No new choice of unvalidated control was specified - leave unvalidated control blank
        if (candidate is null)
        {
            return;
        }

        // Specified control has auto-validation disabled - leave unvalidated control blank
        if (!candidate.ShouldAutoValidate)
        {
            return;
        }

        // Go ahead and make specified control the current unvalidated control for this container
        _unvalidatedControl = candidate;

        // In the case of nested container controls, try to pick the deepest possible unvalidated
        // control. For a container with no unvalidated control, use the active control instead.
        // Stop as soon as we encounter any control that has auto-validation turned off.
        while (_unvalidatedControl is ContainerControl container)
        {
            if (container._unvalidatedControl is not null && container._unvalidatedControl.ShouldAutoValidate)
            {
                _unvalidatedControl = container._unvalidatedControl;
            }
            else if (container._activeControl is not null && container._activeControl.ShouldAutoValidate)
            {
                _unvalidatedControl = container._activeControl;
            }
            else
            {
                break;
            }
        }
    }

    /// <summary>
    ///  Validates the last unvalidated control and its ancestors (up through the ancestor in common
    ///  with enterControl) if enterControl causes validation.
    /// </summary>
    private void EnterValidation(Control enterControl)
    {
        // No unvalidated control to validate - stop now
        if (_unvalidatedControl is null)
        {
            return;
        }

        // Entered control does not trigger validation - stop now
        if (!enterControl.CausesValidation)
        {
            return;
        }

        // Get the effective AutoValidate mode for this control (based on its container control)
        AutoValidate autoValidateMode = GetAutoValidateForControl(_unvalidatedControl);

        // Auto-validate has been turned off in container of unvalidated control - stop now
        if (autoValidateMode == AutoValidate.Disable)
        {
            return;
        }

        // Find common ancestor of entered control and unvalidated control
        Control? commonAncestor = enterControl;
        while (commonAncestor is not null && !commonAncestor.IsDescendant(_unvalidatedControl))
        {
            commonAncestor = commonAncestor.ParentInternal;
        }

        // Should we force focus to stay on same control if there is a validation error?
        bool preventFocusChangeOnError = (autoValidateMode == AutoValidate.EnablePreventFocusChange);

        // Validate control and its ancestors, up to (but not including) the common ancestor
        ValidateThroughAncestor(commonAncestor, preventFocusChangeOnError);
    }

    /// <summary>
    ///  Validates the last unvalidated control and its ancestors up through, but not including the current control.
    ///
    ///  This version always performs validation, regardless of the AutoValidate setting of the control's parent.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This version is intended for user code that wants to force validation, even while auto-validation is
    ///   turned off. When adding any explicit Validate() calls to our code, consider using Validate(true) rather
    ///   than Validate(), so that you will be sensitive to the current auto-validation setting.
    ///  </para>
    /// </remarks>
    public bool Validate() => Validate(checkAutoValidate: false);

    /// <summary>
    ///  Validates the last unvalidated control and its ancestors up through, but not including the current control.
    ///  This version will skip validation if checkAutoValidate is true and the effective AutoValidate setting, as
    ///  determined by the control's parent, is AutoValidate.Disable.
    /// </summary>
    public bool Validate(bool checkAutoValidate)
    {
        return ValidateInternal(checkAutoValidate, out _);
    }

    internal bool ValidateInternal(bool checkAutoValidate, out bool validatedControlAllowsFocusChange)
    {
        validatedControlAllowsFocusChange = false;

        if (AutoValidate == AutoValidate.EnablePreventFocusChange ||
            (_activeControl is not null && _activeControl.CausesValidation))
        {
            if (_unvalidatedControl is null)
            {
                if (_focusedControl is ContainerControl control && _focusedControl.CausesValidation)
                {
                    ContainerControl c = control;
                    if (!c.ValidateInternal(checkAutoValidate, out validatedControlAllowsFocusChange))
                    {
                        return false;
                    }
                }
                else
                {
                    _unvalidatedControl = _focusedControl;
                }
            }

            // Should we force focus to stay on same control if there is a validation error?
            bool preventFocusChangeOnError = true;

            Control? controlToValidate = _unvalidatedControl ?? _focusedControl;

            if (controlToValidate is not null)
            {
                // Get the effective AutoValidate mode for unvalidated control (based on its container control)
                AutoValidate autoValidateMode = GetAutoValidateForControl(controlToValidate);

                // Auto-validate has been turned off in container of unvalidated control - stop now
                if (checkAutoValidate && autoValidateMode == AutoValidate.Disable)
                {
                    return true;
                }

                preventFocusChangeOnError = (autoValidateMode == AutoValidate.EnablePreventFocusChange);
                validatedControlAllowsFocusChange = (autoValidateMode == AutoValidate.EnableAllowFocusChange);
            }

            return ValidateThroughAncestor(null, preventFocusChangeOnError);
        }

        return true;
    }

    /// <summary>
    ///  Validates all selectable child controls in the container, including descendants. This is
    ///  equivalent to calling ValidateChildren(ValidationConstraints.Selectable). See <see cref="ValidationConstraints.Selectable"/>
    ///  for details of exactly which child controls will be validated.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual bool ValidateChildren() => ValidateChildren(ValidationConstraints.Selectable);

    /// <summary>
    ///  Validates all the child controls in the container. Exactly which controls are
    ///  validated and which controls are skipped is determined by <paramref name="validationConstraints"/>.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual bool ValidateChildren(ValidationConstraints validationConstraints)
    {
        if ((int)validationConstraints is < 0x00 or > 0x1F)
        {
            throw new InvalidEnumArgumentException(nameof(validationConstraints), (int)validationConstraints, typeof(ValidationConstraints));
        }

        return !PerformContainerValidation(validationConstraints);
    }

    private bool ValidateThroughAncestor(Control? ancestorControl, bool preventFocusChangeOnError)
    {
        ancestorControl ??= this;

        if (_state[s_stateValidating])
        {
            return false;
        }

        _unvalidatedControl ??= _focusedControl;

        // Return true for a Container Control with no controls to validate.
        if (_unvalidatedControl is null)
        {
            return true;
        }

        if (!ancestorControl.IsDescendant(_unvalidatedControl))
        {
            return false;
        }

        _state[s_stateValidating] = true;
        bool cancel = false;

        Control? currentActiveControl = _activeControl;
        Control? currentValidatingControl = _unvalidatedControl;
        if (currentActiveControl is not null)
        {
            currentActiveControl.ValidationCancelled = false;
            if (currentActiveControl is ContainerControl currentActiveContainerControl)
            {
                currentActiveContainerControl.ResetValidationFlag();
            }
        }

        try
        {
            while (currentValidatingControl is not null && currentValidatingControl != ancestorControl)
            {
                cancel = currentValidatingControl.PerformControlValidation(false);

                if (cancel)
                {
                    break;
                }

                currentValidatingControl = currentValidatingControl.ParentInternal;
            }

            if (cancel && preventFocusChangeOnError)
            {
                if (_unvalidatedControl is null
                    && currentValidatingControl is not null
                    && ancestorControl.IsDescendant(currentValidatingControl))
                {
                    _unvalidatedControl = currentValidatingControl;
                }

                // This bit 'marks' the control that was going to get the focus, so that it will ignore any pending
                // mouse or key events. Otherwise it would still perform its default 'click' action or whatever.
                if (currentActiveControl == _activeControl)
                {
                    if (currentActiveControl is not null)
                    {
                        CancelEventArgs ev = new CancelEventArgs
                        {
                            Cancel = true
                        };

                        currentActiveControl.ValidationCancelled = ev.Cancel;

                        if (currentActiveControl is ContainerControl currentActiveContainerControl)
                        {
                            currentActiveContainerControl._focusedControl?.ValidationCancelled = true;

                            currentActiveContainerControl.ResetActiveAndFocusedControlsRecursive();
                        }
                    }
                }

                // This bit forces the focus to move back to the invalid control
                SetActiveControl(_unvalidatedControl);
            }
        }
        finally
        {
            _unvalidatedControl = null;
            _state[s_stateValidating] = false;
        }

        return !cancel;
    }

    private void ResetValidationFlag()
    {
        // Performance: This is more efficient than using Foreach. Foreach forces the creation of
        // an array subset enum each time we enumerate
        ControlCollection children = Controls;
        int count = children.Count;
        for (int i = 0; i < count; i++)
        {
            children[i].ValidationCancelled = false;
        }
    }

    internal void ResetActiveAndFocusedControlsRecursive()
    {
        if (_activeControl is ContainerControl activeContainerControl)
        {
            activeContainerControl.ResetActiveAndFocusedControlsRecursive();
        }

        _activeControl = null;
        _focusedControl = null;
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeAutoValidate() => _autoValidate != AutoValidate.Inherit;

    /// <summary>
    ///  WM_SETFOCUS handler
    /// </summary>
    private void WmSetFocus(ref Message m)
    {
        if (HostedInWin32DialogManager)
        {
            base.WndProc(ref m);
            return;
        }

        if (ActiveControl is not null)
        {
            WmImeSetFocus();

            // Do not raise GotFocus event since the focus is given to the visible ActiveControl
            if (!ActiveControl.Visible)
            {
                InvokeGotFocus(this, EventArgs.Empty);
            }

            FocusActiveControlInternal();
            return;
        }

        // Try to set the focus to the parent container if there is one.
        if (ParentInternal?.GetContainerControl() is IContainerControl container)
        {
            if (!(container is ContainerControl knowncontainer
                ? knowncontainer.ActivateControl(this)
                : container.ActivateControl(this)))
            {
                return;
            }
        }

        base.WndProc(ref m);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void WndProc(ref Message m)
    {
        switch (m.MsgInternal)
        {
            case PInvokeCore.WM_SETFOCUS:
                WmSetFocus(ref m);
                break;
            default:
                base.WndProc(ref m);
                break;
        }
    }
}
