/*-
 *  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;
import java.util.Objects;

public class EdgePolygon extends BaseEntity {

	private List<HalfEdge> halfEdges;
	
	public EdgePolygon(List<HalfEdge> halfEdges) {
		for (HalfEdge he : halfEdges) {
			addChild(he);
		}
		this.halfEdges = halfEdges;
	}

	public GmPlane getPlane() {
		Point3d midPoint = getMidPoint();

		List<HalfEdge> children = getChildren(HalfEdge.class);
		Vector3d averageNormalVector = new Vector3d(0.0, 0.0, 0.0);

		for (HalfEdge he : children) {

			Point3d start = he.getStart().getPoint();
			Point3d end = he.getEnd().getPoint();

			Vector3d mid2Start = start.minus(midPoint);
			Vector3d mid2End = end.minus(midPoint);

			averageNormalVector = averageNormalVector.plus(mid2Start.cross(mid2End));
		}

		UnitVector3d normalVector = averageNormalVector.toUnitVector();
		return new GmPlane(midPoint, normalVector);
	}

	private Point3d getMidPoint() {
		Point3d midPoint = new Point3d(0, 0, 0);
		List<HalfEdge> children = getChildren(HalfEdge.class);

		for (HalfEdge he : children) {
			midPoint = midPoint.plus(he.getStart().getPoint());
		}
		return (midPoint.div(children.size()));
	}

	public List<HalfEdge> getHalfEdges() {
		return halfEdges;
	}

	public List<Coordinate3d> getCoordinates() {
		List<Coordinate3d> coords = new ArrayList<>();
		HalfEdge firstHE = Objects.requireNonNull(getFirstHalfEdge());
		HalfEdge currHE = firstHE;
		do {
			coords.add(currHE.getStart());
			currHE = currHE.getNext();
		} while (currHE != firstHE);
		return coords;
	}

	public HalfEdge getFirstHalfEdge() {
		if (!halfEdges.isEmpty()) {
			return halfEdges.get(0);
		}
		return null;
	}

	/**
	 * Test whether the given point is lying in- or outside of the non self
	 * intersecting, planar polygon. It's believed, that point and polygon are lying
	 * in the same plane. <br>
	 * <br>
	 * It's assumed, that the polygon is non self intersecting and planar.
	 * Additionally it is assumed, that the point and the polygon lying in the same
	 * plane. The given point is projected on the plane of the polygon. If the point
	 * is lying on an edge of the polygon it's supposed that the point is inside of
	 * the polygon
	 * 
	 * @param rcPoint The point that should be checked
	 * 
	 * @return true, if the point is inside the polygon, false otherwise
	 * 
	 */
	public boolean isPointInsidePolygon(Point3d rcPoint, double eps) {
		// Project the coordinate of the point on the plane
		GmPlane plane = getPlane();
		Point2d projectedPoint = plane.project(rcPoint);
		Polygon2d pP = plane.projectOn2dPolygon(this);
		return pP.isPointInsidePolygon(projectedPoint, eps);
	}

	public int getNrHalfEdges() {
		return halfEdges.size();
	}

}
