package simogl.geometry;

/**
 * This class represents a point (or rather vector) in 3-dimensional space. In openGL terms
 * you would call it a vertex. 
 * @author Frank Bruns
 *
 */
public class Point3D
{
	private float[] point = new float[3];

	/**
	 * Creates a new point (vertex) in three-dimensional space
	 * @param x x-coordinate of the 3D point
	 * @param y y-coordinate of the 3D point
	 * @param z z-coordinate of the 3D point
	 */
	public Point3D(float x, float y, float z)
	{
		this.point[0] = x;
		this.point[1] = y;
		this.point[2] = z;
	}
	
	public Point3D(Point3D point)
	{
		this.point[0] = point.getX();
		this.point[1] = point.getY();
		this.point[2] = point.getZ();
	}
	
	/**
	 * Creates a new point (vertex) in three-dimensional space that has
	 * the coordinates (0,0,0);
	 *
	 */
	public Point3D()
	{
		this.point[0] = 0f;
		this.point[1] = 0f;
		this.point[2] = 0f;
		
	}
	
	/**
	 * Sets the point to the coordinates of the given point
	 * @param point point with new coordinates
	 */
	public void setTo(Point3D point)
	{
		this.point[0] = point.getX();
		this.point[1] = point.getY();
		this.point[2] = point.getZ();
	}
	
	/**
	 * Sets the point to new coordinates
	 * @param x new x-coordinate
	 * @param y new y-coordinate
	 * @param z new z-coordinate
	 */
	public void setTo(float x, float y, float z)
	{
		this.point[0] = x;
		this.point[1] = y;
		this.point[2] = z;
	}
	
	/**
	 * Checks if this point is equal to the given point. If the coordinates
	 * of the two points are the same "true" is returned, else "false".
	 * @param point
	 * @return "true" if equal, "false" if not equal
	 */
	public boolean isEqualTo(Point3D point)
	{
		if(this.point[0] == point.getX() && this.point[1] == point.getY() && this.point[2] == point.getZ())
		{
			return true;
		}
		else return false;
	}
	
	/**
	 * Checks if this point is equal to the specified coordinates
	 * @param x x-coordinate
	 * @param y y-coordinate
	 * @param z z-coordinate
	 * @return "true" if equal, "false" if not equal
	 */
	public boolean isEqualTo(float x, float y, float z)
	{
		if(this.point[0] == x && this.point[1] == y && this.point[2] == z)
		{
			return true;
		}
		else return false;
	}
	
	/**
	 * Adds the given parameters to this point
	 * @param x x-coordinate
	 * @param y y-coordinate
	 * @param z z-coordinate
	 */
	public void add(float x, float y, float z)
	{
		this.point[0] += x;
		this.point[1] += y;
		this.point[2] += z;
	}
	
	/**
	 * Adds the specified point to this point
	 * @param point point to add
	 */
	public void add(Point3D point)
	{
		this.point[0] += point.getX();
		this.point[1] += point.getY();
		this.point[2] += point.getZ();
	}
	
	/**
	 * Subtracts the given parameters from this point
	 * @param x x-coordinate
	 * @param y y-coordinate
	 * @param z z-coordinate
	 */
	public void subtract(float x, float y, float z)
	{
		this.point[0] -= x;
		this.point[1] -= y;
		this.point[2] -= z;
	}
	
	/**
	 * Subtracts the specified point from this point
	 * @param point point to add
	 */
	public void subtract(Point3D point)
	{
		this.point[0] -= point.getX();
		this.point[1] -= point.getY();
		this.point[2] -= point.getZ();
	}
	
	/**
	 * Subtracts p1 from p2 and returns the result as a Point3D object
	 * @param p1 subtrahent
	 * @param p2 minuend
	 */
	public Point3D subtract(Point3D p1, Point3D p2)
	{
		Point3D result = new Point3D();
		
		result.setX(p1.getX() - p2.getX());
		result.setY(p1.getY() - p2.getY());
		result.setZ(p1.getZ() - p2.getZ());
		
		return result;
	}
	
	/**
	 * Multiplies the point by the specified scalar
	 * @param scalar scalar to multiply by
	 */
	public void multiply(float scalar)
	{
		this.point[0] *= scalar;
		this.point[1] *= scalar;
		this.point[2] *= scalar;
	}
	
	/**
	 * Divides the point by the specified scalar
	 * @param scalar scalar to divide by
	 */
	public void divide(float scalar)
	{
		this.point[0] /= scalar;
		this.point[1] /= scalar;
		this.point[2] /= scalar;
	}
	
	/**
	 * Returns the length of this point (or rather vector)
	 * @return length of the point/vector
	 */
	public float getLength()
	{
		float length = (float)Math.sqrt(this.point[0]*this.point[0] + this.point[1]*this.point[1]+ this.point[2]*this.point[2]);
		return length;
	}
	
	/**
	 * Returns the result of the cross product of two points/vectors 
	 * @param p1 point/vector 1
	 * @param p2 point/vector 2
	 * @return cross product of the two points/vectors
	 */
	public Point3D getCrossProduct(Point3D p1, Point3D p2)
	{
		
		Point3D crossProd = new Point3D();
		
		crossProd.setX(p1.getY()*p2.getZ() - p1.getZ()*p2.getY());
		crossProd.setY(p1.getZ()*p2.getX() - p1.getX()*p2.getZ());
		crossProd.setZ(p1.getX()*p2.getY() - p1.getY()*p2.getX());
		
		return crossProd;
	}
	
	/**
	 * Returns the dot product of the two points/vectors. The dot product
	 * is useful to find out whether two vectors are orthogonal to each 
	 * other, because that is the case, if the dot product is 0.
	 * @param p1 point/vector 1
	 * @param p2 point/vector 2
	 * @return dot product of the two points
	 */
	public float getDotProduct(Point3D p1, Point3D p2)
	{		
		return p1.getX()*p2.getX() + p1.getY()*p2.getY() + p1.getZ()*p2.getZ();
	}
	
	/**
	 * Sets this point/vector to the result of the cross product of the
	 * two specified points/vectors  
	 * @param p1 point/vector 1
	 * @param p2 point/vector 2
	 */
	public void setToCrossProduct(Point3D p1, Point3D p2)
	{
		this.point[0] = p1.getY()*p2.getZ() - p1.getZ()*p2.getY();
		this.point[1] = p1.getZ()*p2.getX() - p1.getX()*p2.getZ();
		this.point[2] = p1.getX()*p2.getY() - p1.getY()*p2.getX();
	}
	
	/**
	 * Converts this point (or rather vector) to a vector of length 1.
	 *
	 */
	public void normalize()
	{
		float x = this.point[0];
		float y = this.point[1];
		float z = this.point[2];
		
		x /= this.getLength();
		y /= this.getLength();
		z /= this.getLength();
		
		this.point[0] = x;
		this.point[1] = y;
		this.point[2] = z;
	}
	
	/**
	 * Returns the x-coordinate of the 3D point (vertex)
	 * @return x-coordinate
	 */
	public float getX()
	{
		return this.point[0];
	}
	
	/**
	 * Returns the y-coordinate of the 3D point (vertex)
	 * @return y-coordinate
	 */
	public float getY()
	{
		return this.point[1];
	}
	
	/**
	 * Returns the z-coordinate of the 3D point (vertex)
	 * @return z-coordinate
	 */
	public float getZ()
	{
		return this.point[2];
	}
	
	/**
	 * Sets the point's x-coordinate to the specified value
	 * @param x x-coordinate
	 */
	public void setX(float x)
	{
		this.point[0] = x;
	}
	
	/**
	 * Sets the point's y-coordinate to the specified value
	 * @param y y-coordinate
	 */
	public void setY(float y)
	{
		this.point[1] = y;
	}
	
	/**
	 * Sets the point's z-coordinate to the specified value
	 * @param z z-coordinate
	 */
	public void setZ(float z)
	{
		this.point[2] = z;
	}
	
	/**
	 * Returns the point (vertex) with its 3D coordinates (x,y,z)
	 * @return 3D point or rather vertex
	 */
	public float[] getPoint()
	{
		return this.point;
	}
	
	/**
	 * Returns a copy of this point
	 * @return a copy of this point
	 */
	public Point3D copy()
	{
		return new Point3D(this.point[0],this.point[1],this.point[2]);
	}
	
}
