package triangle;

import java.util.Arrays;

import exception.TriangleException;

public class Triangle {

	// /**
	// * Les longueurs des côtés du triangle.
	// */
	// private double a, b, c;

	/**
	 * Les longueurs des côtés du triangle triées dans l'ordre croissant.
	 */
	private double[] sides;

	public enum Type {
		EQUILATERAL, ISOSCELES, SCALENE
	};

	public enum Angle {
		ACUTE, RIGHT, OBTUSE
	};

	/**
	 * @param values
	 *            Les longueurs des côtés du triangle.
	 * @throws TriangleException
	 *             si le triangle n'est pas valide.
	 */
	public Triangle(double[] values) throws TriangleException {
		if (values.length == 3) {
			// for (int i = 0; i < 3; i++) {
			// if (values[i] <= 0) {
			// throw new TriangleException("");
			// }
			// }
			this.sides = values;
			Arrays.sort(this.sides);
			if (sides[0] + sides[1] <= this.sides[2]) {
				throw new TriangleException("");
			}
		} else {
			throw new TriangleException("");
		}
	}

	/**
	 * @param a
	 *            La longueur d'un côté du triangle.
	 * @param b
	 *            La longueur d'un autre côté du triangle.
	 * @param c
	 *            La longueur d'un autre côté du triangle.
	 * @throws TriangleException
	 *             si le triangle n'est pas valide.
	 */
	public Triangle(double a, double b, double c) throws TriangleException {
		this(new double[] { a, b, c });
	}

	/**
	 * @return Le type du triangle
	 */
	public Type getType() {
		if (this.isEquilateral()) {
			return Type.EQUILATERAL;
		} else if (this.isIsosceles()) {
			return Type.ISOSCELES;
		} else {
			return Type.SCALENE;
		}
	}

	/**
	 * @return Vrai si le triangle est isocèle, faux sinon.
	 */
	public boolean isIsosceles() {
		// return this.sides[0] == this.sides[1] || this.sides[0] ==
		// this.sides[2] || this.sides[1] == this.sides[2];
		return this.sides[0] == this.sides[1] || this.sides[1] == this.sides[2];
	}

	/**
	 * @return Vrai si le triangle est équilatéral, faux sinon.
	 */
	public boolean isEquilateral() {
		// return this.sides[0] == this.sides[1] && this.sides[1] ==
		// this.sides[2];
		return this.sides[0] == this.sides[2];
	}

	/**
	 * @return La nature du plus grand angle du triangle.
	 */
	public Angle getBiggerAngle() {
		// Théorème de Pythagore
		if (floor(this.sides[2] * this.sides[2], 8) == floor(this.sides[0]
				* this.sides[0] + this.sides[1] * this.sides[1], 8)) {
			return Angle.RIGHT;
		}
		double angle = this.getAngles()[2];
		if (angle < 90) {
			return Angle.ACUTE;
		} else {
			return Angle.OBTUSE;
		}
	}

	/**
	 * Arrondi d'un double avec n éléments après la virgule.
	 * 
	 * @param d
	 *            La valeur à arrondir.
	 * @param n
	 *            Le nombre de décimales à conserver.
	 * @return La valeur arrondie à n décimales.
	 */
	private static double floor(double d, int n) {
		// Pour faire des calculs de précision corrects avec des rééls, une
		// meilleure solution est d'utiliser la classe java.math.BigDecimal
		double p = Math.pow(10.0, n);
		return Math.floor((d * p) + 0.5) / p;
	}

	/**
	 * @return Les valeurs des angles (en degré) du triangle triées dans l'ordre
	 *         croissant.
	 */
	public double[] getAngles() {
		// Théorème de Al-Kashi
		double[] angles = new double[] {
				Math.toDegrees(Math.acos((this.sides[0] * this.sides[0]
						+ this.sides[1] * this.sides[1] - this.sides[2]
						* this.sides[2])
						/ (2 * this.sides[0] * this.sides[1]))),
				Math.toDegrees(Math.acos((this.sides[1] * this.sides[1]
						+ this.sides[2] * this.sides[2] - this.sides[0]
						* this.sides[0])
						/ (2 * this.sides[1] * this.sides[2]))),
				Math.toDegrees(Math.acos((this.sides[2] * this.sides[2]
						+ this.sides[0] * this.sides[0] - this.sides[1]
						* this.sides[1])
						/ (2 * this.sides[2] * this.sides[0]))) };
		Arrays.sort(angles);
		return angles;
	}

	/**
	 * @return Une chaîne de caractères indiquant le type du triangle et la
	 *         nature de son plus grand angle.
	 */
	public String toString() {
		return "triangle " + getType();
	}

}
