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

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.jogamp.opengl.GL;

import de.hft.stuttgart.citydoctor2.math.Vector3d;

/**
 * Result of the tesselation process. Primitives can be converted to triangles.
 * 
 * @author Matthias Betz
 *
 */
public class Primitive {

	private static Logger logger = LogManager.getLogger(Primitive.class);

	private static final double AREA_EPSILON = 0.00001;

	private final int type;
	private final List<Integer> pIndices = new ArrayList<>();
	private final List<Vector3d> vertices;

	public Primitive(int type, List<Vector3d> vertices) {
		this.vertices = vertices;
		this.type = type;
	}

	public void fillListWithIndices(List<Integer> indices) {
		switch (type) {
		case GL.GL_TRIANGLES:
			for (int i = 0; i < pIndices.size(); i += 3) {
				addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 1), pIndices.get(i + 2), indices);
			}
			break;
		case GL.GL_TRIANGLE_STRIP:
			for (int i = 0; i < pIndices.size() - 2; i++) {
				if (i % 2 == 0) {
					addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 1), pIndices.get(i + 2), indices);
				} else {
					addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 2), pIndices.get(i + 1), indices);
				}
			}
			break;
		case GL.GL_TRIANGLE_FAN:
			Integer first = pIndices.get(0);
			for (int i = 0; i < pIndices.size() - 2; i++) {
				addToListIfAreaNonNull(first, pIndices.get(i + 1), pIndices.get(i + 2), indices);
			}
			break;
		default:
			throw new IllegalStateException("unknown type found: " + type);
		}
	}

	public void addIndex(Integer i) {
		pIndices.add(i);
	}

	private void addToListIfAreaNonNull(Integer i1, Integer i2, Integer i3, List<Integer> indices) {
		Vector3d v1 = vertices.get(i1);
		Vector3d v2 = vertices.get(i2);
		Vector3d v3 = vertices.get(i3);
		double area = Math.abs(calculateAreaOfTriangle(v1, v2, v3));
		if (area > AREA_EPSILON) {
			indices.add(i1);
			indices.add(i2);
			indices.add(i3);
		} else {
			logger.trace("Tesselator created degenerated triangle, ignoring");
		}
	}
	
	/**
	 * Calculates the area of a triangle given by the 3 points
	 * 
	 * @param v1 the first point of the triangle
	 * @param v2 the second point of the triangle
	 * @param v3 the last point of the triangle
	 * @return the area of the triangle
	 */
	private static double calculateAreaOfTriangle(Vector3d v1, Vector3d v2, Vector3d v3) {
		double a = v1.getDistance(v2);
		double b = v2.getDistance(v3);
		double c = v1.getDistance(v3);
		double s = 0.5d * (a + b + c);
		return Math.sqrt(s * (s - a) * (s - b) * (s - c));
	}

}
