using UnityEngine;

public static class Vector3Extensions {

    public static readonly Vector3 upright      = new Vector3(0.85090352f, 0.85090352f, 0.0f);
    public static readonly Vector3 upleft       = new Vector3(-0.85090352f, 0.85090352f, 0.0f);
    public static readonly Vector3 downright    = new Vector3(0.85090352f, -0.85090352f, 0.0f);
    public static readonly Vector3 downleft     = new Vector3(-0.85090352f, -0.85090352f, 0.0f);

    public static Vector2 ToVector2XZ(this Vector3 v){
        return new Vector2(v.x, v.z);
    }

    public static Vector2 ToVector2(this Vector3 v){
        return v;
    }

    public static float ForwardToTargetPositionXZAngle(this Vector3 position, Vector3 forward, Vector3 targetPosition){
        return position.ForwardToTargetPositionXZAngle(forward.ToVector2XZ(), targetPosition);
    }
    public static float ForwardToTargetPositionXZAngle(this Vector3 position, Vector2 forward, Vector3 targetPosition){
        Vector2 t = (targetPosition - position).ToVector2XZ().normalized;
        Vector2 f = forward.normalized;
        return Mathf.Atan2(t.Cross(f), t.Dot(f)) * Mathf.Rad2Deg;
    }
    public static float ForwardToVectorXZAngle(this Vector3 forward, Vector3 vector){
        return ForwardToVectorXZAngle(forward.ToVector2XZ(), vector);
    }
    public static float ForwardToVectorXZAngle(this Vector2 forward, Vector3 vector){
        Vector2 t = vector.ToVector2XZ().normalized;
        Vector2 f = forward.normalized;
        return Mathf.Atan2(t.Cross(f), t.Dot(f)) * Mathf.Rad2Deg;
    }

    public static Vector3 Center(this Vector3 v1, Vector3 v2){
        return v1 + (v2 - v1) * 0.5f;
    }

    public static float MagnitudeNotSqrt(this Vector3 v){
        return v.x * v.x + v.y * v.y + v.z * v.z;
    }

    public static bool NearCheckX(this Vector3 v1, Vector3 v2, float max){
        return (v1.x - v2.x) * (v1.x - v2.x) <= max * max;
    }
    public static bool NearCheckY(this Vector3 v1, Vector3 v2, float max){
        return (v1.y - v2.y) * (v1.y - v2.y) <= max * max;
    }
    public static bool NearCheckZ(this Vector3 v1, Vector3 v2, float max){
        return (v1.z - v2.z) * (v1.z - v2.z) <= max * max;
    }
    public static bool NearCheck(this Vector3 v1, Vector3 v2, float max){
        return (v1 - v2).MagnitudeNotSqrt() <= max * max;
    }
    public static bool NearCheck(this Vector3 v1, Vector3 v2, float min, float max){
        float dist = (v1 - v2).MagnitudeNotSqrt();
        return dist >= min * min && dist <= max * max;
    }

    public static Vector3 Mul(this Vector3 v1, Vector3 v2){
        return Vector3.Scale(v1, v2);
    }

    public static Vector3 Div(this Vector3 v1, Vector3 v2){
        return new Vector3(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z);
    }

    public static Vector3 Rotate(this Vector3 v, Vector3 eulerAngles){
        Vector3 z = v.RotateZDeg(eulerAngles.z);
        Vector3 x = z.RotateXDeg(eulerAngles.x);
        return x.RotateYDeg(eulerAngles.y);
    }

    public static Vector3 RotateXDeg(this Vector3 v, float angle){
        return v.RotateXRad(angle * Mathf.Deg2Rad);
    }
    public static Vector3 RotateXRad(this Vector3 v, float angle){
        Vector3 result = v;
        float cos = Mathf.Cos(angle);
        float sin = Mathf.Sin(angle);
        result.y = v.y * cos - v.z * sin;
        result.z = v.y * sin + v.z * cos;
        return result;
    }

    public static Vector3 RotateYDeg(this Vector3 v, float angle){
        return v.RotateYRad(angle * Mathf.Deg2Rad);
    }
    public static Vector3 RotateYRad(this Vector3 v, float angle){
        Vector3 result = v;
        float cos = Mathf.Cos(angle);
        float sin = Mathf.Sin(angle);
        result.z = v.z * cos - v.x * sin;
        result.x = v.z * sin + v.x * cos;
        return result;
    }

    public static Vector3 RotateZDeg(this Vector3 v, float angle){
        return v.RotateZRad(angle * Mathf.Deg2Rad);
    }
    public static Vector3 RotateZRad(this Vector3 v, float angle){
        Vector3 result = v;
        float cos = Mathf.Cos(angle);
        float sin = Mathf.Sin(angle);
        result.x = v.x * cos - v.y * sin;
        result.y = v.x * sin + v.y * cos;
        return result;
    }

    public static Vector3 FixIfNaN(this Vector3 v){
        if(float.IsNaN(v.x)){
            v.x = 0;
        }
        if(float.IsNaN(v.y)){
            v.y = 0;
        }
        if(float.IsNaN(v.z)){
            v.z = 0;
        }
        return v;
    }

    public static Vector3 SetX(this Vector3 v, float f){
        v.x = f;
        return v;
    }
    public static Vector3 SetY(this Vector3 v, float f){
        v.y = f;
        return v;
    }
    public static Vector3 SetZ(this Vector3 v, float f){
        v.z = f;
        return v;
    }
    public static Vector3 AddX(this Vector3 v, float f){
        v.x += f;
        return v;
    }
    public static Vector3 AddY(this Vector3 v, float f){
        v.y += f;
        return v;
    }
    public static Vector3 AddZ(this Vector3 v, float f){
        v.z += f;
        return v;
    }
    public static Vector3 MulX(this Vector3 v, float f){
        return v.SetX(v.x * f);
    }
    public static Vector3 MulY(this Vector3 v, float f){
        return v.SetY(v.y * f);
    }
    public static Vector3 MulZ(this Vector3 v, float f){
        return v.SetZ(v.z * f);
    }

    public static Vector3 Min(this Vector3 v1, Vector3 v2){
        if(v1.MagnitudeNotSqrt() < v2.MagnitudeNotSqrt()){
            return v1;
        }else{
            return v2;
        }
    }
    public static Vector3 Max(this Vector3 v1, Vector3 v2){
        if(v1.MagnitudeNotSqrt() > v2.MagnitudeNotSqrt()){
            return v1;
        }else{
            return v2;
        }
    }

    public static Vector3 Round (this Vector3 v1 , Vector3 v2){
        v1.x = Mathf.Round(v1.x + v2.x);
        v1.y = Mathf.Round(v1.y + v2.y);
        v1.z = Mathf.Round(v1.z + v2.z);
        return v1;
    }

    public static Vector3 RandomInsideCone(this Vector3 v, Transform t, float conicAngle){
        return v.RandomInsideCone(t.up, t.forward, conicAngle);
    }
    public static Vector3 RandomInsideCone(this Vector3 v, Vector3 up, Vector3 forward, float conicAngle){
        return Quaternion.AngleAxis(Random.Range(0f, 360f), forward) * Quaternion.AngleAxis(Random.Range(0f, conicAngle), up) * v;
    }
}

public static class Vector2Extensions {

    public static Vector3 ToVector3XZ(this Vector2 v){
        return new Vector3(v.x, 0.0f, v.y);
    }

    public static float Dot(this Vector2 v1, Vector2 v2){
        return v1.x * v2.x + v1.y * v2.y;
    }

    public static float Cross(this Vector2 v1, Vector2 v2){
        return v1.x * v2.y - v1.y * v2.x;
    }

    public static Vector2 RandomRotateDeg(this Vector2 v, float randomAngle){
        return v.RandomRotateRad(randomAngle * Mathf.Deg2Rad);
    }
    public static Vector2 RotateDeg(this Vector2 v, float angle){
        return v.RotateRad(angle * Mathf.Deg2Rad);
    }
    public static Vector2 RandomRotateRad(this Vector2 v, float randomAngle){
        return v.RotateRad(UnityEngine.Random.Range(-randomAngle, randomAngle));
    }
    public static Vector2 RotateRad(this Vector2 v, float angle){
        return new Vector2(
            v.x * Mathf.Cos(angle) - v.y * Mathf.Sin(angle),
            v.x * Mathf.Sin(angle) + v.y * Mathf.Cos(angle)
        );
    }

    public static float ForwardToTargetPositionAngle(this Vector2 position, Vector2 forward, Vector2 targetPosition){
        return forward.TargetAngle(targetPosition - position);
    }

    public static float TargetAngle(this Vector2 vector, Vector2 targetVector){
        Vector2 t = targetVector.normalized;
        Vector2 f = vector.normalized;
        return Mathf.Atan2(t.Cross(f), t.Dot(f)) * Mathf.Rad2Deg;
    }

    public static float MagnitudeNotSqrt(this Vector2 v){
        return v.x * v.x + v.y * v.y;
    }
    public static bool NearCheck(this Vector2 v1, Vector2 v2, float max){
        return (v1 - v2).MagnitudeNotSqrt() <= max * max;
    }
    public static Vector2 SetX(this Vector2 v, float f){
        v.x = f;
        return v;
    }
    public static Vector2 SetY(this Vector2 v, float f){
        v.y = f;
        return v;
    }
    
    public static Vector2 AddX(this Vector2 v, float f){
        v.x += f;
        return v;
    }
    public static Vector2 AddY(this Vector2 v, float f){
        v.y += f;
        return v;
    }
    public static Vector2 Add(this Vector2 v, float f){
        v.x += f;
        v.y += f;
        return v;
    }

    public static Vector2 Mul(this Vector2 v1, Vector2 v2){
        return Vector2.Scale(v1, v2);
    }

    public static Vector2 Div(this Vector2 v1, Vector2 v2){
        return new Vector2(v1.x / v2.x, v1.y / v2.y);
    }
}
