// Matrix.java
// This class implements the basic matrix operations for 3D graphics

package wonderlab.graphics.geometry;

public class Vector3D {

	double [] data;
	int size;
	
	// fcn to test Matrix on command-line
	public static void main (String argv[]) {

		Vector3D temp1 = new Vector3D(1,3,4);
		Vector3D temp2 = new Vector3D(2,7,-5);
		temp1.cross(temp2).print();
	}

	public Vector3D(double a, double b, double c, double d) {
		size = 4;
		data = new double[4];
		
		data[0] = a;
		data[1] = b;
		data[2] = c;
		data[3] = d;

	}

	public Vector3D(double a, double b, double c) {
		size = 3;
		data = new double[3];
		
		data[0] = a;
		data[1] = b;
		data[2] = c;

	}


	public Vector3D(int n) {
		size = n;
		data = new double[n];
		
		for (int i = 0; i < size; i++) {
			data[i] = 0;
		}
	}

	public Vector3D(Vector3D src) {

		size = 0;
		copy(src);
	
	}
		
	public void set(int i, double a) { 
		if ((i >=0) && (i < size)) {
			data[i] = a;
		} else {
			error("dimension violation, attempted to set element out-of-range at " + i);
		}
	}

	public double get(int i) { 
		if ((i >=0) && (i < size)) {
			return data[i]; 
		} else {
			error("dimension violation, attempted to read element out-of-range at " + i);
			return 0.0;
		}
	}
	
	public void add(Vector3D y) {

		int max = Math.min(size,y.size());
		
		for (int i = 0; i < max; i++) {
			data[i] +=  y.get(i);
		}
	}

	public void subtract(Vector3D y) {

		int max = Math.min(size(),y.size());
		
		for (int i = 0; i < max; i++) {
			data[i] -= y.get(i);
		}
	}
	
	public double dot(Vector3D y) {
		double temp = 0;
		
		int max = Math.min(y.size(),size);
		
		for (int i = 0; i < max; i++) {
			temp += data[i] * y.get(i);
		}
		return temp;
	}
	
	public void normalize() {
//		print("normalizing...");
		double length = length();
		if ((length != 0) && (length != 1)) {	
			for (int i = 0; i < size; i++) {
				data[i] = data[i] / length;
			}			
		}
//		print("...normalized");
	}
	
	public double length() {
		return Math.sqrt(dot(this));
	}
		
	public Vector3D scaleBy(double a) {
		for (int i = 0; i < size; i++) {
			data[i] *= a;
		}
		return this;
	}
	
	public int size() {
		return size;
	}

	public void componentMultiply(Vector3D y) {
		if (y.size() != size()) {
			System.out.println ("Component multiply failed.  Other vector has size " + y.size() + ", I have " + size());
		} else {
			for (int i = 0; i < size(); i++) {
				data[i] *= y.get(i);
			}
		}
	}
	
	public void copy(Vector3D src) {
		if (src.size() != size) {
			size = src.size();
			data = new double[size];
		}

		for (int i = 0 ; i < size ; i++) {
			data[i] = src.get(i);
		}
	}

	public Vector3D copy() {
		Vector3D temp = new Vector3D(this);
		return temp;
	}
	
	public double[] asArray() {
		double[] temp = new double[size];
		for (int i = 0; i < size; i++) {
			temp[i] = data[i];
		}
		return temp;
	}

	public void print(String s) {
		System.out.println(s);

		s = "";				
		for (int j = 0; j < size; j++) {
			s += this.get(j) + "\t";
		}
		System.out.println(s);
	}

	public void print() {
		String s = "";
		this.print(s);
	}
	
	public void error(String err) {
		System.out.println("Vector3D: " + err);
	}
	
	/// convert to Spherical coordinates
	// angle theta rotates in x-z plane
	// angle phi rotates in x-y plane
	// returns (length, theta, phi)
	public Vector3D asSpherical() {
		double l, phi, theta;
		Vector3D temp = new Vector3D(3);
		l = this.length();
		phi = Math.acos(data[1]/l);
		theta = Math.acos(data[2]/(l*Math.sin(phi)));
		temp.set(0, l);
		temp.set(1, theta);
		temp.set(2, phi);
		return temp;
	}
	
	// calculates U x V where V is passed as an argument
	public Vector3D cross(Vector3D V) {
		Vector3D U = this;
		if (V.size() != U.size()) {
			System.out.println ("Cross Product failed.  Other vector has size " + V.size() + ", I have " + U.size());
			return null;
		} else {
			return (new Vector3D(U.get(1)*V.get(2)-U.get(2)*V.get(1),U.get(2)*V.get(0) - U.get(0)*V.get(2),U.get(0)*V.get(1)-U.get(1)*V.get(0)));
		}
	}		
	
}
