using System.Drawing;
using System.Drawing.Drawing2D;

namespace Pilz.UI;

public class DisplayHelp
{
    public static void FillRectangle(Graphics g, Rectangle bounds, Color color1)
    {
        FillRectangle(g, bounds, color1, Color.Empty, 90);
    }

    public static void FillRectangle(Graphics g, Rectangle bounds, Color color1, Color color2)
    {
        FillRectangle(g, bounds, color1, color2, 90);
    }

    public static void FillRectangle(Graphics g, Rectangle r, Color color1, Color color2, int gradientAngle)
    {
        if (r.Width == 0 || r.Height == 0)
            return;

        if (color2.IsEmpty)
        {
            if (!color1.IsEmpty)
            {
                var sm = g.SmoothingMode;
                g.SmoothingMode = SmoothingMode.None;

                using var brush = new SolidBrush(color1);
                g.FillRectangle(brush, r);

                g.SmoothingMode = sm;
            }
        }
        else
        {
            using var brush = CreateLinearGradientBrush(r, color1, color2, gradientAngle);
            g.FillRectangle(brush, r);
        }
    }

    public static void FillRectangle(Graphics g, Rectangle r, Color color1, Color color2, int gradientAngle, float[] factors, float[] positions)
    {
        if (r.Width == 0 || r.Height == 0)
            return;

        if (color2.IsEmpty)
        {
            if (!color1.IsEmpty)
            {
                var sm = g.SmoothingMode;
                g.SmoothingMode = SmoothingMode.None;

                using var brush = new SolidBrush(color1);
                g.FillRectangle(brush, r);

                g.SmoothingMode = sm;
            }
        }
        else
        {
            using var brush = CreateLinearGradientBrush(r, color1, color2, gradientAngle);
            var blend = new Blend(factors.Length);
            blend.Factors = factors;
            blend.Positions = positions;
            brush.Blend = blend;
            g.FillRectangle(brush, r);
        }
    }

    public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1, Color color2, int gradientAngle)
    {
        if (color2.IsEmpty)
        {
            if (!color1.IsEmpty)
            {
                using var brush = new SolidBrush(color1);
                FillRoundedRectangle(g, brush, bounds, cornerSize);
            }
        }
        else
        {
            using var brush = CreateLinearGradientBrush(bounds, color1, color2, gradientAngle);
            FillRoundedRectangle(g, brush, bounds, cornerSize);
        }
    }

    public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1, Color color2)
    {
        FillRoundedRectangle(g, bounds, cornerSize, color1, color2, 90);
    }

    public static void FillRoundedRectangle(Graphics g, Rectangle bounds, int cornerSize, Color color1)
    {
        using var brush = new SolidBrush(color1);
        FillRoundedRectangle(g, brush, bounds, cornerSize);
    }

    public static void FillRoundedRectangle(Graphics g, Brush brush, Rectangle bounds, int cornerSize)
    {
        if (cornerSize <= 0)
        {
            var sm = g.SmoothingMode;
            g.SmoothingMode = SmoothingMode.None;
            g.FillRectangle(brush, bounds);
            g.SmoothingMode = sm;
        }
        else
        {
            bounds.Width -= 1;
            bounds.Height -= 1;

            using var path = GetRoundedRectanglePath(bounds, cornerSize);
            g.FillPath(brush, path);
        }
    }

    public static void DrawRectangle(Graphics g, Color color, int x, int y, int width, int height)
    {
        using var pen = new Pen(color, 1f);
        DrawRectangle(g, pen, x, y, width, height);
    }

    public static void DrawRectangle(Graphics g, Color color, Rectangle r)
    {
        DrawRectangle(g, color, r.X, r.Y, r.Width, r.Height);
    }

    public static void DrawRectangle(Graphics g, Pen pen, int x, int y, int width, int height)
    {
        width -= 1;
        height -= 1;
        g.DrawRectangle(pen, x, y, width, height);
    }

    public static void DrawRoundedRectangle(Graphics g, Color color, Rectangle bounds, int cornerSize)
    {
        if (color.IsEmpty)
            return;

        using var pen = new Pen(color);
        DrawRoundedRectangle(g, pen, bounds.X, bounds.Y, bounds.Width, bounds.Height, cornerSize);
    }

    public static void DrawRoundedRectangle(Graphics g, Pen pen, int x, int y, int width, int height, int cornerSize)
    {
        DrawRoundedRectangle(g, pen, null, x, y, width, height, cornerSize);
    }

    public static void DrawRoundedRectangle(Graphics g, Pen pen, Brush fill, int x, int y, int width, int height, int cornerSize)
    {
        width -= 1;
        height -= 1;
        var r = new Rectangle(x, y, width, height);
        using var path = GetRoundedRectanglePath(r, cornerSize);

        if (fill is not null)
            g.FillPath(fill, path);

        g.DrawPath(pen, path);
    }

    public static GraphicsPath GetRoundedRectanglePath(Rectangle r, int cornerSize)
    {
        var path = new GraphicsPath();

        if (cornerSize == 0)
            path.AddRectangle(r);
        else
        {
            AddCornerArc(path, r, cornerSize, eCornerArc.TopLeft);
            AddCornerArc(path, r, cornerSize, eCornerArc.TopRight);
            AddCornerArc(path, r, cornerSize, eCornerArc.BottomRight);
            AddCornerArc(path, r, cornerSize, eCornerArc.BottomLeft);
            path.CloseAllFigures();
        }

        return path;
    }

    public static LinearGradientBrush CreateLinearGradientBrush(Rectangle r, Color color1, Color color2, float gradientAngle)
    {
        if (r.Width <= 0)
            r.Width = 1;
        if (r.Height <= 0)
            r.Height = 1;
        return new LinearGradientBrush(new Rectangle(r.X, r.Y - 1, r.Width, r.Height + 1), color1, color2, gradientAngle);
    }

    public static void AddCornerArc(GraphicsPath path, Rectangle bounds, int cornerDiameter, eCornerArc corner)
    {
        if (cornerDiameter > 0)
        {
            var a = GetCornerArc(bounds, cornerDiameter, corner);
            path.AddArc(a.X, a.Y, a.Width, a.Height, a.StartAngle, a.SweepAngle);
        }

        else if (corner == eCornerArc.TopLeft)
            path.AddLine(bounds.X, bounds.Y + 2, bounds.X, bounds.Y);
        else if (corner == eCornerArc.BottomLeft)
            path.AddLine(bounds.X + 2, bounds.Bottom, bounds.X, bounds.Bottom);
        else if (corner == eCornerArc.TopRight)
            path.AddLine(bounds.Right - 2, bounds.Y, bounds.Right, bounds.Y);
        else if (corner == eCornerArc.BottomRight)
            path.AddLine(bounds.Right, bounds.Bottom - 2, bounds.Right, bounds.Bottom);
    }

    internal static ArcData GetCornerArc(Rectangle bounds, int cornerDiameter, eCornerArc corner)
    {
        if (cornerDiameter == 0)
            cornerDiameter = 1;

        int diameter = cornerDiameter * 2;

        return corner switch
        {
            eCornerArc.TopLeft => new ArcData(bounds.X, bounds.Y, diameter, diameter, 180f, 90f),
            eCornerArc.TopRight => new ArcData(bounds.Right - diameter, bounds.Y, diameter, diameter, 270f, 90f),
            eCornerArc.BottomLeft => new ArcData(bounds.X, bounds.Bottom - diameter, diameter, diameter, 90f, 90f),
            _ => new ArcData(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0f, 90f),
        };
    }

    public enum eCornerArc
    {
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight
    }

    internal struct ArcData
    {
        public int X;
        public int Y;
        public int Width;
        public int Height;
        public float StartAngle;
        public float SweepAngle;

        public ArcData(int x, int y, int width, int height, float startAngle, float sweepAngle)
        {
            X = x;
            Y = y;
            Width = width;
            Height = height;
            StartAngle = startAngle;
            SweepAngle = sweepAngle;
        }
    }
}