/*-
 *  Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
 * 
 *  This file is part of CityDoctor2.
 *
 *  CityDoctor2 is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  CityDoctor2 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with CityDoctor2.  If not, see <https://www.gnu.org/licenses/>.
 */
package de.hft.stuttgart.citydoctor2.edge;


import java.util.ArrayList;
import java.util.List;

public class GmPlane {

	private Point3d x0;

	private UnitVector3d r1;
	private UnitVector3d r2;

	private UnitVector3d n;
	private double d;

	public GmPlane(Point3d point, UnitVector3d normalVector) {
		x0 = point;
		n = normalVector;
		d = new Vector3d(point).dot(n);

		r1 = UnitVector3d.of(n.getZ(), n.getX(), n.getY());
		r2 = n.cross(r1).toUnitVector();

		// r1 ist nicht in allen Faellen rechtwinklig zur Flaechennormalen
		// daher nochmal eine neu Berechnung;
		// ----------------------------------------------------------------
		r1 = n.cross(r2).toUnitVector();

	}

	public double getDistance() {
		return d;
	}

	public Vector3d getNormal() {
		return n;
	}

	public Point3d evaluate(Point2d point) {
		return evaluate(point.getX(), point.getY());
	}

	private Point3d evaluate(double x, double y) {
		Vector3d vectorU = r1.mult(x);
		Vector3d vectorV = r2.mult(y);
		return new Point3d(x0.plus(vectorU).plus(vectorV));
	}

	public GmStraight2d projectOn2dStraight(GmStraight crStraight) {
		Point2d start = project(crStraight.getOrigin());
		Point2d end = project(crStraight.getOrigin().plus(crStraight.getDir()));
		return new GmStraight2d(start, end);
	}

	public GmBoundedStraight2d projectOn2dStraight(GmBoundedStraight crBoundedStraight) {
		Point2d start = project(crBoundedStraight.getOrigin());
		Point2d end = project(crBoundedStraight.getTarget());
		return GmBoundedStraight2d.of(start, end);
	}

	public Point2d project(Point3d crPoint) {
		GmStraight firstAxis = new GmStraight(x0, r1);
		GmStraight secondAxis = new GmStraight(x0, r2);

		double u = firstAxis.project(crPoint).getParameter();
		double v = secondAxis.project(crPoint).getParameter();

		return new Point2d(u, v);
	}

	public GmStraight planeIntersection(GmPlane other) {
		// get plane normals and distances
		// -------------------------------
		Vector3d normal1 = getNormal();
		Vector3d normal2 = other.getNormal();
		double dist1 = getDistance();
		double dist2 = other.getDistance();

		// planes (normal vectors) parallel? -> good bye
		// ---------------------------------------------
		if (Vector3d.areParallel(normal1, normal2) || Vector3d.areAntiParallel(normal1, normal2)) {
			return null; // but rSuccess is false
		}

		// get direction and origin for straight
		// -------------------------------------
		Vector3d dir = normal1.cross(normal2);

		Matrix3d matrix = new Matrix3d();

		matrix.set(0, 0, normal1.getX());
		matrix.set(0, 1, normal1.getY());
		matrix.set(0, 2, normal1.getZ());

		matrix.set(1, 0, normal2.getX());
		matrix.set(1, 1, normal2.getY());
		matrix.set(1, 2, normal2.getZ());

		matrix.set(2, 0, dir.getX());
		matrix.set(2, 1, dir.getY());
		matrix.set(2, 2, dir.getZ());

		double[] b = new double[] { dist1, dist2, 0.0 };
		double[] org = new double[3];
		matrix.gauss(b, org);

		return new GmStraight(new Point3d(org), dir);
	}

	public Polygon2d projectOn2dPolygon(EdgePolygon cpPolygon) {
		HalfEdge startHE = cpPolygon.getFirstHalfEdge();
		HalfEdge currHE = startHE;
		int numHEs = cpPolygon.getNrHalfEdges();
		Coordinate2d[] coords = new Coordinate2d[numHEs];
		int i = 0;
		do {
			Coordinate3d coord = currHE.getStart();
			Point2d p = project(coord.getPoint());
			coords[i++] = new Coordinate2d(p);
			currHE = currHE.getNext();
		} while (currHE != startHE);

		List<HalfEdge2d> halfedges = new ArrayList<>();
		for (i = 0; i < numHEs; ++i) {
			HalfEdge2d he = new HalfEdge2d(coords[i], coords[(i + 1) % (numHEs)]);
			halfedges.add(he);
		}

		return new Polygon2d(halfedges);
	}

}
