// 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.

using System.Drawing;

namespace System.Windows.Forms.Layout
{
    internal partial class FlowLayout
    {
        /// <summary>
        ///  The goal of the FlowLayout Engine is to always layout from left to right. In order to achieve different
        ///  flow directions we have "Proxies" for the Container (the thing laying out) and for setting the bounds of the
        ///  child elements.
        ///
        ///  We have a base ContainerProxy, and derived proxies for all of the flow directions. In order to achieve flow direction of RightToLeft,
        ///  the RightToLeft container proxy detects when we're going to set the bounds and translates it to the right.
        ///
        ///  In order to do a vertical flow, such as TopDown, we pretend we're laying out horizontally. The main way this is
        ///  achieved is through the use of the VerticalElementProxy, which flips all rectangles and sizes.
        ///
        ///  In order to do BottomUp, we combine the same techniques of TopDown with the RightToLeft flow. That is,
        ///  we override the bounds, and translate from left to right, AND use the VerticalElementProxy.
        ///
        ///  A final note: This layout engine does all its RightToLeft translation itself. It does not support
        ///  WS_EX_LAYOUTRTL (OS mirroring).
        /// </summary>
        private class ContainerProxy
        {
            private readonly IArrangedElement _container;
            private readonly bool _isContainerRTL;
            private Rectangle _displayRect;
            private ElementProxy? _elementProxy;
            public ContainerProxy(IArrangedElement container)
            {
                _container = container;
                _isContainerRTL = false;
                if (_container is Control)
                {
                    _isContainerRTL = ((Control)(_container)).RightToLeft == RightToLeft.Yes;
                }
            }

            public virtual Rectangle Bounds
            {
                set
                {
                    if (IsContainerRTL)
                    {
                        // Offset the X coordinate.
                        if (IsVertical)
                        {
                            // Offset the Y value here, since it is really the X value.
                            value.Y = DisplayRect.Bottom - value.Bottom;
                        }
                        else
                        {
                            value.X = DisplayRect.Right - value.Right;
                        }

                        if (Container is FlowLayoutPanel flp)
                        {
                            Point ptScroll = flp.AutoScrollPosition;
                            if (ptScroll != Point.Empty)
                            {
                                Point pt = new Point(value.X, value.Y);
                                if (IsVertical)
                                {
                                    // Offset the Y value here, since it is really the X value.
                                    pt.Offset(0, ptScroll.X);
                                }
                                else
                                {
                                    pt.Offset(ptScroll.X, 0);
                                }

                                value.Location = pt;
                            }
                        }
                    }

                    ElementProxy.Bounds = value;
                }
            }

            public IArrangedElement Container => _container;

            /// <summary>
            ///  Returns the display rectangle of the container - this will be flipped if the layout
            ///  is a vertical layout.
            /// </summary>
            public Rectangle DisplayRect
            {
                get => _displayRect;
                set
                {
                    if (_displayRect != value)
                    {
                        // flip the displayRect since when we do layout direction TopDown/BottomUp, we layout the controls
                        // on the flipped rectangle as if our layout direction were LeftToRight/RightToLeft. In this case
                        // we can save some code bloat
                        _displayRect = LayoutUtils.FlipRectangleIf(IsVertical, value);
                    }
                }
            }

            /// <summary>
            ///  Returns the element proxy to use. A vertical element proxy will typically flip
            ///  all the sizes and rectangles so that it can fake being laid out in a
            ///  horizontal manner.
            /// </summary>
            public ElementProxy ElementProxy
            {
                get
                {
                    if (_elementProxy is null)
                    {
                        _elementProxy = IsVertical ? new VerticalElementProxy() : new ElementProxy();
                    }

                    return _elementProxy;
                }
            }

            protected bool IsContainerRTL => _isContainerRTL;

            /// <summary>
            ///  Specifies if we're TopDown or BottomUp and should use the VerticalElementProxy to
            ///  translate
            /// </summary>
            protected virtual bool IsVertical => false;
            /// <summary>
            ///  Used when you want to translate from right to left, but preserve Margin.Righ
            ///  and Margin.Left.
            /// </summary>
            protected Rectangle RTLTranslateNoMarginSwap(Rectangle bounds)
            {
                Rectangle newBounds = bounds;
                newBounds.X = DisplayRect.Right - bounds.X - bounds.Width + ElementProxy.Margin.Left - ElementProxy.Margin.Right;

                // Since DisplayRect.Right and bounds.X are both adjusted for the AutoScrollPosition, we need add it back here.
                if (Container is FlowLayoutPanel flp)
                {
                    Point ptScroll = flp.AutoScrollPosition;
                    if (ptScroll != Point.Empty)
                    {
                        Point pt = new Point(newBounds.X, newBounds.Y);
                        if (IsVertical)
                        {
                            // We need to treat Vertical a litte differently. It really helps if you draw this out.
                            // Remember that when we layout BottomUp, we first layout TopDown, then call this method.
                            // When we layout TopDown we layout in flipped rectangles. I.e. x becomes y, y becomes x,
                            // height becomes width, width becomes height. We do our layout, then when we eventually
                            // set the bounds of the child elements, we flip back. Thus, x will eventually
                            // become y. We need to adjust for scrolling - but only in the y direction -
                            // and since x becomes y, we adjust x. But since AutoScrollPoisition has not been swapped,
                            // we need to use its Y coordinate when offsetting.

                            pt.Offset(ptScroll.Y, 0);
                        }
                        else
                        {
                            pt.Offset(ptScroll.X, 0);
                        }

                        newBounds.Location = pt;
                    }
                }

                return newBounds;
            }
        }
    }
}
