package hirondelle.starfield.physics;

import static java.lang.Math.asin;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.tan;
import hirondelle.starfield.util.Util;

/**
 Change the direction of motion of the spacecraft.
 
 <P>By default, the spacecraft moves towards the North Celestial Pole (in the general direction of Polaris).
 This class allows you to change that default, and to see what the stars look like when the spacecraft 
 is moving in a different direction.
 
 <P>For example, to point the spacecraft towards the galactic pole near the constellation of Leo, 
 you can try (for J2000) the following values (in degrees): 
<pre>DirectionRightAscension = 192.85
DirectionDeclination = +27.13
Phi = 33
</pre>

<P>To point the spacecraft towards the galactic center, you can try (for J2000): 
<pre>DirectionRightAscension = 266.40
DirectionDeclination = -28.94
Phi = 30
</pre>

<P>The value for Phi is simply controls the rotation of the view about the 
direction of motion. You may adjust it to suit your taste.
*/
final class DirectionOfMotion {
  
  /**
   Constructor. 
   @param aDirectionRightAscension Right ascension of the direction of motion of the spacecraft, in radians.
   @param aDirectionDeclination Declination of the direction of motion of the spacecraft, in radians.
   @param aPhi Rotation angle about the direction of motion, in radians. Controls how the view is rotated. 
  */
  DirectionOfMotion(double aDirectionRightAscension, double aDirectionDeclination, double aPhi){
    fDirRightAscension = aDirectionRightAscension;
    fDirDeclination = aDirectionDeclination;
    fPhi = aPhi;
  }
  
  /**
   Return <tt>true</tt> only if the user has input at least one parameter that differs from the default.
   The default direction of motion is the North Celestial Pole, with a 0 degree angle of rotation of the view. 
  */
  boolean isNotDefault(){
    return fDirRightAscension != 0.0D || Math.abs(Math.PI/2.0D-fDirDeclination) >0.0000001D  || fPhi != 0.0D;
  }
  
  /**
   Change the right ascension and declination of a {@link Star} to reflect its coordinates with respect the 
   direction of motion of the spacecraft. (Of course, the position of the star hasn't changed in space; this 
   is merely a convenient way of doing the calculation.)
   In effect, the direction of motion of the spacecraft becomes the "new" North Celestial Pole.
   This corresponds to a rotation of the coordinates.
    
   <P>(Implementation Note: this transformation is almost exactly the same as the transformation between equatorial 
   coordinates and galactic coordinates.)  
  */
  void changeCoordsOfThe(Star aStar){
    double sinLat = 
      sin(aStar.Declination) * sin(fDirDeclination) + 
      cos(aStar.Declination) * cos(fDirDeclination) * cos (fDirRightAscension - aStar.RightAscension)
    ;
    
    double numerator = sin(fDirRightAscension - aStar.RightAscension);
    double denominator = 
      cos(fDirRightAscension - aStar.RightAscension) * sin(fDirDeclination) - 
      tan(aStar.Declination) * cos(fDirDeclination)
    ;
    double x = atan2(numerator, denominator); //-180..+180
    //double longit = 1.5D * Math.PI  + fPhi - x; //this changes the 0h direction to 12:00
    double longit = fPhi - x; //this keeps the 0-hr direction at 3:00
    
    //finally, change the star state only at the end 
    aStar.Declination = asin(sinLat); //-90..+90
    aStar.RightAscension = longit;
  }

  // PRIVATE 
  
  private double fDirRightAscension;
  private double fDirDeclination;
  private double fPhi;

  /**
   This method is useful for testing the formula. 
   The math is almost exactly the same as the general case.
   Reference: Astronomical Algorithms, Jean Meeus. 

   Galactic Pole, J2000:
   alpha 192.85 
   dec  27.13  
   
   Regulus, J2000, equatorial coords:
   alpha 10h08m22.31s    0.208867
   dec 11d58m1.95s        2.654522
   
   Regulus galactic coords, J2000:
   48.9420 deg lat          0.854199
   226.5039 deg long      3.953413
   */
  private void galacticCoords(Star aStar){
    double poledec = Util.radians(27.13);
    double polealpha = Util.radians(192.85); 
    double sin_b = 
      sin(aStar.Declination) * sin(poledec) + 
      cos(aStar.Declination) * cos(poledec) * cos(polealpha - aStar.RightAscension);
    ;
    
    double numer = sin(polealpha - aStar.RightAscension);
    double denom = cos(polealpha - aStar.RightAscension) * sin(poledec)  - tan(aStar.Declination) * cos(poledec);
    double x = atan2(numer, denom);
    double longit = Util.radians(303) - x;
   
    aStar.Declination = asin(sin_b); //-90..+90
    aStar.RightAscension = longit;
  }
  
}