package util;

import visad.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import com.sun.image.codec.jpeg.*;

public class Helper
{
  static float R = 287f;
  static float g = 9.8f;
  static float minTemp = 200f;
  static float maxTemp = 320f;
  static float scale   = 10f;
  static float offset  = minTemp;
  static int nbins = (int) ((maxTemp - offset)/scale);
  static float[] expVals = new float[nbins];

  static {
    for (int kk = 0; kk < nbins; kk++) {
      expVals[kk] = (float) Math.exp(0);
    }
  }

  public static JLabel getUnitJLabel() {
   return new JLabel("\u00b5m");
  }

  public static float[] heightToPressure(int h_start_idx,
                                         float[] heightValues,
                                         float  bottomPressure,
                                         float[] temperatureValues,
                                         float   sfcHeight) 
  {
    int h_len = heightValues.length;    // meters above MSL
    bottomPressure *= 100f;             // kPa/mb -> Pa
    float bot_height = sfcHeight;
    float bot_temp   = temperatureValues[h_start_idx];
    float[] pressureValues = new float[h_len - h_start_idx];
  
    for (int i = 0; i < (h_len - h_start_idx); i++) {
      int k = h_start_idx + i;
      float top_height  = heightValues[k];
      float top_temp    = temperatureValues[k];
      float Tav         = (top_temp + bot_temp)/2;
      float dz          = top_height - bot_height;
      pressureValues[i] = bottomPressure*((float)Math.exp((float)(-g*dz/(R*Tav))));
      bot_height        = top_height;
      bot_temp          = top_temp;
      bottomPressure    = pressureValues[i];
    }
    
    return pressureValues;
  }

  public static float[] minmax(float[] values)
  {
    float min =  Float.MAX_VALUE;
    float max = -Float.MAX_VALUE;
    for (int k = 0; k < values.length; k++) {
      float val = values[k];
      if (val < min) min = val;
      if (val > max) max = val;
    }
    return new float[] {min, max};
  }

  public static float[] minmax(float[] values, int[] indexes)
  {
    float min =  Float.MAX_VALUE;
    float max = -Float.MAX_VALUE;
    for (int k = 0; k < values.length; k++) {
      float val = values[k];
      if (val < min) {
        min = val;
        indexes[0] = k;
      }
      if (val > max) {
        max = val;
        indexes[1] = k;
      }
    }
    return new float[] {min, max};
  }

  public static double[] minmax(double[] values, int[] indexes)
  {
    double min =  Double.MAX_VALUE;
    double max = -Double.MAX_VALUE;
    for (int k = 0; k < values.length; k++) {
      double val = values[k];
      if (val < min) {
        min = val;
        indexes[0] = k;
      }
      if (val > max) {
        max = val;
        indexes[1] = k;
      }
    }
    return new double[] {min, max};
  }



  public static float[] minmax(float[] values, float valid_lo, float valid_hi) 
  {
    float min = valid_hi;
    float max = valid_lo;
    for (int k = 0; k < values.length; k++) {
      float val = values[k];
      if ((val >= valid_lo) && (val <= valid_hi)) {
        if (val < min) min = val;
        if (val > max) max = val;
      }
    }
    return new float[] {min, max};
  }

  public static double[] minmax(double[] values)
  {
    double min =  Double.MAX_VALUE;
    double max = -Double.MAX_VALUE;
    for (int k = 0; k < values.length; k++) {
      double val = values[k];
      if (val < min) min = val;
      if (val > max) max = val;
    }
    return new double[] {min, max};
  }


  public static double[] minmax(double[] values, double valid_lo, double valid_hi)
  {
    double min = valid_hi;
    double max = valid_lo;
    for (int k = 0; k < values.length; k++) {
      double val = values[k];
      if ((val >= valid_lo) && (val <= valid_hi)) {
        if (val < min) min = val;
        if (val > max) max = val;
      }
    }
    return new double[] {min, max};
  }

  public static float[] unpack(int[] values, float scale, float offset)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale*(values[ii] - offset);
    }
    return new_values;
  }

  public static float[] unpack(float[] values, float scale, float offset)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale*(values[ii] - offset);
    }
    return new_values;
  }

  public static float[] unpack(short[] values, float scale, float offset)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale*(values[ii] - offset);
    }
    return new_values;
  }



  public static double[] scale_array(double[] values, double scale) 
  {
    double[] new_values = new double[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale*values[ii];
    }
    return new_values;
  }

  public static float[] scale_array(float[] values, float scale) 
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale*values[ii];
    }
    return new_values;
  }

  public static float[] unpack(int[] values, float[] scale_s, float[] offset_s)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale_s[ii]*(values[ii] - offset_s[ii]);
    }
    return new_values;
  }

  public static float[] unpack(short[] values, float[] scale_s, float[] offset_s)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale_s[ii]*(values[ii] - offset_s[ii]);
    }
    return new_values;
  }


  public static float[] unpack(float[] values, float[] scale_s, float[] offset_s)
  {
    float[] new_values = new float[values.length];
    for (int ii = 0; ii < values.length; ii++) {
      new_values[ii] = scale_s[ii]*(values[ii] - offset_s[ii]);
    }
    return new_values;
  }


  public static double[][] getValues(FlatField field, boolean copy)
         throws Exception
  {
    return field.getValues(copy);
  }

  public static float[][] unpackFloats(FlatField field, boolean copy)
         throws Exception
  {
    return field.getFloats(copy);
  }

 //... Effective central wavenumbers (inverse centimeters)
   static double[] cwn_terra = {
       2.641767E+03, 2.505274E+03, 2.518031E+03, 2.465422E+03,
       2.235812E+03, 2.200345E+03, 1.478026E+03, 1.362741E+03,
       1.173198E+03, 1.027703E+03, 9.081998E+02, 8.315149E+02,
       7.483224E+02, 7.309089E+02, 7.188677E+02, 7.045309E+02};

 //... Temperature correction slopes (no units)
   static double[]  tcs_terra = {
       9.993487E-01,  9.998699E-01,  9.998604E-01,  9.998701E-01,  
       9.998825E-01,  9.998849E-01,  9.994942E-01,  9.994937E-01, 
       9.995643E-01,  9.997499E-01,  9.995880E-01,  9.997388E-01, 
       9.999192E-01,  9.999171E-01,  9.999174E-01,  9.999264E-01};

 //... Temperature correction intercepts (Kelvin)
   static double[]  tci_terra = {
       4.744530E-01,  9.091094E-02,  9.694298E-02,  8.856134E-02, 
       7.287017E-02,  7.037161E-02,  2.177889E-01,  2.037728E-01,
       1.559624E-01,  7.989879E-02,  1.176660E-01,  6.856633E-02, 
       1.903625E-02,  1.902709E-02,  1.859296E-02,  1.619453E-02};

 //... Effective central wavenumbers (inverse centimeters)
   static double[]  cwn_aqua = {
       2.647418E+03, 2.511763E+03, 2.517910E+03, 2.462446E+03,
       2.248296E+03, 2.209550E+03, 1.474292E+03, 1.361638E+03,
       1.169637E+03, 1.028715E+03, 9.076808E+02, 8.308397E+02,
       7.482977E+02, 7.307761E+02, 7.182089E+02, 7.035020E+02};

 //... Temperature correction slopes (no units)
   static double[]  tcs_aqua = {
       9.993438E-01, 9.998680E-01, 9.998649E-01, 9.998729E-01,
       9.998738E-01, 9.998774E-01, 9.995732E-01, 9.994894E-01,
       9.995439E-01, 9.997496E-01, 9.995483E-01, 9.997404E-01,
       9.999194E-01, 9.999071E-01, 9.999176E-01, 9.999211E-01};

 //... Temperature correction intercepts (Kelvin)
  static double[]  tci_aqua = {
       4.792821E-01, 9.260598E-02, 9.387793E-02, 8.659482E-02,
       7.854801E-02, 7.521532E-02, 1.833035E-01, 2.053504E-01,
       1.628724E-01, 8.003410E-02, 1.290129E-01, 6.810679E-02,
       1.895925E-02, 2.128960E-02, 1.857071E-02, 1.733782E-02};

// Constants are from "The Fundamental Physical Constants",
// Cohen, E. R. and B. N. Taylor, Physics Today, August 1993.

// Planck constant (Joule second)
  static double h = 6.6260755e-34;

// Speed of light in vacuum (meters per second)
 static double c = 2.9979246e+8;

// Boltzmann constant (Joules per Kelvin)      
 static double k = 1.380658e-23;

// Derived constants      
 static double c1 = 2.0 * h * c * c;
 static double c2 = (h * c) / k;

  public static float[]  modis_radiance_to_brightnessTemp(String platformName, int band_number, float[] values)
         throws Exception
  {
    return (Set.doubleToFloat(new double[][] {modis_radiance_to_brightnessTemp(platformName,band_number,(Set.floatToDouble(new float[][] {values}))[0])}))[0]; 
  }
  public static double[] modis_radiance_to_brightnessTemp(String platformName, int band_number,  double[] values)
         throws Exception
  {
 
    if ((band_number < 20) || (band_number > 36) || (band_number == 26)) {
      throw new Exception("bad band number: "+band_number+" band 20-36 but not 26");
    }

    int index;

    if (band_number <= 25) {
      index = band_number - 19;
    }
    else { 
      index = band_number - 20;
    }
    index -= 1;
  
    double cwn;
    double tcs;
    double tci;

    if (platformName.equals("Terra")) {
      cwn = cwn_terra[index];
      tcs = tcs_terra[index];
      tci = tci_terra[index];
    }
    else if (platformName.equals("Aqua")) {
      cwn = cwn_aqua[index];
      tcs = tcs_aqua[index];
      tci = tci_aqua[index];
    }
    else {
      throw new Exception("platformName must equal: Terra or Aqua");
    }
 
    //... Compute Planck radiance - MODIS units
    //... Watts per square meter per steradian per micron
      
    //... Convert wavelength to meters
    double ws = 1.0E-6 * (1.0E+4 / cwn);

    //... Compute brightness temperature
    int len = values.length;
    double[] new_values = new double[len];
    for (int kk = 0; kk < len; kk++) { 
      double rad = values[kk];
      double BT = ( c2 / (ws * Math.log(c1 /(1.0E+6*rad*(ws*ws*ws*ws*ws)) + 1.0))-tci)/tcs;
      new_values[kk] = BT;
    }

    return new_values;
  }

public static float[] msg_radiance_to_brightnessTemperature(int[] index_s, float[] values)
{
   float C2 = 1.43877f;     // K(cm-1)-1
   float C1 = 1.19104E-5f;  // mW(m-2)(sr-1)(cm-1)-4

   float[][] consts = new float[][] {{2569.094f, 0.9959f, 3.471f},
                                     {1598.566f, 0.9963f, 2.219f},
                                     {1362.142f, 0.9991f, 0.485f},
                                     {1149.083f, 0.9996f, 0.181f},
                                     {1034.345f, 0.9999f, 0.060f},
                                     {930.659f, 0.9983f, 0.627f},
                                     {839.661f, 0.9988f, 0.397f},
                                     {752.381f, 0.9981f, 0.576f}};

   float[] new_values = new float[values.length];

   for (int i = 0; i < new_values.length; i++) {
       float A = consts[index_s[i]][1];
       float B = consts[index_s[i]][2];
       float v = consts[index_s[i]][0];

       new_values[i] = ((C2*v /((float) Math.log((double)C1*v*v*v/values[i] + 1))) - B)/A;
   }

   return new_values;
}

public static float[] msg_radiance_to_brightnessTemperature(int index, float[] values) 
{
   float C2 = 1.43877f;     // K(cm-1)-1
   float C1 = 1.19104E-5f;  // mW(m-2)(sr-1)(cm-1)-4

   float[][] consts = new float[][] {{2569.094f, 0.9959f, 3.471f},
 	    	                     {1598.566f, 0.9963f, 2.219f},
        	                     {1362.142f, 0.9991f, 0.485f},
                                     {1149.083f, 0.9996f, 0.181f},
                                     {1034.345f, 0.9999f, 0.060f},
                                     {930.659f, 0.9983f, 0.627f},
                                     {839.661f, 0.9988f, 0.397f},
                                     {752.381f, 0.9981f, 0.576f}};

   float A  = consts[index][1];
   float B  = consts[index][2]; 
   float v  = consts[index][0];

   float[] new_values = new float[values.length];

   for (int i = 0; i < new_values.length; i++) {
       new_values[i] = ((C2*v /((float) Math.log((double)C1*v*v*v/values[i] + 1))) - B)/A;  
   }

   return new_values;
}

public static float[] msg_radiance_to_reflectance(int[] index_s, float[] values)
{
   float[] consts = new float[] {20.76f, 23.24f, 19.85f};

   float[] new_values = new float[values.length];

   for (int i = 0; i < new_values.length; i++) {
     new_values[i] = values[i]/consts[i];
   }

   return new_values;
}

public static float[] msg_radiance_to_reflectance(int index, float[] values)
{
  float[] consts = new float[] {20.76f, 23.24f, 19.85f};

  float[] new_values = new float[values.length];

  float TORAD = consts[index];

  for (int i = 0; i < new_values.length; i++) {
    new_values[i] = values[i]/TORAD;
  }

  return new_values;
}

public static void saveDisplay(DisplayImpl display, Component comp)
{
  final DisplayImpl fdisplay = display;
  final Component cframe      = comp;
    Runnable captureImage = new Runnable() {
      public void run() {
      try{

        JFileChooser fc= new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        fc.setDialogTitle("Save image as Jpeg");
        String dir = fc.getCurrentDirectory().getAbsolutePath();

        if(fc.showDialog(cframe, "Save") == JFileChooser.APPROVE_OPTION)
        {
          System.out.println("Saving image..." );
          String cfn = fc.getSelectedFile().getName();

          BufferedImage image = fdisplay.getImage();
          dir = fc.getCurrentDirectory().getAbsolutePath();
          FileOutputStream ffout = new FileOutputStream(dir+File.separator+cfn);
          JPEGEncodeParam jepar = JPEGCodec.getDefaultJPEGEncodeParam(image);
          jepar.setQuality( 1.0f, true);

          JPEGImageEncoder jpege = JPEGCodec.createJPEGEncoder(ffout) ;
          jpege.encode(image, jepar);
          ffout.close();
          System.out.println("Saved "+cfn);
        }
        else {
           System.out.println("No file was chosen or an error occurred");
        }


      } catch(FileNotFoundException e){e.printStackTrace();
      } catch(IOException e){e.printStackTrace();

      }
     }

    };

    Thread t = new Thread(captureImage);
    t.start();
  }

}
