package jomodel;

import jocode.*;

public class JOVector {
    public float x=0;
    public float y=0;
    public float z=0;
    
    
    /**
     * Create a 0,0,0 vector 
     */
    public JOVector()
    {
    }
    
    /**
     * Create a vector with the given xyz values
     */
    public JOVector(float xpos, float ypos, float zpos)
    {
        x = xpos;
        y = ypos;
        z = zpos;
    }
    
    /**
     * Create a vector from the given float[3] xyz values
     */
    public JOVector (float[] float3)
    {
        x = float3[0];
        y = float3[1];
        z = float3[2];
    }
    
    /**
     * Create a vector that duplicates the given vector
     */
    public JOVector(JOVector v)
    {
        x = v.x;
        y = v.y;
        z = v.z;
    }
    
    /**
     * Create a vector from point1 to point2
     */
    public JOVector(JOVector point1, JOVector point2)
    {
        x = point1.x - point2.x;
        y = point1.y - point2.y;
        z = point1.z - point2.z;
    }
    
    //========================================================================
    // Functions that operate on the vector (change the value of this vector)
    // These functions return "this", so can be chained together:
    //        JOVector a = new GLVector(b).mult(c).normalize()
    //========================================================================
    
    /**
     * Add a vector to this vector
     */
    public JOVector add(JOVector v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
        return this;
    }
    
    /**
     * Subtract vector from this vector
     */
    public JOVector sub(JOVector v)
    {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        return this;
    }
    
    /**
     * Multiply this vector by another vector
     */
    public JOVector mult(JOVector v)
    {
        x *= v.x;
        y *= v.y;
        z *= v.z;
        return this;
    }
    
    /**
     * Divide this vector by another vector
     */
    public JOVector div(JOVector v)
    {
        x /= v.x;
        y /= v.y;
        z /= v.z;
        return this;
    }
    
    /**
     * Add a value to this vector
     */
    public JOVector add(float n)
    {
        x += n;
        y += n;
        z += n;
        return this;
    }
    
    /**
     * Subtract a value from this vector
     */
    public JOVector sub(float n)
    {
        x -= n;
        y -= n;
        z -= n;
        return this;
    }
    
    /**
     * Multiply vector by a value
     */
    public JOVector mult(float n)
    {
        x *= n;
        y *= n;
        z *= n;
        return this;
    }
    
    /**
     * Divide vector by a value
     */
    public JOVector div(float n)
    {
        x /= n;
        y /= n;
        z /= n;
        return this;
    }
    
    /**
     * Normalize the vector (make its length 0).
     */
    public JOVector normalize()
    {
        float len = length();
        if (len==0) return this;
        float invlen = 1f/len;
        x *= invlen;
        y *= invlen;
        z *= invlen;
        return this;
    }
    
    /**
     * Reverse the vector
     */
    public JOVector reverse()
    {
        x = -x;
        y = -y;
        z = -z;
        return this;
    }
    
    /**
     * Return the length of the vector.
     */
    public float length()
    {
        return (float)Math.sqrt(x*x+y*y+z*z);
    }
    
    /**
     * Return a string representation of the vector
     */
    public String toString()
    {
        return new String ("<vector x="+x+" y="+y+" z="+z+">");
    }
    
    /**
     * Return a copy of the vector
     */
    public JOVector getClone()
    {
        return new JOVector(x,y,z);
    }
    
    /**
     * Return true if this vector has the same xyz values as the argument vector
     */
    public boolean equals(JOVector v)
    {
        return (v.x==x && v.y==y && v.z==z);
    }
    
    //==================================================================
    // Functions that perform binary operations (add, subtract, multiply
    // two vectors and return answer in a new vector)
    //==================================================================
    
    /**
     * Return a+b as a new vector
     */
    public static JOVector add(JOVector a, JOVector b)
    {
        return new JOVector(a.x+b.x, a.y+b.y, a.z+b.z);
    }
    
    /**
     * Return a-b as a new vector
     */
    public static JOVector sub(JOVector a, JOVector b)
    {
        return new JOVector(a.x-b.x, a.y-b.y, a.z-b.z);
    }
    
    /**
     * Return a*b as a new vector
     */
    public static JOVector mult(JOVector a, JOVector b)
    {
        return new JOVector(a.x*b.x, a.y*b.y, a.z*b.z);
    }
    
    /**
     * Return a/b as a new vector
     */
    public static JOVector div(JOVector a, JOVector b)
    {
        return new JOVector(a.x/b.x, a.y/b.y, a.z/b.z);
    }
    
    /**
     * Return the given vector multiplied by the given numeric value, as a new vector
     */
    public static JOVector multiply(JOVector v, float r) {
        return new JOVector(v.x*r, v.y*r, v.z*r);
    }
    
    /**
     * Return a new vector scaled by the given factor
     */
    public static JOVector scale(JOVector a, float f)
    {
        return new JOVector(f*a.x,f*a.y,f*a.z);
    }
    
    /**
     * Return the length of the given vector
     */
    public static float length(JOVector a)
    {
        return (float)Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z);
    }
    
    /**
     *  return the dot product of two vectors
     */
    public static float dotProduct(JOVector u, JOVector v)
    {
        return u.x * v.x + u.y * v.y + u.z * v.z;
    }
    
    /**
     * Return the normalized vector as a new vector
     */
    public static JOVector normalize(JOVector v) {
        float len = v.length();
        if (len==0) return v;
        float invlen = 1f/len;
        return new JOVector(v.x*invlen, v.y*invlen, v.z*invlen);
    }
    
    /**
     * Return the cross product of the two vectors, as a new vector.  The returned vector is 
     * perpendicular to the plane created by a and b.
     */
    public static JOVector crossProduct(JOVector a, JOVector b)
    {
        return vectorProduct(a,b).normalize();
    }
    
    /**
     * returns the normal vector of the plane defined by the a and b vectors
     */
    public static JOVector getNormal(JOVector a, JOVector b)
    {
        return vectorProduct(a,b).normalize();
    }
    
    /**
     * returns the normal vector from the three vectors
     */ 
    public static JOVector getNormal(JOVector a, JOVector b, JOVector c)
    {
        return vectorProduct(a,b,c).normalize();
    }
    
    /**
     * returns a x b
     */
    public static JOVector vectorProduct(JOVector a, JOVector b)
    {
        return new JOVector(a.y*b.z-b.y*a.z, a.z*b.x-b.z*a.x, a.x*b.y-b.x*a.y);
    }
    
    /**
     * returns (b-a) x (c-a)
     */ 
    public static JOVector vectorProduct(JOVector a, JOVector b, JOVector c)
    {
        return vectorProduct(sub(b,a),sub(c,a));
    }
    
    /**
     * returns the angle between 2 vectors
     */
    public static float angle(JOVector a, JOVector b)
    {
        a.normalize();
        b.normalize();
        return (a.x*b.x+a.y*b.y+a.z*b.z);
    }
    
    /**
     *  returns the angle between 2 vectors on the XZ plane.
     *  angle is 0-360 where the 0/360 divide is directly in front of the A vector
     *  Ie. when A is pointing directly at B, angle will be 0.
     *  If B moves one degree to the right, angle will be 1,
     *  If B moves one degree to the left, angle will be 360.
     *
     *  right side is 0-180, left side is 360-180
     */
    public static float angleXZ(JOVector a, JOVector b)
    {
        a.normalize();
        b.normalize();
        return (float)((Math.atan2(a.x*b.z-b.x*a.z, a.x*b.x+a.z*b.z) + Math.PI) * JOApp.PI_OVER_180);
    }
    
    /**
     *  returns the angle between 2 vectors on the XY plane.
     *  angle is 0-360 where the 0/360 divide is directly in front of the A vector
     *  Ie. when A is pointing directly at B, angle will be 0.
     *  If B moves one degree to the right, angle will be 1,
     *  If B moves one degree to the left, angle will be 360.
     *
     *  right side is 0-180, left side is 360-180
     */
    public static float angleXY(JOVector a, JOVector b)
    {
        a.normalize();
        b.normalize();
        return (float)((Math.atan2(a.x*b.y-b.x*a.y, a.x*b.x+a.y*b.y) + Math.PI) * JOApp.PI_UNDER_180);
    }
    
    /**
     * return a vector rotated the given number of degrees around the Y axis  
     */
    public static JOVector rotationVector(float degrees) {
        return new JOVector(
                (float)Math.sin(degrees * JOApp.PI_OVER_180),
                0,
                (float)Math.cos(degrees * JOApp.PI_OVER_180) );
    }
}
