using System;
namespace Engine.Core;
/// 
/// Represents a two-dimensional vector.
/// 
/// X position of the .
/// Y position of the .
/// 
/// Initializes a new instance of the  struct with the specified positions.
/// 
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
public readonly struct Vector2D(float x, float y) : IEquatable
{
    /// 
    /// The X coordinate of the .
    /// 
    public readonly float X = x;
    /// 
    /// The Y coordinate of the .
    /// 
    public readonly float Y = y;
    /// 
    /// The magnitude (length) of the .
    /// 
    public float Magnitude => Length(this);
    /// 
    /// The squared magnitude (length) of the .
    /// 
    public float MagnitudeSquared => LengthSquared(this);
    /// 
    /// Gets a  with the direction reversed.
    /// 
    public readonly Vector2D Reversed => -this;
    /// 
    /// The normalized form of the  (a  with the same direction and a magnitude of 1).
    /// 
    public Vector2D Normalized => Normalize(this);
    /// 
    /// Represents the unit  pointing upwards.
    /// 
    public readonly static Vector2D Up = new(0f, 1f);
    /// 
    /// Represents the unit  pointing downwards.
    /// 
    public readonly static Vector2D Down = new(0f, -1f);
    /// 
    /// Represents the unit  pointing leftwards.
    /// 
    public readonly static Vector2D Left = new(-1f, 0f);
    /// 
    /// Represents the unit  pointing rightwards.
    /// 
    public readonly static Vector2D Right = new(1f, 0f);
    /// 
    /// Represents the zero .
    /// 
    public readonly static Vector2D Zero = new(0f, 0f);
    /// 
    /// Represents the  with both components equal to 1.
    /// 
    public readonly static Vector2D One = new(1f, 1f);
    public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
    public static Vector2D operator +(Vector2D left, Vector2D right) => new(left.X + right.X, left.Y + right.Y);
    public static Vector2D operator -(Vector2D left, Vector2D right) => new(left.X - right.X, left.Y - right.Y);
    public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
    public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
    public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
    public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
    public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
    public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
    public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
    public static implicit operator Vector2D(Vector2DInt vector) => new(vector.X, vector.Y);
    public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
    public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
    /// 
    /// Calculates the length of the .
    /// 
    /// The .
    /// The length of the .
    public static float Length(Vector2D vector) => Math.Sqrt(LengthSquared(vector));
    /// 
    /// Calculates the squared length of the .
    /// 
    /// The .
    /// The squared length of the .
    public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
    /// 
    /// Calculates the distance between two s.
    /// 
    /// The start .
    /// The end .
    /// The distance between the two s.
    public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
    /// 
    /// Inverts the direction of the .
    /// 
    /// The .
    /// The inverted .
    public static Vector2D Invert(Vector2D vector) => -vector;
    /// 
    /// Adds two s.
    /// 
    /// The first .
    /// The second .
    /// The sum of the two s.
    public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
    /// 
    /// Subtracts one  from another.
    /// 
    /// The  to subtract from.
    /// The  to subtract.
    /// The result of subtracting the second  from the first.
    public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
    /// 
    /// Multiplies a  by a scalar value.
    /// 
    /// The .
    /// The scalar value.
    /// The result of multiplying the  by the scalar value.
    public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
    /// 
    /// Divides a  by a scalar value.
    /// 
    /// The .
    /// The scalar value.
    /// The result of dividing the  by the scalar value.
    public static Vector2D Divide(Vector2D vector, float value) => vector / value;
    /// 
    /// Calculates the absolute value of each component of the vector.
    /// 
    /// The .
    /// The  with each component's absolute value.
    public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
    /// 
    /// Normalizes the  (creates a unit  with the same direction).
    /// 
    /// The  to normalize.
    /// The normalized .
    public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
    /// 
    /// Reflects a  off a surface with the specified normal.
    /// 
    /// The incident .
    /// The normal  of the surface.
    /// The reflected .
    public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
    /// 
    /// Calculates the  from one point to another.
    /// 
    /// The starting point.
    /// The ending point.
    /// The  from the starting point to the ending point.
    public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
    /// 
    /// Scales a  by another  component-wise.
    /// 
    /// The  to scale.
    /// The  containing the scaling factors for each component.
    /// The scaled .
    public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
    /// 
    /// Calculates a perpendicular  to the given .
    /// 
    /// The input .
    /// A  perpendicular to the input .
    public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
    /// 
    /// Rotates a  by the specified angle (in radians).
    /// 
    /// The  to rotate.
    /// The angle to rotate by, in radians.
    /// The rotated .
    public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(Math.Cos(angleInRadian) * vector.X - Math.Sin(angleInRadian) * vector.Y, Math.Sin(angleInRadian) * vector.X + Math.Cos(angleInRadian) * vector.Y);
    /// 
    /// Returns the component-wise minimum of two s.
    /// 
    /// The first .
    /// The second .
    /// The  containing the minimum components from both input s.
    public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
    /// 
    /// Returns the component-wise maximum of two s.
    /// 
    /// The first .
    /// The second .
    /// The  containing the maximum components from both input s.
    public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
    /// 
    /// Clamps each component of a  between the corresponding component of two other s.
    /// 
    /// The  to clamp.
    /// The  representing the minimum values for each component.
    /// The  representing the maximum values for each component.
    /// A  with each component clamped between the corresponding components of the min and max s.
    public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
    /// 
    /// Performs linear interpolation between two s.
    /// 
    /// The starting  (t = 0).
    /// The ending  (t = 1).
    /// The interpolation parameter.
    /// The interpolated .
    public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
    /// 
    /// Calculates the cross product of two s.
    /// 
    /// The first .
    /// The second .
    /// The cross product of the two s.
    public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
    /// 
    /// Calculates the angle between two s.
    /// 
    /// The first .
    /// The second .
    /// The angle between the two s in radians.
    public static float Angle(Vector2D left, Vector2D right) => Math.Acos(Dot(left, right) / (Length(left) * Length(right)));
    /// 
    /// Calculates the dot product of two s.
    /// 
    /// The first .
    /// The second .
    /// The dot product of the two s.
    public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
    /// 
    /// Determines the orientation of three points represented by s.
    /// 
    /// The first .
    /// The second .
    /// The third .
    /// 
    /// 0 - Collinear.
    /// 1 - Clockwise.
    /// 2 - Counterclockwise.
    /// 
    public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
    {
        Vector2D leftToMiddle = left.FromTo(middle);
        Vector2D middleToRight = middle.FromTo(right);
        float value = leftToMiddle.Y * middleToRight.X -
                    leftToMiddle.X * middleToRight.Y;
        if (value > 0) return 1;
        if (value < 0) return 2;
        return 0;
    }
    /// 
    /// Transforms the  using the specified .
    /// 
    /// The  to transform.
    /// The  to apply.
    /// The transformed .
    public static Vector2D Transform(Vector2D vector, ITransform2D transform)
        => vector.Scale(transform.Scale)
                 .Rotate(transform.Rotation * Math.DegreeToRadian)
                 .Add(transform.Position);
    /// 
    /// Checks if two s are approximately equal within a specified epsilon range.
    /// 
    /// The first .
    /// The second .
    /// The epsilon range.
    ///  if the s are approximately equal; otherwise, .
    public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
        => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
    /// 
    /// Determines whether the specified object is equal to the current .
    /// 
    /// The object to compare with the current .
    ///  if the specified object is equal to the current ; otherwise, .
    public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
    public bool Equals(Vector2D other) => this == other;
    /// 
    /// Generates a hash code for the .
    /// 
    /// A hash code for the .
    public override int GetHashCode() => System.HashCode.Combine(X, Y);
    /// 
    /// Converts the  to its string representation.
    /// 
    /// A string representation of the .
    public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
}
/// 
/// Provides extension methods for  type.
/// 
public static class Vector2DExtensions
{
    /// 
    public static float Length(this Vector2D vector) => Vector2D.Length(vector);
    /// 
    public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
    /// 
    public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
    /// 
    public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
    /// 
    public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
    /// 
    public static Vector2D Divide(this Vector2D vector, float value) => Vector2D.Divide(vector, value);
    /// 
    public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
    /// 
    public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
    /// 
    public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
    /// 
    public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
    /// 
    public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
    /// 
    public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
    /// 
    public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
    /// 
    public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
    /// 
    public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
    /// 
    public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
    /// 
    public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
    /// 
    public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
    /// 
    public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
    /// 
    public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
    /// 
    public static Vector2D Transform(this Vector2D vector, ITransform2D transform) => Vector2D.Transform(vector, transform);
    /// 
    public static Vector2D Transform(this ITransform2D transform, Vector2D vector) => Vector2D.Transform(vector, transform);
    /// 
    public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
}