/* 
 
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.objects;

// file: SegmentObject.java

import rene.gui.Global;
import rene.util.xml.XmlWriter;
import rene.gui.Global;
import rene.zirkel.ZirkelCanvas;
import rene.zirkel.construction.Construction;
import rene.zirkel.construction.Count;
import rene.zirkel.graphics.MyGraphics;
import rene.zirkel.graphics.MyGraphics13;
import rene.zirkel.structures.Coordinates;


public class RayObject extends TwoPointLineObject {
	static Count N = new Count();

	public RayObject(final Construction c, final PointObject p1,
			final PointObject p2) {
		super(c, p1, p2);
		validate();
		updateText();
	}

	@Override
	public String getTag() {
		return "Ray";
	}

	@Override
	public int getN() {
		return N.next();
	}

	@Override
	public void setDefaults() {
		setShowName(Global.getParameter("options.line.shownames", false));
		setShowValue(Global.getParameter("options.line.showvalues", false));
		setColor(Global.getParameter("options.line.color", 0));
		setColorType(Global.getParameter("options.line.colortype", 0));
		setHidden(Cn.Hidden);
		setObtuse(Cn.Obtuse);
		setSolid(Cn.Solid);
		setLarge(Global.getParameter("options.line.large", false));
		setBold(Global.getParameter("options.line.bold", false));
	}

	@Override
	public void setTargetDefaults() {
		setShowName(Global.getParameter("options.line.shownames", false));
		setShowValue(Global.getParameter("options.line.showvalues", false));
		setColor(Global.getParameter("options.line.color", 0));
		setColorType(Global.getParameter("options.line.colortype", 0));
		setLarge(Global.getParameter("options.line.large", false));
		setBold(Global.getParameter("options.line.bold", false));
	}

	@Override
	public void updateText() {
		setText(text2(Global.name("text.ray"), P1.getName(), P2.getName()));
	}

	@Override
	public void validate() {
//        if ((P1==null)) {
//            System.out.println(this.getName()+" : "+P2.getName());
//        }
//        if ((P2==null)) {
//            System.out.println(this.getName()+" : "+P1.getName());
//        }

		if (!P1.valid() || !P2.valid()) {
			Valid = false;
			return;
		} else {
			Valid = true;
			X1 = P1.getX();
			Y1 = P1.getY();
			X2 = P2.getX();
			Y2 = P2.getY();
			// compute normalized vector in the direction of the line:
			DX = X2 - X1;
			DY = Y2 - Y1;
			R = Math.sqrt(DX * DX + DY * DY);
			if (R < 1e-10) {
				Valid = false;
				return;
			}
			DX /= R;
			DY /= R;
		}
	}

	@Override
	public void paint(final MyGraphics g, final ZirkelCanvas zc) {
		if (!Valid || mustHide(zc))
			return;
		// compute middle of the screen:
		final double xm = (zc.minX() + zc.maxX()) / 2, ym = (zc.minY() + zc
				.maxY()) / 2;
		// compute distance from middle to line:
		final double d = (xm - X1) * DY - (ym - Y1) * DX;
		// compute point with minimal distance
		final double x = xm - d * DY, y = ym + d * DX;
		// compute size of the screen
		final double a = Math.max(zc.maxX() - zc.minX(), zc.maxY() - zc.minY());
		if (Math.abs(d) > a)
			return;
		// compute distance from closest point to source
		final double b = (x - X1) * DX + (y - Y1) * DY;
		// compute the two visible endpoints
		k1 = b - a;
		k2 = b + a;
		k12valid = true;
		if (k1 < 0)
			k1 = 0;
		if (k1 >= k2)
			return;
		if (Partial && !zc.showHidden() && Dep != null) {
			final double dd = (zc.maxX() - zc.minX()) / 20;
			double dmin = -dd, dmax = R + dd;
			for (int i = 0; i < NDep; i++) {
				if (!Dep[i].valid() || Dep[i].mustHide(zc))
					continue;
				final double s = project(Dep[i].getX(), Dep[i].getY());
				if (s - dd < dmin)
					dmin = s - dd;
				else if (s + dd > dmax)
					dmax = s + dd;
			}
			if (k1 < dmin)
				k1 = dmin;
			if (k2 > dmax)
				k2 = dmax;
		}
		final double c1 = zc.col(X1 + k1 * DX), c2 = zc.col(X1 + k2 * DX), r1 = zc
		.row(Y1 + k1 * DY), r2 = zc.row(Y1 + k2 * DY);
		// paint:
		if (isStrongSelected() && g instanceof MyGraphics13) {
			((MyGraphics13) g).drawMarkerLine(c1, r1, c2, r2);
		}
		g.setColor(this);
		if (visible(zc)) {
			if (tracked())
				zc.UniversalTrack.drawTrackLine(this, c1, r1, c2, r2);
			g.drawLine(c1, r1, c2, r2, this);
		}
		final String s = getDisplayText();
		if (!s.equals("")) {
			g.setLabelColor(this);
			DisplaysText = true;
			double c = -b + a / 5;
			if (c < -a / 5)
				c = -a / 5;
			else if (c > a / 5)
				c = a / 5;
			if (c < -b + a / 10)
				c = -b + a / 10;
			if (KeepClose) {
				final double side = (YcOffset < 0) ? 1 : -1;
				drawLabel(g, s, zc, X1 + XcOffset * (X2 - X1), Y1 + XcOffset
						* (Y2 - Y1), side * DX, side * DY, 0, 0);
			} else
				drawLabel(g, s, zc, x + c * DX, y + c * DY, DX, DY, XcOffset,
						YcOffset);
		}
	}

	@Override
	public boolean canKeepClose() {
		return true;
	}

	@Override
	public void setKeepClose(final double x, final double y) {
		KeepClose = true;
		XcOffset = (x - X1) / R * DX + (y - Y1) / R * DY;
		YcOffset = (x - X1) / R * DY - (y - Y1) / R * DX;
	}

	@Override
	public String getDisplayValue() {

		return Global.getLocaleNumber(R, "lengths");
	}

	//
	// public String getDisplayValue ()
	// { return ""+round(R,ZirkelCanvas.LengthsFactor);
	// }

	@Override
	public boolean nearto(final int c, final int r, final ZirkelCanvas zc) {
		if (!displays(zc))
			return false;
		// compute point at c,r
		final double x = zc.x(c), y = zc.y(r);
		// compute distance from line
		double d = (x - X1) * DY - (y - Y1) * DX;
		// compute offset
		final double o = (x - X1) * DX + (y - Y1) * DY, o1 = (X2 - X1) * DX
		+ (Y2 - Y1) * DY;
		if (o1 > 0) {
			if (o < 0)
				d = Math.sqrt((x - X1) * (x - X1) + (y - Y1) * (y - Y1));
		} else {
			if (o > 0)
				d = Math.sqrt((x - X1) * (x - X1) + (y - Y1) * (y - Y1));
		}
		// test, if on visible part
		final double s = project(x, y);
		if (s < k1 || s > k2)
			return false;
		// scale in screen coordinates
		Value = Math.abs(zc.col(zc.minX() + d) - zc.col(zc.minX()));
		return Value < zc.selectionSize() * 2;
	}

	@Override
	public void printArgs(final XmlWriter xml) {
		xml.printArg("from", P1.getName());
		xml.printArg("to", P2.getName());
		super.printArgs(xml);
	}

	@Override
	public boolean contains(final double x, final double y) {
		final double a = (x - X1) * DX + (y - Y1) * DY;
		if (a < 1e-9)
			return false;
		return true;
	}

	@Override
	public double project(final double x, final double y) {
		final double h = super.project(x, y);
		if (h < 0)
			return 0;
		return h;
	}

	@Override
	public boolean equals(final ConstructionObject o) {
		if (!(o instanceof RayObject) || !o.valid())
			return false;
		final RayObject l = (RayObject) o;
		return equals(X1, l.X1) && equals(Y1, l.Y1) && equals(DX, l.DX)
		&& equals(DY, l.DY);
	}

	public static Coordinates intersect(final PrimitiveLineObject l1,
			final PrimitiveLineObject l2)
	// compute the intersection coordinates of two lines
	{
		final double det = -l1.DX * l2.DY + l1.DY * l2.DX;
		if (Math.abs(det) < 1e-10)
			return null;
		final double a = (-(l2.X1 - l1.X1) * l2.DY + (l2.Y1 - l1.Y1) * l2.DX)
		/ det;
		return new Coordinates(l1.X1 + a * l1.DX, l1.Y1 + a * l1.DY);
	}

	public static Coordinates intersect(final PrimitiveLineObject l,
			final PrimitiveCircleObject c)
	// compute the intersection coordinates of a line with a circle
	{
		double x = c.getX(), y = c.getY();
		final double r = c.getR();
		final double d = (x - l.X1) * l.DY - (y - l.Y1) * l.DX;
		if (Math.abs(d) > r + 1e-10)
			return null;
		x -= d * l.DY;
		y += d * l.DX;
		double h = r * r - d * d;
		if (h > 0)
			h = Math.sqrt(h);
		else
			h = 0;
		return new Coordinates(x + h * l.DX, y + h * l.DY, x - h * l.DX, y - h
				* l.DY);
	}

}
