package ch.epfl.isochrone.geo;

import static java.lang.Math.*;
/**
 * Un point dans le système de coordonnées OSM. 
 * 
 * @author Charles Thiebaut (238935) 
 * @author Arnaud Delamare (234841)
 *
 */
public final class PointOSM {

   private final int zoom; 
   private final double x; 
   private final double y; 
 
 /**
 * @param zoom 
 *          Le zoom auquel la carte se situe. 
 * @param x
 *         Coordonnée cartésienne du point.
 * @param y
 *         Coordonnée cartésienne du point.
 * @throws IllegalArgumentException
 *         si les différents composantes (zooom, x, y) sont negatives ou en dehors du domaine conserné.                  
 */
 public PointOSM(int zoom, double x, double y)    {
       
       if(zoom < 0 || x < 0 || y < 0 || x > maxXY(zoom) || y > maxXY(zoom))
       {
           throw new IllegalArgumentException(); 
       }
       
       
       
       this.zoom = zoom; 
       this.x = x; 
       this.y = y; 
       
   }       
    
 /**
  * @param zoom 
  *          Le zoom auquel la carte se situe. 
  * @throws IllegalArgumentException
  *         si le zoom est negatif.
  * @return la taille de la carte          
  */
    public static int maxXY(int zoom)
    {
       if(zoom < 0)
       {
           throw new IllegalArgumentException(); 
       }
       
       return (int)pow(2,zoom+8);
    }
    
    public int zoom()
    {
        return zoom; 
    }
    
    
    public double x()
    {
        return x; 
    }
    
    public double y()
    {
        return y; 
    }
    
    /**
     * @return la position x arrondie en entier          
     */
    public int roundedX()
    {   
        return (int)Math.round(x); 
    }

    /**
     * @return la position y arrondie en entier          
     */
    public int roundedY()
    {   
        return (int)Math.round(y); 
    }
    
    /**
     * @param newZoom
     *        le nouveau zoom du point
     * 
     * @throws IllegalArgumentException
     *         si le nouveau zoom est negatif
     * 
     * @return le nouveau point en coordonnées OSM avec le nouveau zoom     
     */
    public PointOSM atZoom(int newZoom)
    {
        if(newZoom < 0)
        {
            throw new IllegalArgumentException(); 
        }
        
        double f = pow(2,newZoom-this.zoom);
        
        return new PointOSM(newZoom,x*f,y*f);
        
    }
    
    
    /**
     * @return le point OSM en coordonnée WGS84
     */
    public PointWGS84 toWGS84() 
    {
        double s = pow(2,zoom+8); 
        double longitude = (2*PI*this.x)/s - PI; 
        double latitude = atan(sinh(PI - (2*PI*this.y)/s)); 
        
        return new PointWGS84(longitude, latitude); 
    }
    
    /**
     * @return redéfinit la methode toString héritée la classe OBJET, et permet d'affiche le point de la manière lisible.
     */
    public String toString()
    {
        return "(" + zoom + ", " + x +", " + y + ")"; 
    }
}
