/* 
 
Copyright 2006 Rene Grothmann, modified by Eric Hakenholz

This file is part of C.a.R. software.

    C.a.R. is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3 of the License.

    C.a.R. is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
 
 package rene.zirkel.tools;

// file: Tracker.java

import java.awt.event.MouseEvent;
import java.util.Enumeration;
import java.util.Vector;

import rene.util.xml.XmlWriter;
import rene.gui.Global;
import rene.zirkel.ZirkelCanvas;
import rene.zirkel.constructors.ObjectConstructor;
import rene.zirkel.graphics.MyGraphics;
import rene.zirkel.graphics.TrackPainter;
import rene.zirkel.objects.ConstructionObject;
import rene.zirkel.objects.MoveableObject;
import rene.zirkel.objects.PointObject;
import rene.zirkel.objects.PrimitiveLineObject;
import rene.zirkel.structures.Coordinates;

public class Tracker extends ObjectConstructor implements TrackPainter {
	ConstructionObject PM;
	int PMax = 8, PN;
	ConstructionObject P;
	ConstructionObject PO[] = new ConstructionObject[PMax];
	Vector V = new Vector();
	Vector VO[] = new Vector[PMax];
	double X, Y, DX, DY;
	double XO[] = new double[PMax], YO[] = new double[PMax],
	DXO[] = new double[PMax], DYO[] = new double[PMax];
	boolean Started;
	boolean StartedO[] = new boolean[PMax];
	boolean Other;

	public Tracker(final ConstructionObject p, final ConstructionObject po[]) {
		super();
		P = p;
		PN = 0;
		for (int i = 0; i < po.length; i++) {
			if (i >= PMax || po[i] == null)
				break;
			PO[PN] = po[i];
			VO[i] = new Vector();
			PN++;
		}
	}

	public Tracker() {
		super();
	}

	@Override
	public void mousePressed(final MouseEvent e, final ZirkelCanvas zc) {
        
		final double x = zc.x(e.getX()), y = zc.y(e.getY());
		if (P == null) {
			P = zc.selectPoint(e.getX(), e.getY());
			if (P == null)
				P = zc.selectLine(e.getX(), e.getY());
			if (P == null)
				return;
			P.setSelected(true);
			zc.repaint();
			if (e.isShiftDown())
				Other = true;
			else
				Other = false;
			showStatus(zc);
		} else if (Other) {
			ConstructionObject P = zc.selectPoint(e.getX(), e.getY());
			if (P == null)
				P = zc.selectLine(e.getX(), e.getY());
			if (P != null) {
				P.setSelected(true);
				zc.repaint();
				PO[PN] = P;
				VO[PN] = new Vector();
				PN++;
			}
			if (!e.isShiftDown() || PN >= PMax)
				Other = false;
			showStatus(zc);
		} else {
			final ConstructionObject pm = zc.selectMoveableObject(e.getX(), e
					.getY());
			PM = pm;
			if (PM != null) {
				zc.clearSelected();
				PM.setSelected(true);
				((MoveableObject) PM).startDrag(x, y);
				zc.repaint();
				showStatus(zc);
			}
			Started = false;
		}
	}

	@Override
	public void mouseMoved(final MouseEvent e, final ZirkelCanvas zc,
			final boolean simple) {
		if (P == null || Other)
			zc.indicatePointsOrLines(e.getX(), e.getY());
		else if (PM == null)
			zc.indicateMoveableObjects(e.getX(), e.getY());
		else
			zc.clearIndicated();
	}

	@Override
	public void mouseDragged(final MouseEvent e, final ZirkelCanvas zc) {
		if (PM == null)
			return;
		((MoveableObject) PM).dragTo(zc.x(e.getX()), zc.y(e.getY()));
		zc.validate();
		track(zc);
		zc.repaint();
	}

	@Override
	public void mouseReleased(final MouseEvent e, final ZirkelCanvas zc) {
		if (PM == null)
			return;
		PM.setSelected(false);
		PM = null;
		zc.repaint();
		showStatus(zc);
	}

	@Override
	public void reset(final ZirkelCanvas zc) {
		zc.clearSelected();
		P = PM = null;
		PN = 0;
		V = new Vector();
		showStatus(zc);
		zc.repaint();
	}

	@Override
	public void showStatus(final ZirkelCanvas zc) {
		if (P == null || Other)
			zc.showStatus(Global.name("message.tracker.select"));
		else if (PM == null)
			zc.showStatus(Global.name("message.tracker.selectpoint"));
		else
			zc.showStatus(Global.name("message.tracker.move"));
	}

	public void track(final ZirkelCanvas zc) {
		if (P == null)
			return;
		if (P instanceof PointObject) {
			final PointObject p = (PointObject) P;
			if (p.valid())
				V.addElement(new Coordinates(p.getX(), p.getY()));
		} else if (P instanceof PrimitiveLineObject) {
			final PrimitiveLineObject L = (PrimitiveLineObject) P;
			if (L.valid()) {
				if (!Started) {
					X = L.getX();
					Y = L.getY();
					DX = L.getDX();
					DY = L.getDY();
					Started = true;
				} else {
					double x, y, dx, dy;
					x = L.getX();
					y = L.getY();
					dx = L.getDX();
					dy = L.getDY();
					final double det = dx * DY - dy * DX;
					if (Math.sqrt(Math.abs(det)) > 1e-9) {
						final double a = (-(X - x) * DY + DX * (Y - y))
						/ (-det);
						V.addElement(new Coordinates(x + a * dx, y + a * dy));
					}
					X = x;
					Y = y;
					DX = dx;
					DY = dy;
				}
			}
		}
		for (int i = 0; i < PN; i++) {
			if (PO[i] == null || !PO[i].valid())
				continue;
			if (PO[i] instanceof PointObject) {
				final PointObject p = (PointObject) PO[i];
				VO[i].addElement(new Coordinates(p.getX(), p.getY()));
			} else if (PO[i] instanceof PrimitiveLineObject) {
				final PrimitiveLineObject L = (PrimitiveLineObject) PO[i];
				if (!StartedO[i]) {
					XO[i] = L.getX();
					YO[i] = L.getY();
					DXO[i] = L.getDX();
					DYO[i] = L.getDY();
					StartedO[i] = true;
				} else {
					double x, y, dx, dy;
					x = L.getX();
					y = L.getY();
					dx = L.getDX();
					dy = L.getDY();
					final double det = dx * DYO[i] - dy * DXO[i];
					if (Math.sqrt(Math.abs(det)) > 1e-9) {
						final double a = (-(XO[i] - x) * DYO[i] + DXO[i]
						                                              * (YO[i] - y))
						                                              / (-det);
						VO[i]
						   .addElement(new Coordinates(x + a * dx, y + a
								   * dy));
					}
					XO[i] = x;
					YO[i] = y;
					DXO[i] = dx;
					DYO[i] = dy;
				}
			}
		}
	}

	public Enumeration elements() {
		return V.elements();
	}

	public void paint(final MyGraphics g, final ZirkelCanvas zc) {
		if (P == null)
			return;
		Coordinates C;
		Enumeration e = V.elements();
		g.setColor(P);
		double c0, r0, c, r;
		final int maxd = zc.width() / 20;
		if (e.hasMoreElements()) {
			C = (Coordinates) e.nextElement();
			c0 = zc.col(C.X);
			r0 = zc.row(C.Y);
			while (e.hasMoreElements()) {
				C = (Coordinates) e.nextElement();
				c = zc.col(C.X);
				r = zc.row(C.Y);
				if (Math.abs(c0 - c) < maxd && Math.abs(r0 - r) < maxd)
					g.drawLine(c0, r0, c, r, P);
				else
					g.drawLine(c0, r0, c0, r0, P);
				c0 = c;
				r0 = r;
			}
		}
		for (int i = 0; i < PN; i++) {
			if (PO[i] == null)
				continue;
			g.setColor(PO[i]);
			e = VO[i].elements();
			if (e.hasMoreElements()) {
				C = (Coordinates) e.nextElement();
				c0 = zc.col(C.X);
				r0 = zc.row(C.Y);
				while (e.hasMoreElements()) {
					C = (Coordinates) e.nextElement();
					c = zc.col(C.X);
					r = zc.row(C.Y);
					if (Math.abs(c0 - c) < maxd && Math.abs(r0 - r) < maxd)
						g.drawLine(c0, r0, c, r, PO[i]);
					else
						g.drawLine(c0, r0, c0, r0, PO[i]);
					c0 = c;
					r0 = r;
				}
			}
		}
	}

	public void validate(final ZirkelCanvas zc) {
	}

	public void save(final XmlWriter xml) {
		if (P == null)
			return;
		xml.startTagStart("Track");
		xml.printArg("track", P.getName());
		for (int i = 0; i < PN; i++) {
			xml.printArg("track" + i, PO[i].getName());
		}
		if (PM != null)
			xml.printArg("move", PM.getName());
		xml.finishTagNewLine();
	}

	@Override
	public boolean useSmartBoard() {
		return false;
	}
}
