// TitleBarButton.cs
// Copyright (c) 2018 Bentley Systems, Incorporated. All Rights Reserved.

using Haestad.Support.Library;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Haestad.Framework.UI.Integration
{
    /// <summary>
    /// This class is the abstract base class for all title bar buttons that live on an application parent form.
    /// </summary>
    internal abstract class TitleBarButton : Button
    {
        #region Constructor
        public TitleBarButton(EventHandler clickEventHandler, ITitleBarColorInformation titleBarColorInformation, ToolTip toolTip, double sizeFactor)
        {
            ClickEventHandler = clickEventHandler;
            TitleBarColorInformation = titleBarColorInformation;
            ToolTip = toolTip;
            SizeFactor = sizeFactor;

            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);

            InitializeEvents();

            // Note: <whar> Important: To allow keyboard navigation of the ribbon (prevents the keyboard giving focus to this window).
            TabStop = false;

            IsMouseOver = false;

            Windows10LightGray = Color.FromArgb(255, 229, 229, 229);
        }
        #endregion

        #region Protected Methods
        /// <summary>
        /// Draws a filled offset rectangle within the defined Rectangle including any specified buffer and directional offsets.
        /// Directional offsets are in terms of directional arrows that point from the rectangle perimeter towards the rectangle centroid.
        /// </summary>
        /// <param name="rectangle">The maximum bounds rectangle. If all offsets and buffers are zero, this is the size of the draw area.</param>
        /// <param name="graphics">The graphics to draw to.</param>
        /// <param name="brush">The brush to use to fill the rectangle.</param>
        /// <param name="edgeBufferPercent">The buffer distance percent (from 0.0 to 0.5) in from each edge of the bounding rectangle.</param>
        /// <param name="leftOffsetPercent">An additional percent offset (from 0.0 to 0.5) in from the left side towards the right side.</param>
        /// <param name="rightOffsetPercent">An additional percent offset (from 0.0 to 0.5) in from the right side towards the left side.</param>
        /// <param name="topOffsetPercent">An additional percent offset (from 0.0 to 0.5) in from the top towards the bottom.</param>
        /// <param name="bottomOffsetPercent">An additional percent offset (from 0.0 to 0.5) in from the bottom towards the top.</param>
        /// <returns>The Rectangle that was filled as a result of the offsets.</returns>
        protected Rectangle DrawOffsetRect(Rectangle rectangle, Graphics graphics, Brush brush, double edgeBufferPercent, double leftOffsetPercent, 
            double rightOffsetPercent, double topOffsetPercent, double bottomOffsetPercent)
        {
            CheckPercentArgument(edgeBufferPercent);
            CheckPercentArgument(leftOffsetPercent);
            CheckPercentArgument(rightOffsetPercent);
            CheckPercentArgument(topOffsetPercent);
            CheckPercentArgument(bottomOffsetPercent);

            int edgeDistance = rectangle.Height; // Note: <whar> Buttons are always square.
            int edgeBufferDistance = Convert.ToInt32(Math.Round((edgeBufferPercent * edgeDistance), MidpointRounding.AwayFromZero));
            int leftOffset = Convert.ToInt32(Math.Round((leftOffsetPercent * edgeDistance), MidpointRounding.AwayFromZero));
            int rightOffset = Convert.ToInt32(Math.Round((rightOffsetPercent * edgeDistance), MidpointRounding.AwayFromZero));
            int topOffset = Convert.ToInt32(Math.Round((topOffsetPercent * edgeDistance), MidpointRounding.AwayFromZero));
            int bottomOffset = Convert.ToInt32(Math.Round((bottomOffsetPercent * edgeDistance), MidpointRounding.AwayFromZero));

            int left = rectangle.Left + edgeBufferDistance + leftOffset;
            int top = rectangle.Top + edgeBufferDistance + topOffset;
            int bottom = rectangle.Bottom - edgeBufferDistance - bottomOffset;
            int right = rectangle.Right - edgeBufferDistance - rightOffset;

            Rectangle outerRectangle = new Rectangle(left, top, right - left, bottom - top);
            graphics.FillRectangle(brush, outerRectangle);
            return outerRectangle;
        }
        #endregion

        #region Protected Overide Methods
        protected override void OnCreateControl()
        {
            InitializeToolTip();
            base.OnCreateControl();
        }

        protected override void OnPaint(PaintEventArgs pevent)
        {
            DrawBackground(pevent);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            IsMouseOver = false;
            // TODO 3: <whar> No Refresh() call here, but seems to work.
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            IsMouseOver = true;
            Refresh();
        }
        #endregion

        #region Protected Methods
        protected void InitializeToolTip()
        {
            ToolTip.SetToolTip(this, ToolTipText);
        }
        #endregion

        #region Private Methods
        private void InitializeEvents()
        {
            Click += ClickEventHandler;
        }

        private void CheckPercentArgument(double percent)
        {
            const double MINIMUM_PERCENT = -0.5;
            const double MAXIMUM_PERCENT = 0.5;
            if (percent < MINIMUM_PERCENT || percent > MAXIMUM_PERCENT)
                throw new ArgumentException("Unexpected percent value in class TitleBarButton."); // I18N. OK.
        }

        private void DrawBackground(PaintEventArgs pevent)
        {
            // Note: <whar> Using pevent.ClipRectangle was causing grief (only under Windows 7 it seems) where the clip rectangle may have been a portion 
            // of the button that was obscured by another window (e.g., tooltip), because the drawing logic here is expecting the clip region to be the full
            // client rectangle. It seems that under Windows 8.1, this is always the case (and I presume for Windows 10 too), but under Windows 7, the clip
            // region could be a small portion of the client rectangle and this would result in graphical corruption where this logic drew tiny buttons in
            // small clipped regions. This is now fixed by always using the client rectangle to re-draw.
            // is not the full client area of the control. The ideal way to re-draw would be to draw to a buffer and blit the clip region,
            // however, since the buttons are very small we just re-draw the full client area to avoid the problem with using the ClipRectangle.

            Rectangle startingRectangle = ClientRectangle;
            int width = Convert.ToInt32(startingRectangle.Width * SizeFactor);
            int height = Convert.ToInt32(startingRectangle.Height * SizeFactor);
            int deltaX = startingRectangle.Width - width;
            int deltaY = startingRectangle.Height - height;

            if (MathLibrary.IsOdd(deltaX))
                deltaX++;
            if (MathLibrary.IsOdd(deltaY))
                deltaY++;

            DrawRectangle = new Rectangle(startingRectangle.X + deltaX / 2, startingRectangle.Y + deltaY / 2,
                startingRectangle.Width - deltaX, startingRectangle.Height - deltaY);

            using (Brush brush = new SolidBrush(BackColor))
            {
                pevent.Graphics.FillRectangle(brush, startingRectangle);
            }
        }
        #endregion

        #region Public Override Properties
        public override Color BackColor
        {
            get
            {
                if (IsMouseOver)
                {
                    if (RibbonExternalConfiguration.Instance.Windows10StyleEnabled)
                        return Windows10LightGray;
                    else
                        return TitleBarColorInformation.GetTitleBarButtonHighlightColor();
                }
                else
                    return TitleBarColorInformation.GetTitleBarButtonBackColor();
            }
        }

        public override Color ForeColor
        {
            get
            {
                return TitleBarColorInformation.GetTitleBarButtonForeColor();
            }
        }
        #endregion

        #region Protected Abstract Properties
        protected abstract string ToolTipText { get; }
        #endregion

        #region Protected Properties
        protected ToolTip ToolTip
        {
            get;
            set;
        }

        protected Rectangle DrawRectangle
        {
            get;
            set;
        }

        protected bool IsMouseOver
        {
            get;
            private set;
        }

        protected ITitleBarColorInformation TitleBarColorInformation
        {
            get;
            private set;
        }
        #endregion

        #region Private Properties
        private EventHandler ClickEventHandler
        {
            get;
            set;
        }

        private double SizeFactor
        {
            get;
            set;
        }

        private Color Windows10LightGray
        {
            get;
            set;
        }
        #endregion
    }
}
