package physics;

import math.Maths;
import math.matrix.Matrix;
import math.matrix.Vector;
import awt.Draw;

public class Projectile extends Trajectory{
	
	enum Method{ANALYTIC, ANALYTICT, EULER}
	
	/**
	 * starting angle
	 */
	double alpha0;
	/**
	 * 
	 */
	double k;
	
	public Matrix motion(){
		alpha0 = 50;
		k = 0.04;
		return motion(Method.EULER);
	}

	/**
	 * The motion of a projectile in an uniform gravitational field
	 * @param met <br>
	 * ANALYTIC/ANALYTICT: The ideal case of motion<br />
	 * EULER: calculate with Euler method 
	 * @return 
	 * ANALYTIC returns y(x) -> x and y<br />
	 * ANALYTICT and EULER returns x(t), y(t)<br />
	 */
	public Matrix motion(Method met){
		double tend = 2.5;
		double dt = 0.01;
		int n = (int)(tend/dt);
		
		Vector t = Vector.fill(0,dt,tend);
		double[] x = new double[n+1];
		double[] u = new double[n+1];
		double[] z = new double[n+1];
		double[] w = new double[n+1];

		int i;
		switch(met){
		case ANALYTIC:
			for(i=0; i<n; i++){
				x[i] = t.get(i)*10; // v0*i*dt*WMath.cosd(a0);
				z[i] = -g/(2*vv0.x()*vv0.x())*x[i]*x[i]
						+ Maths.tand(alpha0)*x[i] + vx0.z();
			}
			break;
		case ANALYTICT:
			for(i=0; i<n; i++){
				x[i] = vv0.x()*t.get(i);
				z[i] = z(t.get(i));
			}
			break;
		case EULER:
			x[0] = vx0.x();
			u[0] = vv0.x();
			z[0] = vx0.z();
			w[0] = vv0.z();
			double v;
			for(i=0; i<n; i++){
				v = Math.sqrt(u[i]*u[i]+w[i]*w[i]);
				x[i+1] = x[i] + dt*u[i];
				u[i+1] = u[i] - dt*(k/m*u[i]*v);
				z[i+1] = z[i] + dt*w[i];
				w[i+1] = w[i] - dt*(k/m*w[i]*Math.sqrt(u[i]*u[i]+w[i]*w[i]) + g);
			}
			break;
		default:
			break;
		}
		return new Matrix(x,z);
	}
	
	/**
	 * ANALYTIC
	 * @return z component of shot at time t 
	 */
	public double z(double t){
		return vv0.z()*t-g/2*t*t+vx0.z();
	}
	
	/**
	 * ANALYTIC
	 * @return T duration of shot 
	 */
	public double T(){
		return (vv0.z()+Math.sqrt(vv0.z()*vv0.z()+2*g*vx0.z()))/g;
	}
	
	/**
	 * ANALYTIC
	 * @return W width of shot 
	 */
	public double W(){
		return vv0.x()*T();
	}
	
	/**
	 * ANALYTIC
	 * @return H max height of shot 
	 */
	public double H(){
		return vx0.z() + vv0.z()*vv0.z()/2/g;
	}
	
	/**
	 * ANALYTIC
	 * @param t time
	 * @return v velocity at time t
	 */
	public double v(double t){
		return Math.sqrt(vv0.norm()*vv0.norm()-2*vv0.z()*g*t+g+g+t*t);
	}
	
	/**
	 * ANALYTIC
	 * @param t time vector
	 * @return v velocity v(t)
	 */
	public double[] v(double[] t){
		double[] v = new double[t.length];
		int i;
		for(i=0; i<t.length; i++)
			v[i] = v(t[i]);
		return v;
	}
	
	/**
	 * ANALYTIC
	 * @param t time vector
	 * @return angle alpha at time t
	 */
	public double alpha(double t){
		return Maths.atand(vv0.z()-g*t/vv0.x());
	}
	
	/**
	 * ANALYTIC
	 * @param t time vector
	 * @return a angle a(t)
	 */
	public double[] alpha(double[] t){
		double[] a = new double[t.length];
		int i;
		for(i=0; i<t.length; i++)
			a[i] = alpha(t[i]);
		return a;
	}
	
	/**
	 * ANALYTIC
	 * @param t time vector
	 * @return Ekin(t) kinetic energy
	 */
	public double[] Ekin(double[] t){
		double[] Ekin = new double[t.length];
		int i;
		for(i=0; i<t.length; i++)
			Ekin[i] = m/2*v(t[i])*v(t[i]);
		return Ekin;
	}
	
	/**
	 * ANALYTIC
	 * @param t time vector
	 * @return Epot(t) potential energy
	 */
	public double[] Epot(double[] t){
		double[] Epot = new double[t.length];
		int i;
		for(i=0; i<t.length; i++)
			Epot[i] = m*g*z(t[i]);
		return Epot;
	}
	
	/**
	 * ANALYTIC
	 * @return E energy
	 */
	public double E(){
		return m/2*vv0.norm()*vv0.norm()+m*g*vx0.z();
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Projectile p = new Projectile();
		Matrix projectileE = p.motion(); 
		Matrix projectileA = p.motion(Method.ANALYTIC);
		Matrix projectileAT = p.motion(Method.ANALYTICT); 
//		System.out.println(projectileM);
//		double[] X = projectileM.getN(0);
//		double[] Y = projectileM.getN(1);
//		Draw projectileA = new Draw(X,Y,"Projectile with Euler method");
//		projectileA.animate();
		Matrix X = new Matrix(3,projectileA.getM());
		Matrix Y = new Matrix(3,projectileA.getM());
		X.setM(0, new Vector(projectileA.getN(0)));
		Y.setM(0, new Vector(projectileA.getN(1)));
		X.setM(1, new Vector(projectileE.getN(0)));
		Y.setM(1, new Vector(projectileE.getN(1)));
		X.setM(2, new Vector(projectileAT.getN(0)));
		Y.setM(2, new Vector(projectileAT.getN(1)));
		Draw projectile = new Draw("Projectile with Euler method");
		projectile.animate(X,Y);
		projectile.markX(p.W());
		projectile.markY(p.H());
	}

}
