package net.willware.Orbits;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * Created by IntelliJ IDEA.
 * User: wware
 * Date: Oct 16, 2004
 * Time: 8:50:17 AM
 */
public class PointMass implements SimObject {
	private String name;
	private double mass;
	private Vector force;
	private Vector position, velocity, acceleration;
	private Vector nextposition, oldposition = null;
	private Vector crashSite = null;

	public double minVelocity = System.INFINITY;
	public double maxVelocity = 0.0;
	public double previousVelocity = 0.0;
	public double maxAcceleration = 0.0;
	public double minAltitude = System.INFINITY;
	public double maxAltitude = 0.0;
	public double previousAltitude = 0.0;
	public System mysystem;


	public PointMass(System s, String name, double mass,
	                 Vector position, Vector velocity,
	                 Color color) {
		mysystem = s;
		this.name = name;
		this.mass = mass;
		this.position = position;
		this.velocity = velocity;
		init();
		setColor(color);
	}
	protected void init() {
		xmin = ymin = 1.0e20;
		xmax = ymax = -1.0e20;
		zeroForce();
	}
	public PointMass() {
		init();
	}
	public String toString() {
		return name;
	}
	protected void zeroForce() {
		force = new Vector(0.0, 0.0, 0.0);
	}
	public double getMass() { return mass; }
	public Vector getPosition() { return position; }
	public Vector getVelocity() { return velocity; }
	public Vector getAcceleration() { return acceleration; }
	public void addForce(Vector f) {
		force = force.add(f);
	}
	public void timeStep1() {
		if (crashSite == null) {
			acceleration = force.scale(1 / mass);
			if (oldposition == null)
				oldposition = position.subtract(velocity.scale(System.dt));
			// Verlet integration
			// http://www.fisica.uniud.it/~ercolessi/md/md/node21.html
			nextposition = position.scale(2).subtract(oldposition)
			        .add(acceleration.scale(System.dt * System.dt));
			velocity = nextposition.subtract(position).scale(1 / System.dt);
			double a = acceleration.length();
			if (a > maxAcceleration)
				maxAcceleration = a;
			double v = velocity.length();
			if (v > maxVelocity)
				maxVelocity = v;
			if (v < minVelocity)
				minVelocity = v;
			previousVelocity = v;
			double altitude = distance(System.Earth) - System.earthRadius;
			if (altitude < minAltitude)
				minAltitude = altitude;
			if (altitude > maxAltitude)
				maxAltitude = altitude;
			if (this != System.Earth && altitude <= 0.0) {
				// handle a crash
				write();
				crashSite = position.subtract(System.Earth.position);
				System.out.println(this + " crashed!");
			}
		}
	}
	public void timeStep2() {
		if (crashSite == null) {
			oldposition = position;
			position = nextposition;
			zeroForce();
		}
	}
	public void interact(SimObject otherguy) {
		if (otherguy != this && otherguy instanceof PointMass) {
			PointMass other = (PointMass) otherguy;
			Vector vr = other.position.subtract(position);
			double r = vr.length();
			double s = (System.GravitationalConstant * getMass() *
			        other.getMass()) / (r * r * r);
			addForce(vr.scale(s));
		}
	}
	public double distance(PointMass otherguy) {
		return position.subtract(otherguy.position).length();
	}

	public void write() {
		if (crashSite == null) {
			Vector v = position.subtract(System.Earth.position);
			addPoint(v.getX(), v.getY());
		}
	}


	double xmin, xmax, ymin, ymax;
	class Point {
		double x, y;
		Point(double x, double y) {
			this.x = x;
			this.y = y;
		}
	}
	private ArrayList lst = new ArrayList();
	public Iterator iterator() {
		return lst.iterator();
	}
	private Color color;
	public void setColor(Color c) { color = c; }
	public Color getColor() { return color; }
	public void addPoint(double x, double y) {
		Point p = new Point(x, y);
		lst.add(p);
		if (x < xmin) xmin = x;
		if (x > xmax) xmax = x;
		if (y < ymin) ymin = y;
		if (y > ymax) ymax = y;
	}
	public void growBoundingBox(Grapher gg) {
		gg.expandLimits(xmin, xmax, ymin, ymax);
	}
	public void draw(Graphics g, Grapher gg) {
		int x=0, y=0, oldx, oldy;
		g.setColor(getColor());
		Iterator pts = iterator();
		if (!pts.hasNext())
			throw new RuntimeException("no points in " + this);
		Point p = (Point) pts.next();
		Grapher.MyCanvas c = gg.canvas;
		oldx = c.getScreenX(p.x);
		oldy = c.getScreenY(p.y);
		while (pts.hasNext()) {
			p = (Point) pts.next();
			x = c.getScreenX(p.x);
			y = c.getScreenY(p.y);
			g.drawLine(oldx, oldy, x, y);
			oldx = x;
			oldy = y;
		}
		if (crashSite != null) {
			x = c.getScreenX(crashSite.getX());
			y = c.getScreenY(crashSite.getY());
			g.setColor(Color.RED);
			g.drawLine(x - 5, y, x + 5, y);
			g.drawLine(x, y + 5, x, y - 5);
			g.drawLine(x - 4, y - 4, x + 4, y + 4);
			g.drawLine(x - 4, y + 4, x + 4, y - 4);
		}
	}
}
