package ch.epfl.isochrone.geo;

import static java.lang.Math.*;
import static ch.epfl.isochrone.math.Math.*;

/**
 * Un point dans le système de coordonnées WGS 84.
 * 
 * @author Charles Thiebaut (238935)
 * @author Arnaud Delamare (234841)
 * 
 */
public final class PointWGS84 {
    private final double longitude;
    private final double latitude;
    private final int RAYONTERRE = 6378137;

    /**
     * @param longitude
     *            La longitude du point GPS (en radians)
     * @param latitude
     *            La latitude du point GPS (en radians)
     * @throws IllegalArgumentException
     *             si la longitude ou la latitude est en dehors du domaine
     *             demandé
     */
    public PointWGS84(double longitude, double latitude) {

        if (longitude < -PI || longitude > PI) {
            throw new IllegalArgumentException();
        }

        if (latitude < -PI / 2 || latitude > PI / 2) {
            throw new IllegalArgumentException();
        }

        this.longitude = longitude;
        this.latitude = latitude;

    }

    public double latitude() {
        return this.latitude;
    }

    public double longitude() {
        return this.longitude;
    }

    /**
     * @return indique la distance entre deux point en coordonnée WSG84, entre
     *         celui passe en argument et celui sur lequel on applique la
     *         methode.
     */
    public double distanceTo(PointWGS84 that) {
        return 2
                * RAYONTERRE
                * asin(sqrt(haversin(this.latitude - that.latitude)
                        + cos(this.latitude) * cos(that.latitude)
                        * haversin(this.longitude - that.longitude)));
    }

    /**
     * @param zoom
     *            indique le zoom à laquelle la carte 2D ce situe.
     * 
     * @throws IllegalArgumentExceptions
     *             Si le zooom est négatif.
     * 
     * @return le point WSG84 en coordonnées OSM.
     */
    public PointOSM toOSM(int zoom) {
        if (zoom < 0) {
            throw new IllegalArgumentException();
        }

        double p = PI;
        double s = pow(2, (zoom + 8));
        double x = ((s) * (this.longitude + p)) / (2 * PI);
        double y = ((s) * (p - asinh(tan(this.latitude)))) / (2 * PI);

        return new PointOSM(zoom, x, y);
    }

    /**
     * @return redéfinit la methode toString héritée la classe OBJET, et permet
     *         d'affiche le point de la manière lisible, avec la latitude et la
     *         longitude en degré.
     */
    public String toString() {
        return "(" + toDegrees(this.longitude) + ", "
                + toDegrees(this.latitude) + ")";
    }

}
