package math.geometry;

import java.text.DecimalFormat;

import math.matrix.Vector;

/**
 * @author Daniel Weschke
 */
public class Point {
	protected double[] point;
	protected int n;
	
	/**
	 * empty point.
	 */
	public Point(int n){
		this.n = n;
		point = new double[n];
	}
	
	/**
	 * new point.
	 * @param x x-coordinate
	 * @param y y-coordinate
	 * @param z z-coordinate
	 */
	public Point(double x, double y, double z){
		n = 3;
		point = new double[]{x, y, z};
	}
	
	/**
	 * new point.
	 * @param p n-dimensional point
	 */
	public Point(double... p){
		n = p.length;
		point = p;
	}
	
	/**
	 * Copy constructor.
	 * @param a the point to copy
	 */
	public Point(Point a){
		this(a.get());
	}
	
	public Point create(int n){
		if(this instanceof Point2D) return new Point2D();
		else return new Point(n);
	}
	
	/**
	 * Create random point.
	 */
	public static Point random(int n){
		double[] point = new double[n];
		int i;
		for(i=0; i<n; i++)
			point[i] = Math.random();
        return new Point(point);
	}
	
	/**
	 * Get coordinates.
	 */
	public double[] get(){ return point; }
	
	/**
	 * Get coordinate.
	 * @param i index of coordinate (first: 0)
	 */
	public double get(int i){ return point[i]; }
	
	/**
	 * Set coordinate.
	 * @param i index of coordinate (first: 0)
	 * @param a value
	 */
	public void set(int i, double a){ point[i] = a; }
	
	/**
	 * Get x-coordinate.
	 * @return x-coordinate
	 */
	public double x(){ return get(0); }
	
	/**
	 * Get y-coordinate.
	 * @return y-coordinate
	 */
	public double y(){ return get(1); }
	
	/**
	 * Get z-coordinate.
	 * @return z-coordinate
	 */
	public double z(){ return get(2); }
	
	/**
	 * Get dimension.
	 * @return dimansion
	 */
	public int n(){ return n; }

	/**
	 * Get distance between origin and point.
	 * @return distance
	 */
	public double r(){
		int i;
		double squares = 0;
		for(i=0; i<n; i++)
			squares += (point[i]*get(i));
		return Math.sqrt(squares);
	}
	
	public double theta3d(){ return Math.atan2(y(), x()); }
	public double phi3d(){ return Math.acos(z()/r()); }
	
	/**
	 * Displace point.
	 * @param a the displacement vector
	 * @return moved point
	 */
	public Point displace(Vector a){
		int i;
		Point point = create(n);
		for(i=0; i<n; i++)
			point.set(i,get(i)+a.get(i));
		return point;
	}
	
	/**
	 * Displacement vector between this point and another one.
	 * @param a other point
	 * @return displacement vector
	 */
	public Vector displace(Point a){
		int i;
		double[] point = new double[n];
		for(i=0; i<n; i++)
			point[i] = a.get(i)-get(i);
		return new Vector(point);
	}
	
	/**
	 * Euclidean distance between this point and another.
	 * @param a other point
	 * @return distance
	 */
	public double distance(Point a){
		return displace(a).norm();
	}
	
	/**
	 * Euclidean distance between this point and another.
	 * @param x component of other point
	 * @param y component of other point
	 * @return distance
	 */
	public double distance(double x, double y){
		return displace(new Point(x, y)).norm();
	}
	
	public boolean equals(Object obj){
		int i;
		Point point = (Point) obj;
		for(i=0; i<n; i++)
			if(get(i) != point.get(i)) return false;
		return true;
	}
	
	/**
	 * String of the point.
	 * Format: (x1;x2;...;xn)
	 */
	@Override
	public String toString(){
		int i;
		DecimalFormat df = new DecimalFormat("#.##");
		String string = "(";
		for(i=0; i<n; i++){
			string += i==0?"":"; ";
			string += df.format(get(i));
		}
		string += ")";
		return string;
	}
	
	public void println(){
		System.out.println(this);
	}
	
}
