// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

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

namespace System.Windows.Forms
{
    [ToolboxItem(false)]
    public class ToolStripPanelRow : Component, IArrangedElement
    {
        private Rectangle _bounds = Rectangle.Empty;
        private BitVector32 _state;
        private int _suspendCount;
        private ToolStripPanelRowManager _rowManager;

        private const int MinAllowedWidth = 50;
        private readonly int _minAllowedWidth = MinAllowedWidth;

        private static readonly int s_stateVisible = BitVector32.CreateMask();
        private static readonly int s_stateDisposing = BitVector32.CreateMask(s_stateVisible);
        private static readonly int s_stateLocked = BitVector32.CreateMask(s_stateDisposing);
        private static readonly int s_stateInitialized = BitVector32.CreateMask(s_stateLocked);
        private static readonly int s_stateCachedBoundsMode = BitVector32.CreateMask(s_stateInitialized);
        private static readonly int s_stateInLayout = BitVector32.CreateMask(s_stateCachedBoundsMode);

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

#if DEBUG
        internal static TraceSwitch s_toolStripPanelRowCreationDebug = new TraceSwitch("ToolStripPanelRowCreationDebug", "Debug code for rafting row creation");
#else
        internal static TraceSwitch s_toolStripPanelRowCreationDebug;
#endif

#if DEBUG
        private static int s_rowCreationCount;
        private readonly int _thisRowID;
#endif

        public ToolStripPanelRow(ToolStripPanel parent) : this(parent, true)
        {
        }

        internal ToolStripPanelRow(ToolStripPanel parent, bool visible)
        {
#if DEBUG
            _thisRowID = ++s_rowCreationCount;
#endif
            if (DpiHelper.IsScalingRequirementMet)
            {
                _minAllowedWidth = DpiHelper.LogicalToDeviceUnitsX(MinAllowedWidth);
            }

            this.ToolStripPanel = parent;
            _state[s_stateVisible] = visible;
            _state[s_stateDisposing | s_stateLocked | s_stateInitialized] = false;

            Debug.WriteLineIf(s_toolStripPanelRowCreationDebug.TraceVerbose, "Created new ToolStripPanelRow");

            using (LayoutTransaction lt = new LayoutTransaction(parent, this, null))
            {
                Margin = DefaultMargin;
                CommonProperties.SetAutoSize(this, true);
            }
        }

        public Rectangle Bounds
        {
            get
            {
                return _bounds;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [SRDescription(nameof(SR.ControlControlsDescr))]
        public Control[] Controls
        {
            get
            {
                Control[] controls = new Control[ControlsInternal.Count];
                ControlsInternal.CopyTo(controls, 0);
                return controls;
            }
        }

        /// <summary>
        ///  Collection of child controls.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [SRDescription(nameof(SR.ControlControlsDescr))]
        internal ToolStripPanelRowControlCollection ControlsInternal
        {
            get
            {
                ToolStripPanelRowControlCollection controlsCollection = (ToolStripPanelRowControlCollection)Properties.GetObject(s_propControlsCollection);

                if (controlsCollection is null)
                {
                    controlsCollection = CreateControlsInstance();
                    Properties.SetObject(s_propControlsCollection, controlsCollection);
                }

                return controlsCollection;
            }
        }

        internal ArrangedElementCollection Cells
        {
            get
            {
                return ControlsInternal.Cells;
            }
        }

        internal bool CachedBoundsMode
        {
            get
            {
                return _state[s_stateCachedBoundsMode];
            }
            set
            {
                _state[s_stateCachedBoundsMode] = value;
            }
        }

        private ToolStripPanelRowManager RowManager
        {
            get
            {
                if (_rowManager is null)
                {
                    _rowManager = (Orientation == Orientation.Horizontal) ? new HorizontalRowManager(this) as ToolStripPanelRowManager
                                                                         : new VerticalRowManager(this) as ToolStripPanelRowManager;
                    Initialized = true;
                }

                return _rowManager;
            }
        }

        protected virtual Padding DefaultMargin
        {
            get
            {
                ToolStripPanelCell cell = RowManager.GetNextVisibleCell(0, /*forward*/true);
                if (cell != null && cell.DraggedControl != null)
                {
                    if (cell.DraggedControl.Stretch)
                    {
                        Padding padding = ToolStripPanel.RowMargin;
                        // clear out the padding.
                        if (Orientation == Orientation.Horizontal)
                        {
                            padding.Left = 0;
                            padding.Right = 0;
                        }
                        else
                        {
                            padding.Top = 0;
                            padding.Bottom = 0;
                        }
                        return padding;
                    }
                }
                return ToolStripPanel.RowMargin;
            }
        }

        protected virtual Padding DefaultPadding
        {
            get { return Padding.Empty; }
        }

        public Rectangle DisplayRectangle
        {
            get
            {
                return RowManager.DisplayRectangle;
            }
        }

        public LayoutEngine LayoutEngine
        {
            get
            {
                return FlowLayout.Instance;
            }
        }

        internal bool Locked
        {
            get
            {
                return _state[s_stateLocked];
            }
        }

        private bool Initialized
        {
            get
            {
                return _state[s_stateInitialized];
            }
            set
            {
                _state[s_stateInitialized] = value;
            }
        }

        public Padding Margin
        {
            get { return CommonProperties.GetMargin(this); }
            set { if (Margin != value)
                {
                    CommonProperties.SetMargin(this, value);
                }
            }
        }

        public virtual Padding Padding
        {
            get { return CommonProperties.GetPadding(this, DefaultPadding); }
            set
            {
                if (Padding != value)
                {
                    CommonProperties.SetPadding(this, value);
                }
            }
        }

        internal Control ParentInternal
        {
            get
            {
                return ToolStripPanel;
            }
        }

        /// <summary>
        ///  Retrieves our internal property storage object. If you have a property
        ///  whose value is not always set, you should store it in here to save
        ///  space.
        /// </summary>
        internal PropertyStore Properties { get; } = new PropertyStore();

        public ToolStripPanel ToolStripPanel { get; }

        internal bool Visible
        {
            get
            {
                return _state[s_stateVisible];
            }
        }

        public Orientation Orientation
        {
            get
            {
                return ToolStripPanel.Orientation;
            }
        }

#if DEBUG
        internal void Debug_PrintRowID()
        {
            Debug.Write(_thisRowID.ToString(CultureInfo.CurrentCulture));
        }
#endif

        /// <summary>
        ///  returns true if there is enough space to "raft" the control
        ///  ow returns false
        /// </summary>
        public bool CanMove(ToolStrip toolStripToDrag)
        {
            return !ToolStripPanel.Locked && !Locked && RowManager.CanMove(toolStripToDrag);
        }

        private ToolStripPanelRowControlCollection CreateControlsInstance()
        {
            return new ToolStripPanelRowControlCollection(this);
        }

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    Debug.WriteLineIf(s_toolStripPanelRowCreationDebug.TraceVerbose, "Disposed ToolStripPanelRow");
                    _state[s_stateDisposing] = true;
                    ControlsInternal.Clear();
                }
            }
            finally
            {
                _state[s_stateDisposing] = false;
                base.Dispose(disposing);
            }
        }

        protected internal virtual void OnControlAdded(Control control, int index)
        {
            // if previously added - remove.

            if (control is ISupportToolStripPanel controlToBeDragged)
            {
                controlToBeDragged.ToolStripPanelRow = this;
            }
            RowManager.OnControlAdded(control, index);
        }

        protected internal virtual void OnOrientationChanged()
        {
            _rowManager = null;
        }

        protected void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds)
        {
            ((IArrangedElement)this).PerformLayout((IArrangedElement)this, PropertyNames.Size);

            RowManager.OnBoundsChanged(oldBounds, newBounds);
        }

        protected internal virtual void OnControlRemoved(Control control, int index)
        {
            if (!_state[s_stateDisposing])
            {
                SuspendLayout();
                RowManager.OnControlRemoved(control, index);

                // if previously added - remove.

                if (control is ISupportToolStripPanel controlToBeDragged && controlToBeDragged.ToolStripPanelRow == this)
                {
                    controlToBeDragged.ToolStripPanelRow = null;
                }

                ResumeLayout(true);
                if (ControlsInternal.Count <= 0)
                {
                    ToolStripPanel.RowsInternal.Remove(this);
                    Dispose();
                }
            }
        }

        internal Size GetMinimumSize(ToolStrip toolStrip)
        {
            if (toolStrip.MinimumSize == Size.Empty)
            {
                return new Size(_minAllowedWidth, _minAllowedWidth);
            }
            else
            {
                return toolStrip.MinimumSize;
            }
        }

        private void ApplyCachedBounds()
        {
            for (int i = 0; i < Cells.Count; i++)
            {
                IArrangedElement element = Cells[i] as IArrangedElement;
                if (element.ParticipatesInLayout)
                {
                    ToolStripPanelCell cell = element as ToolStripPanelCell;
                    element.SetBounds(cell.CachedBounds, BoundsSpecified.None);
                    //                    Debug.Assert( cell.Control is null || cell.CachedBounds.Location == cell.Control.Bounds.Location, "CachedBounds out of sync with bounds!");
                }
            }
        }

        protected virtual void OnLayout(LayoutEventArgs e)
        {
            if (Initialized && !_state[s_stateInLayout])
            {
                _state[s_stateInLayout] = true;
                try
                {
                    Margin = DefaultMargin;
                    CachedBoundsMode = true;
                    try
                    {
                        // dont layout in the constructor that's just tacky.
                        bool parentNeedsLayout = LayoutEngine.Layout(this, e);
                    }
                    finally
                    {
                        CachedBoundsMode = false;
                    }

                    ToolStripPanelCell cell = RowManager.GetNextVisibleCell(Cells.Count - 1, /*forward*/false);
                    if (cell is null)
                    {
                        ApplyCachedBounds();
                    }
                    else if (Orientation == Orientation.Horizontal)
                    {
                        OnLayoutHorizontalPostFix();
                    }
                    else
                    {
                        OnLayoutVerticalPostFix();
                    }
                }
                finally
                {
                    _state[s_stateInLayout] = false;
                }
            }
        }

        private void OnLayoutHorizontalPostFix()
        {
            ToolStripPanelCell cell = RowManager.GetNextVisibleCell(Cells.Count - 1, /*forward*/false);
            if (cell is null)
            {
                ApplyCachedBounds();
                return;
            }
            // figure out how much space we actually need to free.
            int spaceToFree = cell.CachedBounds.Right - RowManager.DisplayRectangle.Right;

            if (spaceToFree <= 0)
            {
                // we're all good. Just apply the cached bounds.
                ApplyCachedBounds();
                return;
            }
            // STEP 1 remove empty space in the row.

            // since layout sisuspended, we'll need to watch changes to the margin
            // as a result of calling FreeSpaceFromRow.
            int[] margins = new int[Cells.Count];
            for (int i = 0; i < Cells.Count; i++)
            {
                ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                margins[i] = c.Margin.Left;
            }

            spaceToFree -= RowManager.FreeSpaceFromRow(spaceToFree);

            // now apply those changes to the cached bounds.
            for (int i = 0; i < Cells.Count; i++)
            {
                ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                Rectangle cachedBounds = c.CachedBounds;
                cachedBounds.X -= Math.Max(0, margins[i] - c.Margin.Left);
                c.CachedBounds = cachedBounds;
            }

            if (spaceToFree <= 0)
            {
                ApplyCachedBounds();
                return;
            }

            // STEP 2 change the size of the remaing ToolStrips from Right to Left.
            int[] cellOffsets = null;
            for (int i = Cells.Count - 1; i >= 0; i--)
            {
                ToolStripPanelCell currentCell = Cells[i] as ToolStripPanelCell;
                if (currentCell.Visible)
                {
                    Size minSize = GetMinimumSize(currentCell.Control as ToolStrip);
                    Rectangle cachedBounds = currentCell.CachedBounds;

                    // found some space to free.
                    if (cachedBounds.Width > minSize.Width)
                    {
                        spaceToFree -= (cachedBounds.Width - minSize.Width);
                        // make sure we dont take more space than we need - if spaceToFree is less than 0, add back in.
                        cachedBounds.Width = (spaceToFree < 0) ? minSize.Width + -spaceToFree : minSize.Width;

                        // we're not reperforming a layout, so we need to adjust the next cell
                        for (int j = i + 1; j < Cells.Count; j++)
                        {
                            if (cellOffsets is null)
                            {
                                cellOffsets = new int[Cells.Count];
                            }
                            cellOffsets[j] += Math.Max(0, currentCell.CachedBounds.Width - cachedBounds.Width);
                        }
                        currentCell.CachedBounds = cachedBounds;
                    }
                }
                if (spaceToFree <= 0)
                {
                    break;
                }
            }

            // fixup for items before it shrinking.
            if (cellOffsets != null)
            {
                for (int i = 0; i < Cells.Count; i++)
                {
                    ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                    Rectangle cachedBounds = c.CachedBounds;
                    cachedBounds.X -= cellOffsets[i];
                    c.CachedBounds = cachedBounds;
                }
            }

            ApplyCachedBounds();
        }

        private void OnLayoutVerticalPostFix()
        {
            ToolStripPanelCell cell = RowManager.GetNextVisibleCell(Cells.Count - 1, /*forward*/false);
            // figure out how much space we actually need to free.
            int spaceToFree = cell.CachedBounds.Bottom - RowManager.DisplayRectangle.Bottom;

            if (spaceToFree <= 0)
            {
                // we're all good. Just apply the cached bounds.
                ApplyCachedBounds();
                return;
            }
            // STEP 1 remove empty space in the row.

            // since layout sisuspended, we'll need to watch changes to the margin
            // as a result of calling FreeSpaceFromRow.
            int[] margins = new int[Cells.Count];
            for (int i = 0; i < Cells.Count; i++)
            {
                ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                margins[i] = c.Margin.Top;
            }

            spaceToFree -= RowManager.FreeSpaceFromRow(spaceToFree);

            // now apply those changes to the cached bounds.
            for (int i = 0; i < Cells.Count; i++)
            {
                ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                Rectangle cachedBounds = c.CachedBounds;
                cachedBounds.X = Math.Max(0, cachedBounds.X - margins[i] - c.Margin.Top);
                c.CachedBounds = cachedBounds;
            }

            if (spaceToFree <= 0)
            {
                ApplyCachedBounds();
                return;
            }

            // STEP 2 change the size of the remaing ToolStrips from Bottom to Top.
            int[] cellOffsets = null;
            for (int i = Cells.Count - 1; i >= 0; i--)
            {
                ToolStripPanelCell currentCell = Cells[i] as ToolStripPanelCell;
                if (currentCell.Visible)
                {
                    Size minSize = GetMinimumSize(currentCell.Control as ToolStrip);
                    Rectangle cachedBounds = currentCell.CachedBounds;

                    // found some space to free.
                    if (cachedBounds.Height > minSize.Height)
                    {
                        spaceToFree -= (cachedBounds.Height - minSize.Height);
                        // make sure we dont take more space than we need - if spaceToFree is less than 0, add back in.
                        cachedBounds.Height = (spaceToFree < 0) ? minSize.Height + -spaceToFree : minSize.Height;

                        // we're not reperforming a layout, so we need to adjust the next cell
                        for (int j = i + 1; j < Cells.Count; j++)
                        {
                            if (cellOffsets is null)
                            {
                                cellOffsets = new int[Cells.Count];
                            }
                            cellOffsets[j] += Math.Max(0, currentCell.CachedBounds.Height - cachedBounds.Height);
                        }
                        currentCell.CachedBounds = cachedBounds;
                    }
                }
                if (spaceToFree <= 0)
                {
                    break;
                }
            }

            // fixup for items before it shrinking.
            if (cellOffsets != null)
            {
                for (int i = 0; i < Cells.Count; i++)
                {
                    ToolStripPanelCell c = Cells[i] as ToolStripPanelCell;
                    Rectangle cachedBounds = c.CachedBounds;
                    cachedBounds.Y -= cellOffsets[i];
                    c.CachedBounds = cachedBounds;
                }
            }

            ApplyCachedBounds();
        }

        private void SetBounds(Rectangle bounds)
        {
            if (bounds != this._bounds)
            {
                Rectangle oldBounds = this._bounds;

                this._bounds = bounds;
                OnBoundsChanged(oldBounds, bounds);
            }
        }

        private void SuspendLayout()
        {
            _suspendCount++;
        }

        private void ResumeLayout(bool performLayout)
        {
            _suspendCount--;
            if (performLayout)
            {
                ((IArrangedElement)this).PerformLayout(this, null);
            }
        }

        ArrangedElementCollection IArrangedElement.Children
        {
            get
            {
                return Cells;
            }
        }

        /// <summary>
        ///  Should not be exposed as this returns an unexposed type.
        /// </summary>
        IArrangedElement IArrangedElement.Container
        {
            get
            {
                return ToolStripPanel;
            }
        }

        Rectangle IArrangedElement.DisplayRectangle
        {
            get
            {
                Rectangle displayRectangle = Bounds;

                return displayRectangle;
            }
        }

        bool IArrangedElement.ParticipatesInLayout
        {
            get
            {
                return Visible;
            }
        }

        PropertyStore IArrangedElement.Properties
        {
            get
            {
                return Properties;
            }
        }

        Size IArrangedElement.GetPreferredSize(Size constrainingSize)
        {
            Size preferredSize = LayoutEngine.GetPreferredSize(this, constrainingSize - Padding.Size) + Padding.Size;

            if (Orientation == Orientation.Horizontal && ParentInternal != null)
            {
                preferredSize.Width = DisplayRectangle.Width;
            }
            else
            {
                preferredSize.Height = DisplayRectangle.Height;
            }

            return preferredSize;
        }

        // Sets the bounds for an element.
        void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified)
        {
            // in this case the parent is telling us to refresh our bounds - dont
            // call PerformLayout
            SetBounds(bounds);
        }

        void IArrangedElement.PerformLayout(IArrangedElement container, string propertyName)
        {
            if (_suspendCount <= 0)
            {
                OnLayout(new LayoutEventArgs(container, propertyName));
            }
        }

        #region MouseStuff

#if DEBUG
        internal static readonly TraceSwitch ToolStripPanelMouseDebug = new TraceSwitch("ToolStripPanelMouse", "Debug ToolStrip WM_MOUSEACTIVATE code");
#else
        internal static readonly TraceSwitch ToolStripPanelMouseDebug;
#endif

        internal Rectangle DragBounds
        {
            get
            {
                return RowManager.DragBounds;
            }
        }

        internal void MoveControl(ToolStrip movingControl, Point startClientLocation, Point endClientLocation)
        {
            RowManager.MoveControl(movingControl, startClientLocation, endClientLocation);
        }

        //

        internal void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag)
        {
            RowManager.JoinRow(toolStripToDrag, locationToDrag);
        }

        internal void LeaveRow(ToolStrip toolStripToDrag)
        {
            RowManager.LeaveRow(toolStripToDrag);
            if (ControlsInternal.Count == 0)
            {
                ToolStripPanel.RowsInternal.Remove(this);
                Dispose();
            }
        }

        #endregion

        private abstract class ToolStripPanelRowManager
        {
            private FlowLayoutSettings _flowLayoutSettings;

            public ToolStripPanelRowManager(ToolStripPanelRow owner)
            {
                Row = owner;
            }

            public virtual bool CanMove(ToolStrip toolStripToDrag)
            {
                if (toolStripToDrag is ISupportToolStripPanel raftingControl)
                {
                    if (raftingControl.Stretch)
                    {
                        Debug.WriteLineIf(ToolStripPanelRow.s_toolStripPanelRowCreationDebug.TraceVerbose, "TSP RM CanMove returns false - the item moving is stretched.");
                        return false;
                    }
                }
                foreach (Control c in Row.ControlsInternal)
                {
                    raftingControl = c as ISupportToolStripPanel;
                    if (raftingControl != null)
                    {
                        if (raftingControl.Stretch)
                        {
                            Debug.WriteLineIf(ToolStripPanelRow.s_toolStripPanelRowCreationDebug.TraceVerbose, "TSP RM CanMove returns false - the row already contains a stretched item.");
                            return false;
                        }
                    }
                }
                return true;
            }

            public virtual Rectangle DragBounds
            {
                get { return Rectangle.Empty; }
            }

            public virtual Rectangle DisplayRectangle
            {
                get { return Rectangle.Empty; }
            }

            public ToolStripPanel ToolStripPanel
            {
                get { return Row.ToolStripPanel; }
            }

            public ToolStripPanelRow Row { get; }

            public FlowLayoutSettings FlowLayoutSettings
            {
                get
                {
                    if (_flowLayoutSettings is null)
                    {
                        _flowLayoutSettings = new FlowLayoutSettings(Row);
                    }

                    return _flowLayoutSettings;
                }
            }

            protected internal virtual int FreeSpaceFromRow(int spaceToFree)
            {
                return 0;
            }

            protected virtual int Grow(int index, int growBy)
            {
                int freedSpace = 0;
                if (index >= 0 && index < Row.ControlsInternal.Count - 1)
                {
                    ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index];
                    if (cell.Visible)
                    {
                        freedSpace = cell.Grow(growBy);
                    }
                }
                return freedSpace;
            }

            public ToolStripPanelCell GetNextVisibleCell(int index, bool forward)
            {
                if (forward)
                {
                    for (int i = index; i < Row.Cells.Count; i++)
                    {
                        ToolStripPanelCell cell = Row.Cells[i] as ToolStripPanelCell;
                        if ((cell.Visible || (Row.ToolStripPanel.Visible && cell.ControlInDesignMode)) && cell.ToolStripPanelRow == Row)
                        {
                            return cell;
                        }
                    }
                }
                else
                {
                    for (int i = index; i >= 0; i--)
                    {
                        ToolStripPanelCell cell = Row.Cells[i] as ToolStripPanelCell;
                        if ((cell.Visible || (Row.ToolStripPanel.Visible && cell.ControlInDesignMode)) && cell.ToolStripPanelRow == Row)
                        {
                            return cell;
                        }
                    }
                }
                return null;
            }

            /// <summary>
            ///  grows all controls after the index to be their preferred size.
            ///  reports back how much space was used.
            /// </summary>
            protected virtual int GrowControlsAfter(int index, int growBy)
            {
                if (growBy < 0)
                {
                    Debug.Fail("why was a negative number given to growControlsAfter?");
                    return 0;
                }

                int spaceToFree = growBy;

                for (int i = index + 1; i < Row.ControlsInternal.Count; i++)
                {
                    // grow the n+1 item first if it was previously shrunk.
                    int freedSpace = Grow(i, spaceToFree);

                    if (freedSpace >= 0)
                    {
                        spaceToFree -= freedSpace;
                        if (spaceToFree <= 0)
                        {
                            return growBy;
                        }
                    }
                }

                return growBy - spaceToFree;
            }

            /// <summary>
            ///  grows all controls before the index to be their preferred size.
            ///  reports back how much space was used.
            /// </summary>
            protected virtual int GrowControlsBefore(int index, int growBy)
            {
                if (growBy < 0)
                {
                    Debug.Fail("why was a negative number given to growControlsAfter?");
                    return 0;
                }

                int spaceToFree = growBy;

                // grow the n-1 item first if it was previously shrunk.
                for (int i = index - 1; i >= 0; i--)
                {
                    spaceToFree -= Grow(i, spaceToFree);
                    if (spaceToFree <= 0)
                    {
                        return growBy; // we've already gotten all the free space.
                    }
                }

                return growBy - spaceToFree;
            }

            public virtual void MoveControl(ToolStrip movingControl, Point startClientLocation, Point endClientLocation)
            {
                //     ToolStripPanel.Join(movingControl, endScreenLocation);
            }
            public virtual void LeaveRow(ToolStrip toolStripToDrag)
            {
            }

            public virtual void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag)
            {
            }

            protected internal virtual void OnControlAdded(Control c, int index)
            {
            }

            protected internal virtual void OnControlRemoved(Control c, int index)
            {
            }

            protected internal virtual void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds)
            {
            }
        }

        private class HorizontalRowManager : ToolStripPanelRowManager
        {
            public HorizontalRowManager(ToolStripPanelRow owner) : base(owner)
            {
                owner.SuspendLayout();
                FlowLayoutSettings.WrapContents = false;
                FlowLayoutSettings.FlowDirection = FlowDirection.LeftToRight;
                owner.ResumeLayout(false);
            }

            public override Rectangle DisplayRectangle
            {
                get
                {
                    Rectangle displayRect = ((IArrangedElement)Row).DisplayRectangle;

                    if (ToolStripPanel != null)
                    {
                        Rectangle raftingDisplayRectangle = ToolStripPanel.DisplayRectangle;

                        if ((!ToolStripPanel.Visible || LayoutUtils.IsZeroWidthOrHeight(raftingDisplayRectangle)) && (ToolStripPanel.ParentInternal != null))
                        {
                            // if were layed out before we're visible we have the wrong display rectangle, so we need to calculate it.
                            displayRect.Width = ToolStripPanel.ParentInternal.DisplayRectangle.Width - (ToolStripPanel.Margin.Horizontal + ToolStripPanel.Padding.Horizontal) - Row.Margin.Horizontal;
                        }
                        else
                        {
                            displayRect.Width = raftingDisplayRectangle.Width - Row.Margin.Horizontal;
                        }
                    }

                    return displayRect;
                }
            }

            public override Rectangle DragBounds
            {
                get
                {
                    Rectangle dragBounds = Row.Bounds;
                    int index = ToolStripPanel.RowsInternal.IndexOf(Row);

                    if (index > 0)
                    {
                        Rectangle previousRowBounds = ToolStripPanel.RowsInternal[index - 1].Bounds;
                        int y = previousRowBounds.Y + previousRowBounds.Height - (previousRowBounds.Height >> 2);

                        dragBounds.Height += dragBounds.Y - y;
                        dragBounds.Y = y;
                    }

                    if (index < ToolStripPanel.RowsInternal.Count - 1)
                    {
                        Rectangle nextRowBounds = ToolStripPanel.RowsInternal[index + 1].Bounds;

                        dragBounds.Height += (nextRowBounds.Height >> 2) + Row.Margin.Bottom + ToolStripPanel.RowsInternal[index + 1].Margin.Top;
                    }

                    dragBounds.Width += Row.Margin.Horizontal + ToolStripPanel.Padding.Horizontal + 5;
                    dragBounds.X -= Row.Margin.Left + ToolStripPanel.Padding.Left + 4;
                    return dragBounds;
                }
            }

            /// <summary>
            ///  returns true if there is enough space to "raft" the control
            ///  ow returns false
            /// </summary>
            public override bool CanMove(ToolStrip toolStripToDrag)
            {
                if (base.CanMove(toolStripToDrag))
                {
                    Size totalSize = Size.Empty;

                    for (int i = 0; i < Row.ControlsInternal.Count; i++)
                    {
                        totalSize += Row.GetMinimumSize(Row.ControlsInternal[i] as ToolStrip);
                    }

                    totalSize += Row.GetMinimumSize(toolStripToDrag as ToolStrip);
                    return totalSize.Width < DisplayRectangle.Width;
                }
                Debug.WriteLineIf(ToolStripPanelRow.s_toolStripPanelRowCreationDebug.TraceVerbose, "HorizontalRM.CanMove returns false - not enough room");
                return false;
            }

            protected internal override int FreeSpaceFromRow(int spaceToFree)
            {
                int requiredSpace = spaceToFree;
                // take a look at the last guy.  if his right edge exceeds
                // the new bounds, then we should go ahead and push him into view.

                if (spaceToFree > 0)
                {
                    // we should shrink the last guy and then move him.
                    ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count - 1,  /*forward*/false);
                    if (lastCellOnRow is null)
                    {
                        return 0;
                    }
                    Padding cellMargin = lastCellOnRow.Margin;

                    // only check margin.left as we are only concerned with getting right edge of
                    // the toolstrip into view. (space after the fact doesnt count).
                    if (cellMargin.Left >= spaceToFree)
                    {
                        cellMargin.Left -= spaceToFree;
                        cellMargin.Right = 0;
                        spaceToFree = 0;
                    }
                    else
                    {
                        spaceToFree -= lastCellOnRow.Margin.Left;
                        cellMargin.Left = 0;
                        cellMargin.Right = 0;
                    }
                    lastCellOnRow.Margin = cellMargin;

                    // start moving the toolstrips before this guy.
                    spaceToFree -= MoveLeft(Row.Cells.Count - 1, spaceToFree);

                    if (spaceToFree > 0)
                    {
                        spaceToFree -= lastCellOnRow.Shrink(spaceToFree);
                    }
                }
                return requiredSpace - Math.Max(0, spaceToFree);
            }

            public override void MoveControl(ToolStrip movingControl, Point clientStartLocation, Point clientEndLocation)
            {
                if (Row.Locked)
                {
                    return;
                }

                if (DragBounds.Contains(clientEndLocation))
                {
                    int index = Row.ControlsInternal.IndexOf(movingControl);
                    int deltaX = clientEndLocation.X - clientStartLocation.X;

                    if (deltaX < 0)
                    {
                        // moving to the left
                        MoveLeft(index, deltaX * -1);
                    }
                    else
                    {
                        MoveRight(index, deltaX);
                    }
                }
                else
                {
                    base.MoveControl(movingControl, clientStartLocation, clientEndLocation);
                }
            }

            private int MoveLeft(int index, int spaceToFree)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft: " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                int freedSpace = 0;

                Row.SuspendLayout();
                try
                {
                    if (spaceToFree == 0 || index < 0)
                    {
                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Early EXIT - 0 ");
                        return 0;
                    }

                    // remove all margins starting from the index.
                    for (int i = index; i >= 0; i--)
                    {
                        ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }
                        int requiredSpace = spaceToFree - freedSpace;

                        Padding cellMargin = cell.Margin;

                        if (cellMargin.Horizontal >= requiredSpace)
                        {
                            freedSpace += requiredSpace;

                            cellMargin.Left -= requiredSpace;
                            cellMargin.Right = 0;
                            cell.Margin = cellMargin;
                        }
                        else
                        {
                            freedSpace += cell.Margin.Horizontal;
                            cellMargin.Left = 0;
                            cellMargin.Right = 0;
                            cell.Margin = cellMargin;
                        }

                        if (freedSpace >= spaceToFree)
                        {
                            // add the space we freed to the next guy.
                            if (index + 1 < Row.Cells.Count)
                            {
                                cell = GetNextVisibleCell(index + 1, /*forward*/true);
                                if (cell != null)
                                {
                                    cellMargin = cell.Margin;
                                    cellMargin.Left += spaceToFree;
                                    cell.Margin = cellMargin;
                                }
                            }

                            Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                            return spaceToFree;
                        }
                    }
                }
                finally
                {
                    Row.ResumeLayout(true);
                }

                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture));
                return freedSpace;
            }

            private int MoveRight(int index, int spaceToFree)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight: " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                int freedSpace = 0;
                Row.SuspendLayout();
                try
                {
                    if (spaceToFree == 0 || index < 0 || index >= Row.ControlsInternal.Count)
                    {
                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Early EXIT - 0 ");
                        return 0;
                    }

                    ToolStripPanelCell cell;
                    Padding cellMargin;

                    // remove all margins after this point in the index.
                    for (int i = index + 1; i < Row.Cells.Count; i++)
                    {
                        cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }
                        int requiredSpace = spaceToFree - freedSpace;

                        cellMargin = cell.Margin;

                        if (cellMargin.Horizontal >= requiredSpace)
                        {
                            freedSpace += requiredSpace;

                            cellMargin.Left -= requiredSpace;
                            cellMargin.Right = 0;
                            cell.Margin = cellMargin;
                        }
                        else
                        {
                            freedSpace += cell.Margin.Horizontal;
                            cellMargin.Left = 0;
                            cellMargin.Right = 0;
                            cell.Margin = cellMargin;
                        }

                        break;
                    }

                    // add in the space at the end of the row.
                    if (Row.Cells.Count > 0 && (spaceToFree > freedSpace))
                    {
                        ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count - 1, /*forward*/false);
                        if (lastCell != null)
                        {
                            freedSpace += DisplayRectangle.Right - lastCell.Bounds.Right;
                        }
                        else
                        {
                            freedSpace += DisplayRectangle.Width;
                        }
                    }

                    // set the margin of the control that's moving.
                    if (spaceToFree <= freedSpace)
                    {
                        // add the space we freed to the first guy.
                        cell = GetNextVisibleCell(index, /*forward*/true);
                        if (cell is null)
                        {
                            cell = Row.Cells[index] as ToolStripPanelCell;
                        }
                        Debug.Assert(cell != null, "Dont expect cell to be null here, what's going on?");

                        if (cell != null)
                        {
                            cellMargin = cell.Margin;
                            cellMargin.Left += spaceToFree;
                            cell.Margin = cellMargin;
                        }
                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                        return spaceToFree;
                    }

                    // Now start shrinking.
                    for (int i = index + 1; i < Row.Cells.Count; i++)
                    {
                        cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }
                        int requiredSpace = spaceToFree - freedSpace;
                        freedSpace += cell.Shrink(requiredSpace);

                        if (spaceToFree >= freedSpace)
                        {
                            Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered (Shrink): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                            Row.ResumeLayout(true);
                            return spaceToFree;
                        }
                    }

                    if (Row.Cells.Count == 1)
                    {
                        cell = GetNextVisibleCell(index,/*forward*/true);
                        if (cell != null)
                        {
                            cellMargin = cell.Margin;
                            cellMargin.Left += freedSpace;
                            cell.Margin = cellMargin;
                        }
                    }
                }
                finally
                {
                    Row.ResumeLayout(true);
                }

                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture));

                return freedSpace;
            }

            public override void LeaveRow(ToolStrip toolStripToDrag)
            {
                // this code is here to properly add space to the next control when the
                // toolStripToDrag has been removed from the row.
                Row.SuspendLayout();
                int index = Row.ControlsInternal.IndexOf(toolStripToDrag);
                if (index >= 0)
                {
                    if (index < Row.ControlsInternal.Count - 1 /*not the last one in the row*/)
                    {
                        ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index];
                        if (cell.Visible)
                        {
                            int spaceOccupiedByCell = cell.Margin.Horizontal + cell.Bounds.Width;

                            // add the space occupied by the cell to the next one.
                            ToolStripPanelCell nextCell = GetNextVisibleCell(index + 1, /*forward*/true);
                            if (nextCell != null)
                            {
                                Padding nextCellMargin = nextCell.Margin;
                                nextCellMargin.Left += spaceOccupiedByCell;
                                nextCell.Margin = nextCellMargin;
                            }
                        }
                    }
                    // remove the control from the row.
                    ((IList)Row.Cells).RemoveAt(index);
                }
                Row.ResumeLayout(true);
            }

            protected internal override void OnControlAdded(Control control, int index)
            {
            }

            protected internal override void OnControlRemoved(Control control, int index)
            {
            }

            public override void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Horizontal JoinRow called ");
                int index;

                if (!Row.ControlsInternal.Contains(toolStripToDrag))
                {
                    Row.SuspendLayout();

                    try
                    {
                        if (Row.ControlsInternal.Count > 0)
                        {
                            // walk through the columns and determine which column you want to insert into.
                            for (index = 0; index < Row.Cells.Count; index++)
                            {
                                ToolStripPanelCell cell = Row.Cells[index] as ToolStripPanelCell;
                                if (!cell.Visible && !cell.ControlInDesignMode)
                                {
                                    continue;
                                }

                                //  [:   ]  [: x  ]
                                if (Row.Cells[index].Bounds.Contains(locationToDrag))
                                {
                                    break;
                                }

                                // take into account the following scenarios
                                //  [:   ]  x [:   ]
                                // x [:  ]    [:   ]
                                if (Row.Cells[index].Bounds.X >= locationToDrag.X)
                                {
                                    break;
                                }
                            }

                            Control controlToPushAside = Row.ControlsInternal[index];
                            // Plop the new control in the midst of the row in question.
                            if (index < Row.ControlsInternal.Count)
                            {
                                Row.ControlsInternal.Insert(index, toolStripToDrag);
                            }
                            else
                            {
                                Row.ControlsInternal.Add(toolStripToDrag);
                            }

                            // since layout is suspended the control may not be set to its preferred size yet
                            int controlToDragWidth = (toolStripToDrag.AutoSize) ? toolStripToDrag.PreferredSize.Width : toolStripToDrag.Width;

                            //
                            // now make it look like it belongs in the row.
                            //
                            // PUSH the controls after it to the right

                            int requiredSpace = controlToDragWidth;
                            if (index == 0)
                            {
                                // make sure we account for the left side
                                requiredSpace += locationToDrag.X;
                            }
                            int freedSpace = 0;

                            if (index < Row.ControlsInternal.Count - 1)
                            {
                                ToolStripPanelCell nextCell = (ToolStripPanelCell)Row.Cells[index + 1];
                                Padding nextCellMargin = nextCell.Margin;

                                // if we've already got the empty space
                                // (available to us via the margin) use that.
                                if (nextCellMargin.Left > requiredSpace)
                                {
                                    nextCellMargin.Left -= requiredSpace;
                                    nextCell.Margin = nextCellMargin;
                                    freedSpace = requiredSpace;
                                }
                                else
                                {
                                    // otherwise we've got to
                                    // push all controls after this point to the right
                                    // this dumps the extra stuff into the margin of index+1
                                    freedSpace = MoveRight(index + 1, requiredSpace - freedSpace);

                                    // refetch the margin for "index+1" and remove the freed space
                                    // from it - we want to actually put this to use on the control
                                    // before this one - we're making room for the control at
                                    // position "index"
                                    if (freedSpace > 0)
                                    {
                                        nextCellMargin = nextCell.Margin;
                                        nextCellMargin.Left = Math.Max(0, nextCellMargin.Left - freedSpace);
                                        nextCell.Margin = nextCellMargin;
                                    }
                                }
                            }
                            else
                            {
                                // we're adding to the end.
                                ToolStripPanelCell nextCell = GetNextVisibleCell(Row.Cells.Count - 2,  /*forward*/false);
                                ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count - 1,  /*forward*/false);

                                // count the stuff at the end of the row as freed space
                                if (nextCell != null && lastCell != null)
                                {
                                    Padding lastCellMargin = lastCell.Margin;
                                    lastCellMargin.Left = Math.Max(0, locationToDrag.X - nextCell.Bounds.Right);
                                    lastCell.Margin = lastCellMargin;
                                    freedSpace = requiredSpace;
                                }
                            }

                            // If we still need more space, then...
                            // PUSH the controls before it to the left
                            if (freedSpace < requiredSpace && index > 0)
                            {
                                freedSpace = MoveLeft(index - 1, requiredSpace - freedSpace);
                            }

                            if (index == 0)
                            {
                                // if the index is zero and there were controls in the row
                                // we need to take care of pushing over the new cell.
                                if (freedSpace - controlToDragWidth > 0)
                                {
                                    ToolStripPanelCell newCell = Row.Cells[index] as ToolStripPanelCell;
                                    Padding newCellMargin = newCell.Margin;
                                    newCellMargin.Left = freedSpace - controlToDragWidth;
                                    newCell.Margin = newCellMargin;
                                }
                            }
                        }
                        else
                        {
                            // we're adding to the beginning.
                            Row.ControlsInternal.Add(toolStripToDrag);

#if DEBUG
                            ISupportToolStripPanel ctg = toolStripToDrag as ISupportToolStripPanel;
                            ToolStripPanelRow newPanelRow = ctg.ToolStripPanelRow;
                            Debug.Assert(newPanelRow == Row, "we should now be in the new panel row.");
#endif
                            if (Row.Cells.Count > 0 || toolStripToDrag.IsInDesignMode)
                            {
                                // we're adding to the beginning.
                                ToolStripPanelCell cell = GetNextVisibleCell(Row.Cells.Count - 1, /*forward*/false);
                                if (cell is null && toolStripToDrag.IsInDesignMode)
                                {
                                    cell = (ToolStripPanelCell)Row.Cells[Row.Cells.Count - 1];
                                }

                                if (cell != null)
                                {
                                    Padding cellMargin = cell.Margin;
                                    cellMargin.Left = Math.Max(0, locationToDrag.X - Row.Margin.Left);
                                    cell.Margin = cellMargin;
                                }
                            }
                        }
                    }
                    finally
                    {
                        Row.ResumeLayout(true);
                    }
                }
            }

            protected internal override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds)
            {
                base.OnBoundsChanged(oldBounds, newBounds);
            }
        }

        private class VerticalRowManager : ToolStripPanelRowManager
        {
            public VerticalRowManager(ToolStripPanelRow owner) : base(owner)
            {
                owner.SuspendLayout();
                FlowLayoutSettings.WrapContents = false;
                FlowLayoutSettings.FlowDirection = FlowDirection.TopDown;
                owner.ResumeLayout(false);
            }

            public override Rectangle DisplayRectangle
            {
                get
                {
                    Rectangle displayRect = ((IArrangedElement)Row).DisplayRectangle;

                    if (ToolStripPanel != null)
                    {
                        Rectangle raftingDisplayRectangle = ToolStripPanel.DisplayRectangle;

                        if ((!ToolStripPanel.Visible || LayoutUtils.IsZeroWidthOrHeight(raftingDisplayRectangle)) && (ToolStripPanel.ParentInternal != null))
                        {
                            // if were layed out before we're visible we have the wrong display rectangle, so we need to calculate it.
                            displayRect.Height = ToolStripPanel.ParentInternal.DisplayRectangle.Height - (ToolStripPanel.Margin.Vertical + ToolStripPanel.Padding.Vertical) - Row.Margin.Vertical;
                        }
                        else
                        {
                            displayRect.Height = raftingDisplayRectangle.Height - Row.Margin.Vertical;
                        }
                    }

                    return displayRect;
                }
            }
            public override Rectangle DragBounds
            {
                get
                {
                    Rectangle dragBounds = Row.Bounds;
                    int index = ToolStripPanel.RowsInternal.IndexOf(Row);

                    if (index > 0)
                    {
                        Rectangle previousRowBounds = ToolStripPanel.RowsInternal[index - 1].Bounds;
                        int x = previousRowBounds.X + previousRowBounds.Width - (previousRowBounds.Width >> 2);

                        dragBounds.Width += dragBounds.X - x;
                        dragBounds.X = x;
                    }

                    if (index < ToolStripPanel.RowsInternal.Count - 1)
                    {
                        Rectangle nextRowBounds = ToolStripPanel.RowsInternal[index + 1].Bounds;

                        dragBounds.Width += (nextRowBounds.Width >> 2) + Row.Margin.Right + ToolStripPanel.RowsInternal[index + 1].Margin.Left;
                    }

                    dragBounds.Height += Row.Margin.Vertical + ToolStripPanel.Padding.Vertical + 5;
                    dragBounds.Y -= Row.Margin.Top + ToolStripPanel.Padding.Top + 4;

                    return dragBounds;
                }
            }

            /// <summary>
            ///  returns true if there is enough space to "raft" the control
            ///  ow returns false
            /// </summary>
            public override bool CanMove(ToolStrip toolStripToDrag)
            {
                if (base.CanMove(toolStripToDrag))
                {
                    Size totalSize = Size.Empty;

                    for (int i = 0; i < Row.ControlsInternal.Count; i++)
                    {
                        totalSize += Row.GetMinimumSize(Row.ControlsInternal[i] as ToolStrip);
                    }

                    totalSize += Row.GetMinimumSize(toolStripToDrag);
                    return totalSize.Height < DisplayRectangle.Height;
                }

                Debug.WriteLineIf(ToolStripPanelRow.s_toolStripPanelRowCreationDebug.TraceVerbose, "VerticalRM.CanMove returns false - not enough room");
                return false;
            }
            protected internal override int FreeSpaceFromRow(int spaceToFree)
            {
                int requiredSpace = spaceToFree;
                // take a look at the last guy.  if his right edge exceeds
                // the new bounds, then we should go ahead and push him into view.

                if (spaceToFree > 0)
                {
                    // we should shrink the last guy and then move him.
                    ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count - 1,  /*forward*/false);
                    if (lastCellOnRow is null)
                    {
                        return 0;
                    }
                    Padding cellMargin = lastCellOnRow.Margin;

                    // only check margin.left as we are only concerned with getting right edge of
                    // the toolstrip into view. (space after the fact doesnt count).
                    if (cellMargin.Top >= spaceToFree)
                    {
                        cellMargin.Top -= spaceToFree;
                        cellMargin.Bottom = 0;
                        spaceToFree = 0;
                    }
                    else
                    {
                        spaceToFree -= lastCellOnRow.Margin.Top;
                        cellMargin.Top = 0;
                        cellMargin.Bottom = 0;
                    }
                    lastCellOnRow.Margin = cellMargin;

                    // start moving the toolstrips before this guy.
                    spaceToFree -= MoveUp(Row.Cells.Count - 1, spaceToFree);

                    if (spaceToFree > 0)
                    {
                        spaceToFree -= lastCellOnRow.Shrink(spaceToFree);
                    }
                }
                return requiredSpace - Math.Max(0, spaceToFree);
            }

            public override void MoveControl(ToolStrip movingControl, Point clientStartLocation, Point clientEndLocation)
            {
                if (Row.Locked)
                {
                    return;
                }
                if (DragBounds.Contains(clientEndLocation))
                {
                    int index = Row.ControlsInternal.IndexOf(movingControl);
                    int deltaY = clientEndLocation.Y - clientStartLocation.Y;

                    if (deltaY < 0)
                    {
                        // moving to the left
                        MoveUp(index, deltaY * -1);
                    }
                    else
                    {
                        MoveDown(index, deltaY);
                    }
                }
                else
                {
                    base.MoveControl(movingControl, clientStartLocation, clientEndLocation);
                }
            }

            private int MoveUp(int index, int spaceToFree)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp: " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                int freedSpace = 0;

                Row.SuspendLayout();
                try
                {
                    if (spaceToFree == 0 || index < 0)
                    {
                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp Early EXIT - 0 ");
                        return 0;
                    }

                    // remove all margins starting from the index.
                    for (int i = index; i >= 0; i--)
                    {
                        ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }
                        int requiredSpace = spaceToFree - freedSpace;

                        Padding cellMargin = cell.Margin;

                        if (cellMargin.Vertical >= requiredSpace)
                        {
                            freedSpace += requiredSpace;

                            cellMargin.Top -= requiredSpace;
                            cellMargin.Bottom = 0;
                            cell.Margin = cellMargin;
                        }
                        else
                        {
                            freedSpace += cell.Margin.Vertical;
                            cellMargin.Top = 0;
                            cellMargin.Bottom = 0;
                            cell.Margin = cellMargin;
                        }

                        if (freedSpace >= spaceToFree)
                        {
                            // add the space we freed to the next guy.
                            if (index + 1 < Row.Cells.Count)
                            {
                                cell = GetNextVisibleCell(index + 1, /*forward*/true);
                                if (cell != null)
                                {
                                    cellMargin = cell.Margin;
                                    cellMargin.Top += spaceToFree;
                                    cell.Margin = cellMargin;
                                }
                            }

                            Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                            return spaceToFree;
                        }
                    }
                }
                finally
                {
                    Row.ResumeLayout(true);
                }

                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture));

                return freedSpace;
            }

            private int MoveDown(int index, int spaceToFree)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown: " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                int freedSpace = 0;
                Row.SuspendLayout();
                try
                {
                    if (spaceToFree == 0 || index < 0 || index >= Row.ControlsInternal.Count)
                    {
                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Early EXIT - 0 ");
                        return 0;
                    }

                    ToolStripPanelCell cell;
                    Padding cellMargin;

                    // remove all margins after this point in the index.
                    for (int i = index + 1; i < Row.Cells.Count; i++)
                    {
                        cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }

                        int requiredSpace = spaceToFree - freedSpace;

                        cellMargin = cell.Margin;

                        if (cellMargin.Vertical >= requiredSpace)
                        {
                            freedSpace += requiredSpace;

                            cellMargin.Top -= requiredSpace;
                            cellMargin.Bottom = 0;
                            cell.Margin = cellMargin;
                        }
                        else
                        {
                            freedSpace += cell.Margin.Vertical;
                            cellMargin.Top = 0;
                            cellMargin.Bottom = 0;
                            cell.Margin = cellMargin;
                        }

                        break;
                    }

                    // add in the space at the end of the row.
                    if (Row.Cells.Count > 0 && (spaceToFree > freedSpace))
                    {
                        ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count - 1, /*forward*/false);
                        if (lastCell != null)
                        {
                            freedSpace += DisplayRectangle.Bottom - lastCell.Bounds.Bottom;
                        }
                        else
                        {
                            freedSpace += DisplayRectangle.Height;
                        }
                    }

                    // set the margin of the control that's moving.
                    if (spaceToFree <= freedSpace)
                    {
                        // add the space we freed to the first guy.
                        cell = (ToolStripPanelCell)Row.Cells[index];
                        cellMargin = cell.Margin;
                        cellMargin.Top += spaceToFree;
                        cell.Margin = cellMargin;

                        Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                        return spaceToFree;
                    }

                    // Now start shrinking.
                    for (int i = index + 1; i < Row.Cells.Count; i++)
                    {
                        cell = (ToolStripPanelCell)Row.Cells[i];
                        if (!cell.Visible && !cell.ControlInDesignMode)
                        {
                            continue;
                        }
                        int requiredSpace = spaceToFree - freedSpace;
                        freedSpace += cell.Shrink(requiredSpace);

                        if (spaceToFree >= freedSpace)
                        {
                            Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered (Shrink): " + spaceToFree.ToString(CultureInfo.InvariantCulture));
                            Row.ResumeLayout(true);
                            return spaceToFree;
                        }
                    }

                    if (Row.Cells.Count == 1)
                    {
                        cell = GetNextVisibleCell(index,/*forward*/true);
                        if (cell != null)
                        {
                            cellMargin = cell.Margin;
                            cellMargin.Top += freedSpace;
                            cell.Margin = cellMargin;
                        }
                    }
                }
                finally
                {
                    Row.ResumeLayout(true);
                }

                int recoveredSpace = spaceToFree - freedSpace;
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered Partial (Shrink): " + recoveredSpace.ToString(CultureInfo.InvariantCulture));

                return recoveredSpace;
            }

            protected internal override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds)
            {
                base.OnBoundsChanged(oldBounds, newBounds);

                // if our bounds have changed - we should shove the toolbars up so they're in view.
                if (Row.Cells.Count > 0)
                {
                    // take a look at the last guy.  if his right edge exceeds
                    // the new bounds, then we should go ahead and push him into view.
                    ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count - 1, /*forward=*/false);
                    int spaceToFree = (lastCell != null) ? lastCell.Bounds.Bottom - newBounds.Height : 0;

                    if (spaceToFree > 0)
                    {
                        // we should shrink the last guy and then move him.
                        ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count - 1,  /*forward*/false);

                        Padding cellMargin = lastCellOnRow.Margin;

                        // only check margin.left as we are only concerned with getting bottom edge of
                        // the toolstrip into view. (space after the fact doesnt count).
                        if (cellMargin.Top >= spaceToFree)
                        {
                            cellMargin.Top -= spaceToFree;
                            cellMargin.Bottom = 0;
                            lastCellOnRow.Margin = cellMargin;
                            spaceToFree = 0;
                        }
                        else
                        {
                            spaceToFree -= lastCellOnRow.Margin.Top;
                            cellMargin.Top = 0;
                            cellMargin.Bottom = 0;
                            lastCellOnRow.Margin = cellMargin;
                        }
                        spaceToFree -= lastCellOnRow.Shrink(spaceToFree);
                        // start moving the toolstrips before this guy.
                        MoveUp(Row.Cells.Count - 1, spaceToFree);
                    }
                }
            }

            protected internal override void OnControlRemoved(Control c, int index)
            {
            }

            protected internal override void OnControlAdded(Control control, int index)
            {
            }

            public override void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag)
            {
                Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Vertical JoinRow called ");
                int index;

                if (!Row.ControlsInternal.Contains(toolStripToDrag))
                {
                    Row.SuspendLayout();
                    try
                    {
                        if (Row.ControlsInternal.Count > 0)
                        {
                            // walk through the columns and determine which column you want to insert into.
                            for (index = 0; index < Row.Cells.Count; index++)
                            {
                                ToolStripPanelCell cell = Row.Cells[index] as ToolStripPanelCell;
                                if (!cell.Visible && !cell.ControlInDesignMode)
                                {
                                    continue;
                                }
                                //  [:   ]  [: x  ]
                                if (cell.Bounds.Contains(locationToDrag))
                                {
                                    break;
                                }

                                // take into account the following scenarios
                                //  [:   ]  x [:   ]
                                // x [:  ]    [:   ]
                                if (cell.Bounds.Y >= locationToDrag.Y)
                                {
                                    break;
                                }
                            }

                            Control controlToPushAside = Row.ControlsInternal[index];
                            // Plop the new control in the midst of the row in question.
                            if (index < Row.ControlsInternal.Count)
                            {
                                Row.ControlsInternal.Insert(index, toolStripToDrag);
                            }
                            else
                            {
                                Row.ControlsInternal.Add(toolStripToDrag);
                            }

                            // since layout is suspended the control may not be set to its preferred size yet
                            int controlToDragWidth = (toolStripToDrag.AutoSize) ? toolStripToDrag.PreferredSize.Height : toolStripToDrag.Height;

                            //
                            // now make it look like it belongs in the row.
                            //
                            // PUSH the controls after it to the right

                            int requiredSpace = controlToDragWidth;

                            if (index == 0)
                            {
                                // make sure we account for the left side
                                requiredSpace += locationToDrag.Y;
                            }
                            int freedSpace = 0;

                            if (index < Row.ControlsInternal.Count - 1)
                            {
                                ToolStripPanelCell nextCell = GetNextVisibleCell(index + 1,  /*forward*/true);
                                if (nextCell != null)
                                {
                                    Padding nextCellMargin = nextCell.Margin;

                                    // if we've already got the empty space
                                    // (available to us via the margin) use that.
                                    if (nextCellMargin.Top > requiredSpace)
                                    {
                                        nextCellMargin.Top -= requiredSpace;
                                        nextCell.Margin = nextCellMargin;
                                        freedSpace = requiredSpace;
                                    }
                                    else
                                    {
                                        // otherwise we've got to
                                        // push all controls after this point to the right
                                        // this dumps the extra stuff into the margin of index+1
                                        freedSpace = MoveDown(index + 1, requiredSpace - freedSpace);

                                        // refetch the margin for "index+1" and remove the freed space
                                        // from it - we want to actually put this to use on the control
                                        // before this one - we're making room for the control at
                                        // position "index"
                                        if (freedSpace > 0)
                                        {
                                            nextCellMargin = nextCell.Margin;
                                            nextCellMargin.Top -= freedSpace;
                                            nextCell.Margin = nextCellMargin;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // we're adding to the end.
                                ToolStripPanelCell nextCell = GetNextVisibleCell(Row.Cells.Count - 2,  /*forward*/false);
                                ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count - 1,  /*forward*/false);

                                // count the stuff at the end of the row as freed space
                                if (nextCell != null && lastCell != null)
                                {
                                    Padding lastCellMargin = lastCell.Margin;
                                    lastCellMargin.Top = Math.Max(0, locationToDrag.Y - nextCell.Bounds.Bottom);
                                    lastCell.Margin = lastCellMargin;
                                    freedSpace = requiredSpace;
                                }
                            }

                            // If we still need more space, then...
                            // PUSH the controls before it to the left
                            if (freedSpace < requiredSpace && index > 0)
                            {
                                freedSpace = MoveUp(index - 1, requiredSpace - freedSpace);
                            }

                            if (index == 0)
                            {
                                // if the index is zero and there were controls in the row
                                // we need to take care of pushing over the new cell.
                                if (freedSpace - controlToDragWidth > 0)
                                {
                                    ToolStripPanelCell newCell = Row.Cells[index] as ToolStripPanelCell;
                                    Padding newCellMargin = newCell.Margin;
                                    newCellMargin.Top = freedSpace - controlToDragWidth;
                                    newCell.Margin = newCellMargin;
                                }
                            }
                        }
                        else
                        {
                            // we're adding to the beginning.
                            Row.ControlsInternal.Add(toolStripToDrag);

#if DEBUG
                            ISupportToolStripPanel ctg = toolStripToDrag as ISupportToolStripPanel;
                            ToolStripPanelRow newPanelRow = ctg.ToolStripPanelRow;
                            Debug.Assert(newPanelRow == Row, "we should now be in the new panel row.");
#endif
                            if (Row.Cells.Count > 0)
                            {
                                ToolStripPanelCell cell = GetNextVisibleCell(Row.Cells.Count - 1, /*forward*/false);
                                if (cell != null)
                                {
                                    Padding cellMargin = cell.Margin;
                                    cellMargin.Top = Math.Max(0, locationToDrag.Y - Row.Margin.Top);
                                    cell.Margin = cellMargin;
                                }
                            }
                        }
                    }
                    finally
                    {
                        Row.ResumeLayout(true);
                    }
                }
            }

            public override void LeaveRow(ToolStrip toolStripToDrag)
            {
                // this code is here to properly add space to the next control when the
                // toolStripToDrag has been removed from the row.
                Row.SuspendLayout();
                int index = Row.ControlsInternal.IndexOf(toolStripToDrag);
                if (index >= 0)
                {
                    if (index < Row.ControlsInternal.Count - 1 /*not the last one in the row*/)
                    {
                        ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index];
                        if (cell.Visible)
                        {
                            int spaceOccupiedByCell = cell.Margin.Vertical + cell.Bounds.Height;

                            // add the space occupied by the cell to the next one.
                            ToolStripPanelCell nextCell = GetNextVisibleCell(index + 1, /*forward*/true);
                            if (nextCell != null)
                            {
                                Padding nextCellMargin = nextCell.Margin;
                                nextCellMargin.Top += spaceOccupiedByCell;
                                nextCell.Margin = nextCellMargin;
                            }
                        }
                    }
                    // remove the control from the row.
                    ((IList)Row.Cells).RemoveAt(index);
                }
                Row.ResumeLayout(true);
            }
        }

        /// <summary>
        ///  ToolStripPanelRowControlCollection
        ///
        ///  this class represents the collection of controls on a particular row.
        ///  when you add and remove controls from this collection - you also add and remove
        ///  controls to and from the ToolStripPanel.Control's collection (which happens
        ///  to be externally readonly.)
        ///
        ///  This class is used to represent the IArrangedElement.Children for the ToolStripPanelRow -
        ///  which means that this collection represents the IArrangedElements to layout for
        ///  a particular ToolStripPanelRow.
        ///
        ///  We need to keep copies of the controls in both the ToolStripPanelRowControlCollection and
        ///  the ToolStripPanel.Control collection  as the ToolStripPanel.Control collection
        ///  is responsible for parenting and unparenting the controls (ToolStripPanelRows do NOT derive from
        ///  Control and thus are NOT hwnd backed).
        /// </summary>
        internal class ToolStripPanelRowControlCollection : ArrangedElementCollection, IList, IEnumerable
        {
            private readonly ToolStripPanelRow _owner;
            private ArrangedElementCollection _cellCollection;

            public ToolStripPanelRowControlCollection(ToolStripPanelRow owner)
            {
                _owner = owner;
            }

            public ToolStripPanelRowControlCollection(ToolStripPanelRow owner, Control[] value)
            {
                _owner = owner;
                AddRange(value);
            }

            public new virtual Control this[int index]
            {
                get
                {
                    return GetControl(index);
                }
            }

            public ArrangedElementCollection Cells
            {
                get
                {
                    if (_cellCollection is null)
                    {
                        _cellCollection = new ArrangedElementCollection(InnerList);
                    }
                    return _cellCollection;
                }
            }

            public ToolStripPanel ToolStripPanel
            {
                get
                {
                    return _owner.ToolStripPanel;
                }
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public int Add(Control value)
            {
                if (value is null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
                if (!(value is ISupportToolStripPanel control))
                {
                    throw new NotSupportedException(string.Format(SR.TypedControlCollectionShouldBeOfType, typeof(ToolStrip).Name));
                }

                int index = InnerList.Add(control.ToolStripPanelCell);

                OnAdd(control, index);
                return index;
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public void AddRange(Control[] value)
            {
                if (value is null)
                {
                    throw new ArgumentNullException(nameof(value));
                }

                ToolStripPanel currentOwner = ToolStripPanel;

                if (currentOwner != null)
                {
                    currentOwner.SuspendLayout();
                }

                try
                {
                    for (int i = 0; i < value.Length; i++)
                    {
                        Add(value[i]);
                    }
                }
                finally
                {
                    if (currentOwner != null)
                    {
                        currentOwner.ResumeLayout();
                    }
                }
            }

            public bool Contains(Control value)
            {
                for (int i = 0; i < Count; i++)
                {
                    if (GetControl(i) == value)
                    {
                        return true;
                    }
                }
                return false;
            }

            public virtual void Clear()
            {
                if (_owner != null)
                {
                    ToolStripPanel.SuspendLayout();
                }

                try
                {
                    while (Count != 0)
                    {
                        RemoveAt(Count - 1);
                    }
                }
                finally
                {
                    if (_owner != null)
                    {
                        ToolStripPanel.ResumeLayout();
                    }
                }
            }

            public override IEnumerator GetEnumerator() { return new ToolStripPanelCellToControlEnumerator(InnerList); }

            private Control GetControl(int index)
            {
                Control control = null;
                ToolStripPanelCell cell = null;

                if (index < Count && index >= 0)
                {
                    cell = (ToolStripPanelCell)(InnerList[index]);
                    control = cell?.Control;
                }
                return control;
            }
            private int IndexOfControl(Control c)
            {
                for (int i = 0; i < Count; i++)
                {
                    ToolStripPanelCell cell = (ToolStripPanelCell)(InnerList[i]);
                    if (cell.Control == c)
                    {
                        return i;
                    }
                }
                return -1;
            }

            void IList.Clear() { Clear(); }

            bool IList.IsFixedSize { get { return InnerList.IsFixedSize; } }

            bool IList.Contains(object value) { return InnerList.Contains(value); }

            bool IList.IsReadOnly { get { return InnerList.IsReadOnly; } }

            void IList.RemoveAt(int index) { RemoveAt(index); }

            void IList.Remove(object value) { Remove(value as Control); }

            int IList.Add(object value) { return Add(value as Control); }

            int IList.IndexOf(object value) { return IndexOf(value as Control); }

            void IList.Insert(int index, object value) { Insert(index, value as Control); }

            public int IndexOf(Control value)
            {
                for (int i = 0; i < Count; i++)
                {
                    if (GetControl(i) == value)
                    {
                        return i;
                    }
                }
                return -1;
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public void Insert(int index, Control value)
            {
                if (value is null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
                if (!(value is ISupportToolStripPanel control))
                {
                    throw new NotSupportedException(string.Format(SR.TypedControlCollectionShouldBeOfType, typeof(ToolStrip).Name));
                }

                InnerList.Insert(index, control.ToolStripPanelCell);
                OnAdd(control, index);
            }

            /// <summary>
            ///  Do proper cleanup of ownership, etc.
            /// </summary>
            private void OnAfterRemove(Control control, int index)
            {
                if (_owner != null)
                {
                    // unfortunately we dont know the index of the control in the ToolStripPanel's
                    // control collection, as all rows share this collection.
                    // To unparent this control we need to use Remove instead  of RemoveAt.
                    using (LayoutTransaction t = new LayoutTransaction(ToolStripPanel, control, PropertyNames.Parent))
                    {
                        _owner.ToolStripPanel.Controls.Remove(control);
                        _owner.OnControlRemoved(control, index);
                    }
                }
            }

            private void OnAdd(ISupportToolStripPanel controlToBeDragged, int index)
            {
                if (_owner != null)
                {
                    LayoutTransaction layoutTransaction = null;
                    if (ToolStripPanel != null && ToolStripPanel.ParentInternal != null)
                    {
                        layoutTransaction = new LayoutTransaction(ToolStripPanel, ToolStripPanel.ParentInternal, PropertyNames.Parent);
                    }
                    try
                    {
                        if (controlToBeDragged != null)
                        {
                            controlToBeDragged.ToolStripPanelRow = _owner;

                            if (controlToBeDragged is Control control)
                            {
                                control.ParentInternal = _owner.ToolStripPanel;
                                _owner.OnControlAdded(control, index);
                            }
                        }
                    }
                    finally
                    {
                        if (layoutTransaction != null)
                        {
                            layoutTransaction.Dispose();
                        }
                    }
                }
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public void Remove(Control value)
            {
                int index = IndexOfControl(value);
                RemoveAt(index);
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public void RemoveAt(int index)
            {
                if (index >= 0 && index < Count)
                {
                    Control control = GetControl(index);
                    ToolStripPanelCell cell = InnerList[index] as ToolStripPanelCell;
                    InnerList.RemoveAt(index);
                    OnAfterRemove(control, index);
                }
            }

            [EditorBrowsable(EditorBrowsableState.Never)]
            public void CopyTo(Control[] array, int index)
            {
                if (array is null)
                {
                    throw new ArgumentNullException(nameof(array));
                }

                if (index < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(index));
                }

                if (index >= array.Length || InnerList.Count > array.Length - index)
                {
                    throw new ArgumentException(SR.ToolStripPanelRowControlCollectionIncorrectIndexLength);
                }

                for (int i = 0; i < InnerList.Count; i++)
                {
                    array[index++] = GetControl(i);
                }
            }

            ///  We want to pretend like we're only holding controls... so everywhere we've returned controls.
            ///  but the problem is if you do a foreach, you'll get the cells not the controls.  So we've got
            ///  to sort of write a wrapper class around the ArrayList enumerator.
            private class ToolStripPanelCellToControlEnumerator : IEnumerator, ICloneable
            {
                private readonly IEnumerator _arrayListEnumerator;

                internal ToolStripPanelCellToControlEnumerator(ArrayList list)
                {
                    _arrayListEnumerator = ((IEnumerable)list).GetEnumerator();
                }

                public virtual object Current
                {
                    get
                    {
                        ToolStripPanelCell cell = _arrayListEnumerator.Current as ToolStripPanelCell;
                        Debug.Assert(cell != null, "Expected ToolStripPanel cells only!!!" + _arrayListEnumerator.Current.GetType().ToString());
                        return cell?.Control;
                    }
                }

                public object Clone()
                {
                    return MemberwiseClone();
                }

                public virtual bool MoveNext()
                {
                    return _arrayListEnumerator.MoveNext();
                }

                public virtual void Reset()
                {
                    _arrayListEnumerator.Reset();
                }
            }
        }
    }
}
