/* ----------------------------------------
 * Origin  : GFI INFORMATIQUE 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 * ----------------------------------------
 */
package fr.tlse.irap.utils;

public class CoordConverter {

	// public constant
	public static final double rSUN = 6.96e5; 
	public static final double DRAW_SCALE = 1200000.0;
	// private constant
	private static final double AU = 149597870.700;

	/**
	 * convert distance from Astronomic Unity (AU) to km
	 * @param distanceInAU
	 */
	public static double toKm(double distanceInAU) {
		return distanceInAU * AU;
	}

	/**
	 * convert distance from km to Astronomic Unity (AU)
	 * @param distanceInKm
	 */
	public static double toAU(double distanceInKm) {
		return distanceInKm / AU;
	}

	/**
	 * convert distance from km to radial distance from Sun (rSun)
	 * @param distanceInKm
	 */
	public static double toRsun(double distanceInKm) {
		return distanceInKm / rSUN;
	}

	/**
	 * convert distance from radial distance from Sun (rSun) to km
	 * @param distanceInRsun
	 */
	public static double rsunToKm(double distanceInRsun) {
		return distanceInRsun * rSUN;
	}

	/**
	 * convert distance from km to ecliptic unity
	 * @param distanceInKm
	 * @return
	 */
	public static double toEcliptic(double distanceInKm) {
		return distanceInKm / DRAW_SCALE;
	}

	/**
	 * convert distance from ecliptic unity to km
	 * @param distanceInKm
	 * @return
	 */
	public static double eclipticToKm(double distanceInEcliptic) {
		return distanceInEcliptic * DRAW_SCALE;	
	}

	/**
	 * Compute spherical coordinates from cartesian coordinates
	 * @param x, y, z : cartesian coordinates
	 * @return double array with Lon, Lat, Radius. array[0] contains longitude in degrees [0°,360°[, 
	 * 		   array[1] contains latitude in degrees [-90°,+90°], 
	 * 		   array[2] contains radius in the same unit than x, y, z
	 */
	public static double[] toSpheric(double x, double y, double z) {
		double[] longLatRadius = new double[3];
		// longitude in radians
		double phi = Math.atan2(y, x); 
		// co-latitude in radians
		double theta = Math.atan2(Math.sqrt(x*x + y*y), z); 
		// longitude in degrees [-180,+180[
		longLatRadius[0] = Math.toDegrees(phi); 
		if (longLatRadius[0] < 0d) {
			// longitude in degrees [0,360[
			longLatRadius[0] += 360.0d; 
		}
		// co-latitude in degrees
		longLatRadius[1] = 90 - Math.toDegrees(theta);
		// Radius
		longLatRadius[2] = Math.sqrt(x*x + y*y + z*z);

		return longLatRadius;
	}

	/**
	 * Compute cartesian coordinates from spherical coordinates
	 * @param Lon, Lat, Radius : spheric coordinates
	 * @return double array with x, y, z 
	 */
	public static double[] toCartesian(double longitude, double latitude, double radius) {
		double phi = Math.toRadians(longitude);
		double theta = Math.toRadians(90.0 - latitude);
		double[] xyz = new double[3];
		
		xyz[0] = radius * Math.sin(theta) * Math.cos(phi);
		xyz[1] = radius * Math.sin(theta) * Math.sin(phi);
		xyz[2] = radius * Math.cos(theta);

		return xyz;
	}

	/**
	 * Transform spherical coordinates from Carrington to an Ecliptic
	 * coordinate's system (HAE, HEE) AttSun is the 0 meridian of sun expressed
	 * in ecliptic coordinate's system (HAE or HEE)
	 */
	public static double[] CarringtonToHAE(double[] longLat, double[] attSun) {
		double[] longLatResult = new double[2];
		longLatResult[0] = longLat[0] + attSun[0];
		longLatResult[1] = longLat[1] + attSun[1];
		if (longLatResult[0] > 360d) {
			longLatResult[0] -= 360.0d; // longitude in degrees [0,360[
		}	
		return longLatResult;
	}

	/**
	 * Transform spherical longitude from Carrington to an Ecliptic
	 * coordinate's system (HAE, HEE) AttLongSun is the 0 longitude meridian of sun expressed
	 * in ecliptic coordinate's system (HAE or HEE)
	 */
	public static double CarringtonToHAE(double longLat, double attLongSun) {
		double longLatResult;
		longLatResult = longLat + attLongSun;
		if (longLatResult > 360d) {
			longLatResult -= 360.0d; // longitude in degrees [0,360[
		}	
		return longLatResult;
	}

	/**
	 * Transform spherical coordinates from HAE to Carrington coordinate's
	 * system AttSun is the 0 meridian of sun expressed in HAE coordinate's
	 * system
	 */
	public static double[] HAEToCarrington(double[] longLat, double[] attSun) {
		double[] longLatResult = new double[2];
		longLatResult[0] = longLat[0] - attSun[0]; // longitude
		longLatResult[1] = longLat[1] - attSun[1]; // latitude
		if (longLatResult[0] < 0d) {
			longLatResult[0] += 360.0d; // longitude in degrees [0,360[
		}	

		return longLatResult;
	}

	/**
	 * Normalize a longitude to a longitude [0, 360[ degrees
	 * @param longitude expressed in degrees
	 * @return normalized longitude expressed in degrees [0, 360[ 
	 */
	public static double normalizeLongitude(double longitude) {
		double normLong = longitude;
		while (normLong >= 360.0) {
			normLong -= 360.0;
		}
		if (normLong < 0) {
			normLong += 360.0;
		}
		// treat limit case : 359.9999999999...
		if (normLong > 359.99 && normLong < 360.0) {
			normLong = 0.0;
		}
		return normLong;
	}

	/**
	 * Normalize a lalitude to a lalitude [-180, 180[ degrees
	 * @param lalitude expressed in degrees
	 * @return normalized lalitude expressed in degrees [-180, 180[ 
	 */
	public static double normalizeLatitude(double latitude) {
		double normLat = latitude;
		while (normLat >= 180) {
			normLat -= 360.0;
		}
		if (normLat < -180.0) {
			normLat += 360.0;
		}
		// treat limit case : 179.9999999999...
		if (normLat > 179.99 && normLat < 180.0) {
			normLat = -180.0;
		}
		return normLat;
	}

	/**
	 * Transform rectangular coordinates from HAE to CARRINGTON coordinate's
	 * system AttSun is the 0 meridian of sun expressed in spherical HAE
	 * coordinate's system
	 * @param attitudeSun : 0 meridian of sun in spherical HAE coordinate's system, expressed in degrees
	 * @return CARRINGTON coordinates, expressed in degrees
	 */
	public static double[] rectangularToCarrington(double x, double y, double z, double[] attitudeSun) {
		// First, convert rectangular to spherical coordinates
		double[] HAELongLatRadius = toSpheric(x, y, z);
		// Then, compute the CARRINGTON coordinates
		double[] CARLongLat = HAEToCarrington(HAELongLatRadius, attitudeSun);
		return CARLongLat;
	}

}
