package hirondelle.starfield.physics;

import hirondelle.starfield.catalog.parser.Catalog;
import hirondelle.starfield.projection.Projector;
import hirondelle.starfield.util.Util;
import static hirondelle.starfield.util.Consts.NL;

import java.io.File;

/** 
 Model object for all items input by the user.
 
 <P>Converts text to objects, and performs validation. 
 If an error is detected, then the caller can report all errors. 
*/
public final class InputParameters {

  /**
   Construct using text only.
   
   @param aBeta the Lorentz boost speed, 0 <= beta < 1.
   @param aLimitingMag the brightness below which a star is taken as invisible to the human eye. Typical values are in the range 4.0 to 6.0. 
   @param aCatalog the star catalog being used as a data source
   @param aCatalogDir the directory that contains the catalog data. Must be an existing directory, must contain only 
   the catalog's data files and nothing else. No subdirectories will be scanned by this tool.
   @param aProjector the projection to use when mapping positions on the celestial sphere to the image plane. 
   @param aOutputFile the complete file name of the generated image. The directory must already exist; it won't be created for you.
   @param aImageSize width of the image in pixels. The image is square, so this is also the height of the image.
   @param aMagnification is applied only to the half-sky projections. When beta is high, increasing the 
   magnification can be used to resolve stars that are crowded together in the center.
   @param aDirectionRA the right ascension of the direction of motion of the spacecraft, in degrees, 0..360. Defaults to the North Celestial Pole.
   @param aDirectionDec the declination of the direction of motion of the spacecraft, in degrees, -90..+90. Defaults to the North Celestial Pole. 
   @param aRotation the angle to rotate the image about the center, in degrees, 0..360. Defaults to 0.
   */
  public InputParameters(
    String aBeta, String aLimitingMag, String aCatalogDir, String aCatalog, String aOutputFile, String aProjector,
    String aImageSize, String aMagnification, String aDirectionRA, String aDirectionDec, String aRotation
  ) throws InputParameterException {
    InputParameterException problem = new InputParameterException();
    try {
      fBeta = Double.valueOf(aBeta);
      if (fBeta < 0.0D || fBeta >= 1.0D){
        problem.add("Beta is not in the range [0..1): " + aBeta);
      }
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert beta to a number: " + aBeta);
    }
    
    try {
      fLimitingMagnitude = Double.valueOf(aLimitingMag);
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert the limiting magnitude to a number: " + aLimitingMag);
    }
    
    try {
      fCatalog = Catalog.valueOf(aCatalog);
    }
    catch (RuntimeException ex){
      problem.add("Unknown catalog: " + aCatalog);
    }
    
    try {
      fProjector = Projector.valueOf(aProjector);
    }
    catch (RuntimeException ex){
      problem.add("Unknown projection: " + aProjector);
    }

    fCatalogDir = new File(aCatalogDir);
    if (! fCatalogDir.exists()){
      problem.add("Catalog directory doesn't exist: " + aCatalogDir);
    }
    else if (fCatalogDir.isFile()){
      problem.add("Catalog directory isn't a directory: " + aCatalogDir);
    }
    
    fOutputFile = new File(aOutputFile); //no validation
    
    try {
      fImageSize = Integer.valueOf(aImageSize);
      if (fImageSize <= 0){
        problem.add("Image size must be greater than 0: " + aImageSize);
      }
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert Image Size to an integer: " + aImageSize);
    }

    try {
      fMagnification = Integer.valueOf(aMagnification);
      if (fMagnification <= 0){
        problem.add("Magnification must be greater than 0: " + aMagnification);
      }
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert Magnification to an integer: " + aMagnification);
    }

    try {
      fDirectionRA = Double.valueOf(aDirectionRA);
      if (fDirectionRA < 0.0D || fDirectionRA > 360.0D){
        problem.add("Direction of motion RA is not in range [0..360]: " + aDirectionRA);
      }
      fDirectionRA = Util.radians(fDirectionRA);
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert Direction of Motion RA to a number: " + aDirectionRA);
    }
    
    try {
      fDirectionDec = Double.valueOf(aDirectionDec);
      if (fDirectionDec < -90.0D || fDirectionDec > 90.0D){
        problem.add("Direction of motion Dec is not in Range [-90..+90]: " + aDirectionDec);
      }
      fDirectionDec = Util.radians(fDirectionDec);
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert Direction of Motion Dec to a number: " + aDirectionDec);
    }
    
    try {
      fRotation = Double.valueOf(aRotation);
      if (fRotation < 0.0D || fRotation > 360.0D){
        problem.add("Rotation is not in range [0..360]: " + aRotation);
      }
      fRotation = Util.radians(fRotation);
    }
    catch (NumberFormatException ex){
      problem.add("Can't convert Rotation to a number: " + aRotation);
    }
    
    if (problem.hasError()){
      throw problem;
    }
  }
  
  public double getBeta(){ return fBeta; }
  public double getLimitingMagnitude(){ return fLimitingMagnitude; }
  public Catalog getCatalog(){ return fCatalog; }
  public File getCatalogDirectory(){ return fCatalogDir; }
  public Projector getProjector(){ return fProjector; }
  public File getOutputFile(){ return fOutputFile; }
  public int getImageSize(){ return fImageSize; }
  public int getMagnification(){ return fMagnification; }
  public double getDirectionOfMotionRA(){ return fDirectionRA; }
  public double getDirectionOfMotionDec(){ return fDirectionDec; }
  public double getRotation(){ return fRotation; }
  
  /** Intended For debugging. */
  @Override public String toString(){
    StringBuilder result = new StringBuilder();
    result.append("Beta:" + fBeta + NL);
    result.append("Limiting Mag:" + fLimitingMagnitude + NL);
    result.append("Catalog:" + fCatalog + NL);
    result.append("Catalog Dir:" + fCatalogDir + NL);
    result.append("Projection:" + fProjector + NL);
    result.append("Output File:" + fOutputFile + NL);
    result.append("Image Size:" + fImageSize + NL);
    result.append("Magnification:" + fMagnification + NL);
    result.append("Direction of Motion RA:" + fDirectionRA + NL);
    result.append("Direction of Motion Dec:" + fDirectionDec + NL);
    result.append("Rotation:" + fRotation + NL);
    return result.toString();
  }
  
  private double fBeta;
  private double fLimitingMagnitude;
  private Catalog fCatalog;
  private File fCatalogDir;
  private Projector fProjector;
  private File fOutputFile;
  private int fImageSize;
  private int fMagnification;
  private double fDirectionRA;
  private double fDirectionDec;
  private double fRotation;
  
}