using InABox.Core;
using netDxf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Vector3 = System.Numerics.Vector3;

namespace InABox.Dxf;
internal static class Utils
{
    public static PointF Transform(this Matrix matrix, PointF point)
    {
        var arr = new[] { point };
        matrix.TransformPoints(arr);
        return arr[0];
    }
    public static PointF TransformVector(this Matrix matrix, PointF point)
    {
        var arr = new[] { point };
        matrix.TransformVectors(arr);
        return arr[0];
    }

    public static float GetRotation(this Matrix matrix)
    {
        var vec = matrix.TransformVector(new PointF(1, 0));
        return (float)(Math.Atan2(vec.Y, vec.X) * 180 / Math.PI);
    }

    public static float GetScale(this Matrix matrix)
    {
        var vec = matrix.TransformVector(new PointF(1, 0));
        return (float)Math.Sqrt(vec.X * vec.X + vec.Y * vec.Y);
    }

    public static float Mod(float x, float y)
    {
        return (float)(x - y * Math.Floor(x / y));
    }

    public static Vector3 ToVec3(this netDxf.Vector3 vec)
    {
        return new((float)vec.X, (float)vec.Y, (float)vec.Z);
    }
    public static netDxf.Vector3 ToNetVec3(this Vector3 vec)
    {
        return new(vec.X, vec.Y, vec.Z);
    }


    public static Matrix4x4 Translate(this Matrix4x4 matrix, float x, float y, float z)
    {
        return Matrix4x4.CreateTranslation(x, y, z) * matrix;
    }
    public static Matrix4x4 Scale(this Matrix4x4 matrix, float x, float y, float z)
    {
        return Matrix4x4.CreateScale(x, y, z) * matrix;
    }
    public static Matrix4x4 Rotate(this Matrix4x4 matrix, float x, float y, float z, float angle)
    {
        var cos = (float)Math.Cos(angle * Math.PI / 180);
        var sin = (float)Math.Sin(angle * Math.PI / 180);

        var rotMatrix = new Matrix4x4();
        rotMatrix.M11 = x * x * (1 - cos) + cos;
        rotMatrix.M21 = x * y * (1 - cos) - z * sin;
        rotMatrix.M31 = x * z * (1 - cos) + y * sin;

        rotMatrix.M12 = y * x * (1 - cos) + z * sin;
        rotMatrix.M22 = y * y * (1 - cos) + cos;
        rotMatrix.M32 = y * z * (1 - cos) - x * sin;

        rotMatrix.M13 = z * x * (1 - cos) - y * sin;
        rotMatrix.M23 = z * y * (1 - cos) + x * sin;
        rotMatrix.M33 = z * z * (1 - cos) + cos;

        rotMatrix.M14 = 0;
        rotMatrix.M24 = 0;
        rotMatrix.M34 = 0;
        rotMatrix.M44 = 1;
        rotMatrix.M43 = 0;
        rotMatrix.M42 = 0;
        rotMatrix.M41 = 0;

        return rotMatrix * matrix;
    }

    public static RectangleF? CombineBounds(IEnumerable<RectangleF?> bounds)
    {
        var e = bounds.NotNull().GetEnumerator();
        if (!e.MoveNext())
        {
            return null;
        }

        var bound = e.Current;
        while(e.MoveNext())
        {
            var thisBound = e.Current;

            var x = Math.Min(bound.X, thisBound.X);
            var y = Math.Min(bound.Y, thisBound.Y);
            var right = Math.Max(bound.Right, thisBound.Right);
            var bottom = Math.Max(bound.Bottom, thisBound.Bottom);

            bound.X = x;
            bound.Y = y;
            bound.Height = bottom - bound.Y;
            bound.Width = right - bound.X;
        }

        return bound;
    }
    public static RectangleF? CombineBounds(params RectangleF?[] bounds)
    {
        return CombineBounds((IEnumerable<RectangleF?>)bounds);
    }

    public static RectangleF AddPoint(RectangleF rectangle, PointF point)
    {
        var x = Math.Min(rectangle.X, point.X);
        var y = Math.Min(rectangle.Y, point.Y);
        var right = Math.Max(rectangle.Right, point.X);
        var bottom = Math.Max(rectangle.Bottom, point.Y);
        rectangle.X = x;
        rectangle.Y = y;
        rectangle.Height = bottom - rectangle.Y;
        rectangle.Width = right - rectangle.X;
        return rectangle;
    }
    public static RectangleF? RectangleFromPoints(params PointF[] points)
    {
        if (points.Length == 0) return null;

        var rectangle = new RectangleF(points[0].X, points[0].Y, 0, 0);
        for(int i = 1; i < points.Length; ++i)
        {
            var point = points[i];
            var x = Math.Min(rectangle.X, point.X);
            var y = Math.Min(rectangle.Y, point.Y);
            var right = Math.Max(rectangle.Right, point.X);
            var bottom = Math.Max(rectangle.Bottom, point.Y);
            rectangle.X = x;
            rectangle.Y = y;
            rectangle.Height = bottom - rectangle.Y;
            rectangle.Width = right - rectangle.X;
        }
        return rectangle;
    }
}
