using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace ModernControls
{
    /// <summary>
    /// Из skill winforms-modern-buttons: градиент, тень, скругление, hover/pressed.
    /// </summary>
    public class ModernGradientButton : Button
    {
        public enum ButtonState { Normal, Hover, Pressed }

        private ButtonState _state = ButtonState.Normal;

        [Category("Appearance")]
        public Color GradientTop { get; set; } = Color.FromArgb(66, 133, 244);

        [Category("Appearance")]
        public Color GradientBottom { get; set; } = Color.FromArgb(30, 87, 194);

        [Category("Appearance")]
        public Color HoverGradientTop { get; set; } = Color.FromArgb(90, 150, 255);

        [Category("Appearance")]
        public Color HoverGradientBottom { get; set; } = Color.FromArgb(45, 105, 220);

        [Category("Appearance")]
        public Color PressedGradientTop { get; set; } = Color.FromArgb(25, 70, 160);

        [Category("Appearance")]
        public Color PressedGradientBottom { get; set; } = Color.FromArgb(15, 55, 130);

        [Category("Appearance")]
        public int CornerRadius { get; set; } = 12;

        [Category("Appearance")]
        public int ShadowDepth { get; set; } = 5;

        [Category("Appearance")]
        public Color ShadowColor { get; set; } = Color.FromArgb(60, 0, 0, 0);

        [Category("Appearance")]
        public bool AnimateHover { get; set; } = true;

        public ModernGradientButton()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint
                     | ControlStyles.UserPaint
                     | ControlStyles.ResizeRedraw
                     | ControlStyles.SupportsTransparentBackColor
                     | ControlStyles.OptimizedDoubleBuffer, true);

            FlatStyle = FlatStyle.Flat;
            FlatAppearance.BorderSize = 0;
            BackColor = Color.Transparent;
            ForeColor = Color.White;
            Font = new Font("Segoe UI", 9.5f, FontStyle.Regular);
            Cursor = Cursors.Hand;
            Size = new Size(140, 42);
            Padding = new Padding(0, 0, 0, ShadowDepth);
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            _state = ButtonState.Hover;
            Invalidate();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            _state = ButtonState.Normal;
            Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs mevent)
        {
            base.OnMouseDown(mevent);
            _state = ButtonState.Pressed;
            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs mevent)
        {
            base.OnMouseUp(mevent);
            _state = ClientRectangle.Contains(mevent.Location) ? ButtonState.Hover : ButtonState.Normal;
            Invalidate();
        }

        private static GraphicsPath RoundedRect(Rectangle bounds, int radius)
        {
            var d = radius * 2;
            var path = new GraphicsPath();

            if (radius <= 0)
            {
                path.AddRectangle(bounds);
                return path;
            }

            path.StartFigure();
            path.AddArc(bounds.X, bounds.Y, d, d, 180, 90);
            path.AddArc(bounds.Right - d, bounds.Y, d, d, 270, 90);
            path.AddArc(bounds.Right - d, bounds.Bottom - d, d, d, 0, 90);
            path.AddArc(bounds.X, bounds.Bottom - d, d, d, 90, 90);
            path.CloseFigure();
            return path;
        }

        protected override void OnPaint(PaintEventArgs pevent)
        {
            var g = pevent.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            var buttonRect = new Rectangle(0, 0, Width - 1, Height - 1 - ShadowDepth);

            for (var i = ShadowDepth; i > 0; i--)
            {
                var alpha = ShadowColor.A * i / (ShadowDepth * 3);
                using (var shadowBrush = new SolidBrush(Color.FromArgb(alpha, ShadowColor)))
                using (var shadowPath = RoundedRect(
                           new Rectangle(i, buttonRect.Y + i + 1, buttonRect.Width - i, buttonRect.Height - 1),
                           CornerRadius))
                {
                    g.FillPath(shadowBrush, shadowPath);
                }
            }

            Color top;
            Color bottom;
            switch (_state)
            {
                case ButtonState.Hover:
                    top = HoverGradientTop;
                    bottom = HoverGradientBottom;
                    break;
                case ButtonState.Pressed:
                    top = PressedGradientTop;
                    bottom = PressedGradientBottom;
                    break;
                default:
                    top = GradientTop;
                    bottom = GradientBottom;
                    break;
            }

            using (var path = RoundedRect(buttonRect, CornerRadius))
            using (var brush = new LinearGradientBrush(buttonRect, top, bottom, LinearGradientMode.Vertical))
            {
                g.FillPath(brush, path);

                using (var highlightPen = new Pen(Color.FromArgb(40, Color.White), 1))
                {
                    g.DrawArc(highlightPen, buttonRect.X, buttonRect.Y, CornerRadius * 2, CornerRadius * 2, 180, 90);
                }
            }

            TextRenderer.DrawText(
                g,
                Text,
                Font,
                buttonRect,
                ForeColor,
                TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
        }

        public void ApplyPreset(Preset preset)
        {
            switch (preset)
            {
                case Preset.Primary:
                    GradientTop = Color.FromArgb(66, 133, 244);
                    GradientBottom = Color.FromArgb(30, 87, 194);
                    HoverGradientTop = Color.FromArgb(90, 150, 255);
                    HoverGradientBottom = Color.FromArgb(45, 105, 220);
                    PressedGradientTop = Color.FromArgb(25, 70, 160);
                    PressedGradientBottom = Color.FromArgb(15, 55, 130);
                    break;
                case Preset.Success:
                    GradientTop = Color.FromArgb(67, 189, 118);
                    GradientBottom = Color.FromArgb(30, 140, 80);
                    HoverGradientTop = Color.FromArgb(90, 205, 135);
                    HoverGradientBottom = Color.FromArgb(40, 160, 95);
                    PressedGradientTop = Color.FromArgb(20, 120, 65);
                    PressedGradientBottom = Color.FromArgb(10, 95, 50);
                    break;
                case Preset.Danger:
                    GradientTop = Color.FromArgb(235, 87, 87);
                    GradientBottom = Color.FromArgb(200, 40, 40);
                    HoverGradientTop = Color.FromArgb(245, 110, 110);
                    HoverGradientBottom = Color.FromArgb(215, 55, 55);
                    PressedGradientTop = Color.FromArgb(175, 30, 30);
                    PressedGradientBottom = Color.FromArgb(140, 20, 20);
                    break;
                case Preset.Neutral:
                    GradientTop = Color.FromArgb(120, 130, 140);
                    GradientBottom = Color.FromArgb(80, 90, 100);
                    HoverGradientTop = Color.FromArgb(140, 150, 160);
                    HoverGradientBottom = Color.FromArgb(95, 105, 115);
                    PressedGradientTop = Color.FromArgb(65, 75, 85);
                    PressedGradientBottom = Color.FromArgb(50, 60, 70);
                    break;
            }

            Invalidate();
        }

        public enum Preset { Primary, Success, Danger, Neutral }
    }
}
