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

namespace Pilz.UI;


/// <summary>
/// Contains static methods that are used for the standart PaintingObject Types.
/// </summary>
public class DefaultDrawMethodes
{

    public static void DrawText(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;
        var b = new SolidBrush(obj.TextColor);
        var p = new PointF();
        var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Width), (int)Math.Round(obj.Height));

        var f = StringFormat.GenericDefault;
        f.Alignment = obj.HorizontalTextAlignment;
        f.LineAlignment = obj.VerticalTextAlignment;

        float zoomFactor;
        if (obj.Parent is null)
            zoomFactor = 1.0f;
        else
        {
            zoomFactor = obj.Parent.ZoomFactor.Width;
        }

        e.Graphics.DrawString(obj.Text, new Font(obj.TextFont.FontFamily, obj.TextFont.Size * zoomFactor, obj.TextFont.Style), b, rect, f);
    }
    private static object _DrawPicture_newSyncObj = new object();

    public static void DrawPicture(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;
        Image objImg;
        Size objImgSize;
        RectangleF result;
        Bitmap image;
        SizeF zoomf;
        bool hasNoParent = e.PaintingObject.Parent is null;
        object syncObj;

        if (hasNoParent)
        {
            zoomf = new SizeF(1f, 1f);
            syncObj = _DrawPicture_newSyncObj;
        }
        else
        {
            zoomf = e.PaintingObject.Parent.ZoomFactor;
            syncObj = e.PaintingObject.Parent;
        }

        lock (syncObj)
        {
            if (obj?.Image is null)
                return;
            objImg = obj.Image;
            objImgSize = objImg.Size;
        }

        image = (Bitmap)obj.BufferedImage;
        result = CalculateImageResolution(obj, objImgSize, zoomf);

        if (obj.ImageProperties.Rotate == 90 || obj.ImageProperties.Rotate == 270)
            result = CalculateImageResolution(obj, new SizeF(objImgSize.Height, objImgSize.Width), zoomf);

        if (image is null)
        {
            bool needRescaleImageBecauseRot = false;

            image = DrawToNewImage((Bitmap)objImg, result.Size);

            switch (obj.ImageProperties.Rotate)
            {
                case 90:
                    {
                        image.RotateFlip(RotateFlipType.Rotate90FlipNone);
                        needRescaleImageBecauseRot = true;
                        break;
                    }
                case 180:
                    {
                        image.RotateFlip(RotateFlipType.Rotate180FlipNone);
                        break;
                    }
                case 270:
                    {
                        image.RotateFlip(RotateFlipType.Rotate270FlipNone);
                        needRescaleImageBecauseRot = true;
                        break;
                    }
            }
            if (obj.ImageProperties.FlipX)
                image.RotateFlip(RotateFlipType.RotateNoneFlipX);
            if (obj.ImageProperties.FlipY)
                image.RotateFlip(RotateFlipType.RotateNoneFlipY);

            if (needRescaleImageBecauseRot)
            {
                result = CalculateImageResolution(obj, new SizeF(objImgSize.Height, objImgSize.Width), zoomf);
                image = DrawToNewImage(image, result.Size);
            }

            obj.BufferedImage = image;
        }

        if (image is not null)
        {
            lock (syncObj)
                e.Graphics.DrawImageUnscaled(image, new Rectangle(new Point((int)Math.Round(obj.Location.X + result.Location.X - e.Offset.X), (int)Math.Round(obj.Location.Y + result.Location.Y - e.Offset.Y)), result.Size.ToSize()));
        }
    }

    private static Bitmap DrawToNewImage(Bitmap image, SizeF newSize)
    {
        var bmp = new Bitmap((int)Math.Round(newSize.Width < 0f ? newSize.Width * -1 : newSize.Width), (int)Math.Round(newSize.Height < 0f ? newSize.Height * -1 : newSize.Height));
        var gimage = Graphics.FromImage(bmp);
        gimage.SmoothingMode = SmoothingMode.HighQuality;
        gimage.PixelOffsetMode = PixelOffsetMode.HighQuality;
        gimage.PageUnit = GraphicsUnit.Pixel;
        gimage.InterpolationMode = InterpolationMode.HighQualityBicubic;
        gimage.DrawImage(image, new RectangleF(PointF.Empty, newSize));
        gimage.Dispose();
        return bmp;
    }

    public static void DrawLine(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;
        var p2 = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle };

        if (obj.LineEndCap is not null)
        {
            var args = obj.LineEndCap.Configure();
            p2.StartCap = args.LineCap;
            p2.CustomStartCap = args.CustomLineCap;
        }

        if (obj.LineStartCap is not null)
        {
            var args = obj.LineStartCap.Configure();
            p2.EndCap = args.LineCap;
            p2.CustomEndCap = args.CustomLineCap;
        }

        p2.Alignment = PenAlignment.Center;
        var no = new PointF(e.X, e.Y);
        e.Graphics.DrawLine(p2, no, no + obj.Size);
    }

    private static RectangleF CalculateImageResolution(PaintingObject obj, SizeF imageSize, SizeF zoom)
    {
        var result = new RectangleF();
        var objrect = new RectangleF(obj.Location, obj.Size);
        var size = new SizeF(imageSize.Width * zoom.Width, imageSize.Height * zoom.Height);
        var clientRectangle = objrect;
        float val = clientRectangle.Width / size.Width;

        clientRectangle = objrect;
        float num = Math.Min(val, clientRectangle.Height / size.Height);

        result.Width = (int)Math.Round(Math.Truncate((double)(size.Width * num)));
        result.Height = (int)Math.Round(Math.Truncate((double)(size.Height * num)));

        clientRectangle = objrect;
        result.X = (long)Math.Round(clientRectangle.Width - result.Width) / 2L;

        clientRectangle = objrect;
        result.Y = (long)Math.Round(clientRectangle.Height - result.Height) / 2L;

        return result;
    }

    public static void DrawTriangle(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;

        var p1 = new Point((int)Math.Round(obj.Size.Width / 2f + e.X), (int)Math.Round(e.Y));
        var p2 = new Point((int)Math.Round(e.X), (int)Math.Round(e.Y + obj.Size.Height));
        var p3 = new Point((int)Math.Round(e.X + obj.Size.Width), (int)Math.Round(e.Y + obj.Size.Height));

        if (obj.EnableFill)
        {
            var b = new SolidBrush(obj.FillColor);
            e.Graphics.FillPolygon(b, new[] { p1, p2, p3 });
        }

        if (obj.EnableOutline)
        {
            float lw = obj.OutlineThicknes;
            var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
            e.Graphics.DrawPolygon(p, new[] { p1, p2, p3 });
        }
    }

    public static void DrawRectangle(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;
        float hol = obj.OutlineThicknes / 2f;

        if (obj.EnableFill)
        {
            var b = new SolidBrush(obj.FillColor);
            var rect = obj.EnableOutline ? new Rectangle((int)Math.Round(e.X + hol), (int)Math.Round(e.Y + hol), (int)Math.Round(obj.Size.Width - hol * 2f), (int)Math.Round(obj.Size.Height - hol * 2f)) : new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
            e.Graphics.FillRectangle(b, rect);
        }

        if (obj.EnableOutline)
        {
            var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
            var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
            e.Graphics.DrawRectangle(p, rect);
        }
    }

    public static void DrawEllipse(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;

        if (obj.EnableFill)
        {
            var b = new SolidBrush(obj.FillColor);
            var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
            e.Graphics.FillEllipse(b, rect);
        }

        if (obj.EnableOutline)
        {
            var p = new Pen(obj.OutlineColor, obj.OutlineThicknes) { DashStyle = obj.OutlineDashStyle, Alignment = PenAlignment.Inset };
            var rect = new Rectangle((int)Math.Round(e.X), (int)Math.Round(e.Y), (int)Math.Round(obj.Size.Width), (int)Math.Round(obj.Size.Height));
            e.Graphics.DrawEllipse(p, rect);
        }
    }

    public static void DrawSelection(PaintingObjectPaintEventArgs e)
    {
        var obj = e.PaintingObject;
        float lw = 2.5f;
        float hlw = lw / 2f;
        float hlwphol = hlw; // + hol
        float hlwpholm2 = hlwphol * 2f;

        var p = new Pen(Color.CornflowerBlue, lw) { DashStyle = obj.SelectionDashStyle, Alignment = PenAlignment.Outset };
        var rect = new Rectangle((int)Math.Round(e.X - hlwphol), (int)Math.Round(e.Y - hlwphol), (int)Math.Round(obj.Size.Width + hlwpholm2), (int)Math.Round(obj.Size.Height + hlwpholm2));
        e.Graphics.DrawRectangle(p, rect);
    }

    public static void DrawGrid(PaintEventArgs e, PaintingControl pc, PointF offset)
    {
        var p = new Pen(pc.GridColor, 0.5f);

        int curX = (int)Math.Round(pc.GridChunkSize.Width * pc.ZoomFactor.Width - offset.X);
        while (curX < pc.Width)
        {
            e.Graphics.DrawLine(p, curX, -offset.Y, curX, pc.Height);
            curX = (int)Math.Round(curX + pc.GridChunkSize.Width * pc.ZoomFactor.Width);
        }

        int curY = (int)Math.Round(pc.GridChunkSize.Height * pc.ZoomFactor.Height - offset.Y);
        while (curY < pc.Height)
        {
            e.Graphics.DrawLine(p, -offset.X, curY, pc.Width, curY);
            curY = (int)Math.Round(curY + pc.GridChunkSize.Height * pc.ZoomFactor.Height);
        }
    }

    public static void DrawAreaSelection(PaintEventArgs e, PaintingControl pc, PointF startMousePos, PointF lastMousePos)
    {
        var rectToDraw = HelpfulDrawingFunctions.GetRectangle(startMousePos, lastMousePos);
        var p = new Pen(pc.AreaSelectionColor);
        p.DashStyle = startMousePos.X >= lastMousePos.X ? DashStyle.DashDot : DashStyle.Solid;
        p.Width = 3f;
        e.Graphics.DrawRectangle(p, rectToDraw.X, rectToDraw.Y, rectToDraw.Width, rectToDraw.Height);
    }

}