package org.spview.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
//import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.spview.filehandler.FortranFormat;

/*
 * Class to calculate local simulation spectrum through a simul job; called by JFSelPred
 */


/**
 * This panel creates and runs a job to calculate a local simulation spectrum
 * from (area) selected predictions.
 */
public class JFSimul extends JFrame implements ActionListener {

    /**
	 * 
	 */
    private static final long serialVersionUID = -3391496760301771994L;
	private final String    playd;                                         // SPVIEW installation directory
    private final JFSelPred calling;

    private double[] lsx;                                            // loc-sim spectrum X
    private double[] lsy;                                            // loc-sim spectrum Y

    // simulation parameters
    private final JFormattedTextField   jftfumin;                          // for vumin
    private float     vumin;                                         // lower frequency cm-1
    private final JFormattedTextField   jftfumax;                          // for vumax
    private float     vumax;                                         // upper frequency cm-1
    private final JFormattedTextField   jftftemp;                          // for vtemp
    private float     vtemp;                                         // temperature K
    private final JFormattedTextField   jftfres;                           // for vres
    private float     vres;                                          // resolution cm-1
    private final JFormattedTextField   jftfpaf;                           // for vpaf
    private float     vpaf;                                          // Drawing Steps Size cm-1
    private final JFormattedTextField   jftfthres;                         // for vthres
    private float     vthres;                                        // Intensity Threshold cm-2/atm
    private final JComboBox<String> jcbsptype;                             // for tsptype
    private String    nsptype;                                       // Spectrum type
    private final String[]  tappf;                                         // apparatus function choice array
    private final JComboBox<String> jcbappf;                               // for tappf
    private String    nappf;                                         // Apparatus function
    private final JFormattedTextField   jftfcdae;                          // for cdae
    private float     cdae;                                          // Press Broadening Coeff cm-1.atm-1
    private final JFormattedTextField   jftfmass;                          // for mass
    private float     mass;                                          // Molar Mass
    private final JFormattedTextField   jftfptt;                           // for ptt
    private float     ptt;                                           // Total   Pressure Torr

    private final JButton jbset;                                           // set button

    // variables
    private final String      tempod;                                      // SPVIEW tempo directory
    private final String      njobf;                                       // job file
    private boolean     jobexist;                                    // job file exist status
    private PrintWriter out1;                                        // to write job
    private final String      lfil;                                        // line file
    private final String      nerrf;                                       // error file
    private final String      noutf;                                       // output file
    private Process     monproc;                                     // various processes
    private final String      nxyf;                                        // simulated spectrum file
    private BufferedReader br;
    private final boolean     verbose = true;

    private final String lnsep;                                            // line separator
    private final String fisep;                                            // file separator

/////////////////////////////////////////////////////////////////////

    /**
     * Construct a new JFSimul.
     *
     * @param ccalling  calling JFSelPred
     * @param cix0      window X location
     * @param ciy0      window Y location
     */
    public JFSimul( JFSelPred ccalling, int cix0, int ciy0 ) {

        playd   = System.getProperty("spview.home");                 // SPVIEW installation directory
        calling = ccalling;                                          // calling JFSelPred

        lnsep = System.getProperty("line.separator");
        fisep = System.getProperty("file.separator");
        // default cdae
        double dcdae = .09;                                                 // default cdae

        // format definition
//        nff = NumberFormat.getNumberInstance(Locale.US);             // ask a plain format
//        dff = (DecimalFormat)nff;                                    // reduce to decimal format
//        dff.applyPattern("00000.000000");                            // define pattern
//        nfi = NumberFormat.getNumberInstance(Locale.US);             // ask a plain format
//        dfi = (DecimalFormat)nfi;                                    // reduce to decimal format
//        dfi.applyPattern("0.00E00");                                 // define pattern

        tempod =playd+fisep+"tempo";                                 // SPVIEW tempo directory
        njobf = tempod+fisep+"locsim_job";                           // job file
        jobexist = false;                                            // job file MUST be created
        lfil  = tempod+fisep+"locsim_lines";                         // line file
        nerrf = njobf+"_err";                                        // error file
        noutf = njobf+"_out";                                        // output file
        nxyf  = tempod+fisep+"simul.xy";                             // simulated spectrum file

        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        //setLayout(new BorderLayout());
        getContentPane().setLayout(new BorderLayout());
        setLocation(cix0, ciy0);

        // panel to show and get local simulation parameters
        // panels
        JPanel pcentre = new JPanel(new GridLayout(11, 2, 5, 5));

        // SIMULATION PARAMETERS
        // formats
        // Number Format
        NumberFormat nffloat = NumberFormat.getInstance(Locale.US);               // float
        nffloat.setGroupingUsed(false);                              // NO grouping
        nffloat.setMaximumFractionDigits( 7);                        // fraction  digits
        // UMIN
        pcentre.add(new JLabel("   Lower Frequency Limit (cm-1) ",null,JLabel.LEFT));
        jftfumin = new JFormattedTextField(nffloat);
        jftfumin.setValue(0.f);
        pcentre.add(jftfumin);
        // UMAX
        pcentre.add(new JLabel("   Upper Frequency Limit (cm-1) ",null,JLabel.LEFT));
        jftfumax = new JFormattedTextField(nffloat);
        jftfumax.setValue(0.f);
        pcentre.add(jftfumax);
        // TEMP
        pcentre.add(new JLabel("   Temperature (K) ",null,JLabel.LEFT));
        jftftemp = new JFormattedTextField(nffloat);
        jftftemp.setValue(0.f);
        pcentre.add(jftftemp);
        // RES
        pcentre.add(new JLabel("   Resolution (cm-1) ",null,JLabel.LEFT));
        jftfres = new JFormattedTextField(nffloat);
        jftfres.setValue(0.f);
        pcentre.add(jftfres);
        // PAF
        pcentre.add(new JLabel("   Drawing Steps Size (cm-1) ",null,JLabel.LEFT));
        jftfpaf = new JFormattedTextField(nffloat);
        jftfpaf.setValue(0.f);
        pcentre.add(jftfpaf);
        // THRES
        pcentre.add(new JLabel("   Intensity Threshold (cm-2/atm) ",null,JLabel.LEFT));
        jftfthres = new JFormattedTextField(nffloat);
        jftfthres.setValue(0.f);
        pcentre.add(jftfthres);
        // TYPE
        // spectrum type choice array
        String[] tsptype = new String[3];
        tsptype[0] = "trans";
        tsptype[1] = "raman";
        tsptype[2] = "absor";
        pcentre.add(new JLabel("   Spectrum Type ",null,JLabel.LEFT));
        jcbsptype = new JComboBox<>(tsptype);
        jcbsptype.addItem("");
        jcbsptype.setSelectedItem("");                               // default to space
        jcbsptype.setBackground(Color.WHITE);
        nsptype = "";
        pcentre.add(jcbsptype);
        // APPF
        tappf = new String[4];
        tappf[0] = "dirac";
        tappf[1] = "sinc";
        tappf[2] = "sinc2";
        tappf[3] = "gauss";
        pcentre.add(new JLabel("   Apparatus Function ",null,JLabel.LEFT));
        jcbappf = new JComboBox<>(tappf);
        jcbappf.addItem("");
        jcbappf.setSelectedItem("");                                 // default to space
        jcbappf.setBackground(Color.WHITE);
        nappf = "";
        pcentre.add(jcbappf);
        // CDAE
        pcentre.add(new JLabel("   Press Broad Coeff (cm-1.atm-1) "));
        jftfcdae = new JFormattedTextField(nffloat);
        jftfcdae.setValue((float) dcdae);
        pcentre.add(jftfcdae);
        // PTT
        pcentre.add(new JLabel("   Total Pressure (Torr) "));
        jftfptt = new JFormattedTextField(nffloat);
        jftfptt.setValue(0.f);
        pcentre.add(jftfptt);
        // MASS
        pcentre.add(new JLabel("   Molar Mass "));
        jftfmass = new JFormattedTextField(nffloat);
        jftfmass.setValue(0.f);
        pcentre.add(jftfmass);


        pcentre.setBorder(BorderFactory.createTitledBorder("SIMULATION PARAMETERS"));

        // south panel
        jbset  = new JButton("Set");
        jbset.setToolTipText("Set local simulation parameters");
        jbset.setBackground(Color.WHITE);
        jbset.addActionListener(this);

        // box for jbset
        Box boxsud = Box.createHorizontalBox();                          // add button to box
        boxsud.add(jbset);
        JPanel psud = new JPanel();
        psud.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        psud.add(boxsud);                                            // add box

        // add panels
        //add(pcentre,"Center");
        getContentPane().add(pcentre,"Center");
        //add(psud,"South");
        getContentPane().add(psud,"South");

        setVisible(false);
        pack();
    }

/////////////////////////////////////////////////////////////////////

    /**
     * Process events.
     */
    public void actionPerformed(ActionEvent evt) {

        // set button
        if( evt.getSource() == jbset ) {
            if( testCJP( verbose ) ) {
                // good new parameters
                jobexist = false;                                    // job file MUST be created
                setVisible(false);
                calling.simParmSet();                                // warn JFSelPred
            }
        }
    }

/////////////////////////////////////////////////////////////////////

    // test parameters
    private boolean testCJP( boolean verb ) {

        // SIMULATION PARAMETERS
        // UMIN
        vumin = ((Number)jftfumin.getValue()).floatValue();
        if( vumin < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Lower Frequency >= 0 requested");
            }
            return false;
        }
        // UMAX
        vumax = ((Number)jftfumax.getValue()).floatValue();
        if( vumax <= vumin ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Upper Frequency > Lower Frequency requested");
            }
            return false;
        }
        // TEMP
        vtemp = ((Number)jftftemp.getValue()).floatValue();
        if( vtemp < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Temperature >= 0 requested");
            }
            return false;
        }
        // RES
        vres = ((Number)jftfres.getValue()).floatValue();
        if( vres < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Resolution >= 0 requested");
            }
            return false;
        }
        // PAF
        vpaf = ((Number)jftfpaf.getValue()).floatValue();
        if( vpaf <= 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Drawing Step Size > 0 requested");
            }
            return false;
        }
        if( vpaf >= vres ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Drawing Step Size < Resolution requested");
            }
            return false;
        }
        // THRES
        vthres = ((Number)jftfthres.getValue()).floatValue();
        if( vthres < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Intensity Thershold >= 0 requested");
            }
            return false;
        }
        // SPECTRUM TYPE
        nsptype = (String)jcbsptype.getSelectedItem();
        assert nsptype != null;
        if(nsptype.equals("")) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"You have to first define the Spectrum Type");
            }
            return false;
        }
        // APPF
        nappf = (String)jcbappf.getSelectedItem();
        assert nappf != null;
        if(nappf.equals("")) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"You have to first define the Apparatus Function");
            }
            return false;
        }
        // line file parameters test
        // CDAE
        cdae = ((Number)jftfcdae.getValue()).floatValue();
        if( cdae < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Press Broad Coeff >= 0 requested");
            }
            return false;
        }
        // MASS
        mass = ((Number)jftfmass.getValue()).floatValue();
        if( mass <= 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Molar Mass > 0 requested");
            }
            return false;
        }
        // PTT
        ptt = ((Number)jftfptt.getValue()).floatValue();
        if( ptt < 0 ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Total Pressure >= 0 requested");
            }
            return false;
        }
        // simul.f limits
        double refl  = 0.;                                           // FWHM reference
        double pdlmh;                                           // FWHM nb of steps
        if( nappf.equals(tappf[0]) ) {                               // DIRAC
            vres = (float) 0.;                                       // resolution = 0
        }
        double tef;                                             // effective temperature
        double sl;                                                   // LORENTZ
        double sd;                                                   // DOPPLER
        if( nappf.equals(tappf[3]) ) {
            //  GAUSS
            sd = (3.581097E-7)*Math.sqrt(vtemp/mass)*(vumin+vumax)/2.;
            tef = vtemp*(1.+Math.pow(vres/sd,2.));
        }
        else {
            tef = vtemp;
        }
        sl = cdae*ptt/760.;                                          // 760 <- atmospheres
        sd = (3.581097E-7)*Math.sqrt(tef/mass)*(vumin+vumax)/2.;
        if(nsptype.equals("raman")) {
            sd = Math.sqrt(sd*sd+vres*vres);
        }
        refl = Math.max(refl,sl);
        refl = Math.max(refl,sd);
        // sampling test
        refl = 2.*refl;
        if( vres > refl && (! nappf.equals(tappf[0])) ) {            // NOT DIRAC
            refl = vres;
        }
        pdlmh = refl/vpaf;
        if( pdlmh < 3. ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Sampling too sparse"                                 +lnsep+
                                                   pdlmh+" drawing steps in a full width at half maximum"+lnsep+
                                                   "Has to be >= 3");
            }
            return false;
        }
        if( pdlmh > 20. ) {
            if( verb ) {
                JOptionPane.showMessageDialog(null,"Sampling too dense"                                  +lnsep+
                                                   pdlmh+" drawing steps in a full width at half maximum"+lnsep+
                                                   "Should be <= 20");
            }
            //return false;                                            // only a warning in simul.f
        }
        // everything is OK
        return true;
    }

    /**
     * Calculate local simulation spectrum.
     *
     * @param cpredx  predictions X
     * @param cpredy  predictions Y
     */
    public boolean calLocSim( double[] cpredx, double[] cpredy ) {

        if( ! testCJP( ! verbose ) ) {
            // simulation parameters NOT OK
            JOptionPane.showMessageDialog(null,"You have to define local simulation parameters");
            setVisible(true);
            return false;
        }
        if( ! createLines( cpredx, cpredy ) ) {
            // line file NOT OK
            return false;
        }
        if( ! jobexist ) {
            if( ! createJob() ) {
                // job NOT OK
                return false;
            }
        }
        if( ! runJob() ){
            // run NOT OK
            return false;
        }
        return readLsxy();
    }

    // Create line file following FORMAT 2000 of simul.f
	private boolean createLines(double[] cpredx, double[] cpredy) {

		try {
			out1 = new PrintWriter(new BufferedWriter(new FileWriter(lfil)));

			for (int i = 0; i < cpredx.length; i++) {
				// for each selected pred point
				out1.println(FortranFormat.formFreq(cpredx[i]) + FortranFormat.formInt(cpredy[i]));
			}
		} catch (IOException ioe) { // IO error
			JOptionPane.showMessageDialog(null, "IO error while writing file" + lnsep + lfil + lnsep + ioe);
            return false;
        }
		finally {
			// close the file
			if (out1 != null) {
				out1.close();
				if (out1.checkError()) {
					JOptionPane.showMessageDialog(null, "PrintWriter error while creating line file" + lnsep + lfil);
					return false;
				}
			}
        }
        return true;
    }

    // Create job
    private boolean createJob() {

        try {
            out1 = new PrintWriter( new BufferedWriter( new FileWriter( njobf ) ) );

            out1.println("#! /bin/sh");
            out1.println("##");
            out1.println("## Local simulation job created by SPVIEW");
            out1.println("##");
            out1.println("BASD="+playd+fisep+"packages"+fisep+"PKF");
            out1.println("##");
            out1.println(" SCRD=$BASD"+fisep+"prog"+fisep+"exe");
            out1.println("##");
            out1.println("##  SIMULATION PARAMETERS");
            out1.println("##");
            out1.println(" UMIN="+vumin);
            out1.println(" UMAX="+vumax);
            out1.println(" TEMP="+vtemp);
            out1.println(" RES="+vres);
            out1.println(" PAF="+vpaf);
            out1.println(" THRES="+vthres);
            out1.println("##");
            out1.println(" LFIL="+lfil);
            out1.println(" CDAE="+cdae);
            out1.println(" MASS="+mass);
            out1.println(" UK="+1.);
            out1.println(" PTT="+ptt);
            if(!nsptype.equals("raman")) {
                out1.println(" PPT=$PTT");
                out1.println(" CL="+1.);
            }
            out1.println("##");
            out1.println(" APPF="+nappf);
            out1.println("##");
            out1.println("##  SIMULATION");
            out1.println("##");
            out1.println(" $SCRD"+fisep+"passx simul "+nsptype+" $UMIN $UMAX $TEMP $RES $PAF $THRES \\");
            out1.print  ("                        ");                // 1st
            out1.print  (" $LFIL $CDAE $MASS $UK $PTT");
            if(!nsptype.equals("raman")) {
                out1.println(" $PPT $CL \\");
            }
            else {
                out1.println(" \\");
            }
            out1.println("                     end $APPF");
            out1.println("##");
        }
        catch (IOException ioe) {                                    // IO error
            JOptionPane.showMessageDialog(null,"IO error while writing file"+lnsep+
                                               njobf                        +lnsep+
                                               ioe);
            return false;
        }
        finally {
            // close the file
            if (out1 != null) {
                out1.close();
                if( out1.checkError() ) {
                    JOptionPane.showMessageDialog(null,"PrintWriter error while creating job file"+lnsep+
                                                       njobf);
                    return false;
                }
            }
        }
        try {
            monproc = Runtime.getRuntime().exec("chmod u+x "+njobf);  // allow execution
        }
        catch (IOException ioe) {                                    // IO error
            JOptionPane.showMessageDialog(null,"IO error while changing rights of file"+lnsep+
                                               njobf                                   +lnsep+
                                               ioe);
            return false;
        }
        return true;
    }

    // run job and test error file
    private boolean runJob() {

        // command
        String ncmd = "cd " + tempod + " ; " + njobf + " >" + noutf + " 2>" + nerrf;      // change to tempo directory
        // run command in a shell
        try {
            String[] cmd = {"/bin/sh", "-c", ncmd};
            monproc = Runtime.getRuntime().exec(cmd);
        }
        catch (IOException ioe) {                                    // IO error
            JOptionPane.showMessageDialog(null,"IO error while running command"+lnsep+
                    ncmd +lnsep+
                                               ioe);
            return false;
        }
        try {
            monproc.waitFor();                                       // wait for end of job
        }
        catch (InterruptedException ie) {                            // error
            JOptionPane.showMessageDialog(null,"InterruptedException while running command"+lnsep+
                    ncmd);
            return false;
        }
        // error file has to be empty
        File errf = new File( nerrf );
        if( errf.length() != 0 ) {
            JOptionPane.showMessageDialog(null,"Error while running local simulation job"+lnsep+
                                               "See : "+nerrf);
            return false;
        }
        return true;
    }

    // Read lsx and lsy from simul.xy following FORMAT 2001 of simul.f
    private boolean readLsxy() {

        String str;                                                  // to read file
        double cx;                                                   // to keep X
        double cy;                                                   // to keep Y
        // to keep points, unknown nb of points
        ArrayList<Double> alx = new ArrayList<>();                             // frequency
        ArrayList<Double> aly = new ArrayList<>();                             // intensity
        try{
            File f = new File( nxyf );                               // the file
            br = new BufferedReader( new FileReader( f ) );          // open
            while ((str = br.readLine()) != null) {                  // line read
                try {
                    // read data
                    cx = Double.parseDouble(str.substring(0, 12));        // Double for X
                    cy = Double.parseDouble(str.substring(12,25));       // Double for Y
                }
                catch (NumberFormatException e) {                    // format error
                    continue;                                        // skip the line
                }
                alx.add(cx);                                         // keep data
                aly.add(cy);
            }
        }
        catch(IOException ioe) {                                     // IO error
            JOptionPane.showMessageDialog(null,"IO error while reading file"+lnsep+
                                               nxyf                         +lnsep+
                                               ioe);
            return false;
        }
        finally {
            // close
            if (br !=null) {
                try {
                    br.close();
                }
                catch (IOException ignored) {
                }
            }
        }
        // set lsx, lsy
        int nbxy = alx.size();                                       // nb of points
        if( nbxy == 0 ) {
            // no point
            JOptionPane.showMessageDialog(null,"No valid data found in file"+lnsep+
                                               nxyf);
            return false;                                            // invalid read
        }
        lsx = new double[nbxy];                                      // create arrays
        lsy = new double[nbxy];
        for(int i=0; i<nbxy; i++) {
            // set data
            lsx[i] = alx.get(i);
            lsy[i] = aly.get(i);
        }
        return true;
    }

//    // format a frequency (FORTRAN format : F12.6)
//    // WARNING : only positive values
//    private String formFreq( double cx ) {
//
//        StringBuffer sb;
//        sb = new StringBuffer(dff.format(cx));                       // format
//        for( int i=0; i<sb.length(); i++ ) {
//            if( sb.charAt(i) == '0' ) {
//                sb.setCharAt(i, ' ');                                // replace leading 0s with spaces
//            }
//            else {
//                // end of leading 0s
//                break;
//            }
//        }
//        return sb.toString();
//    }
//
//    // format an intensity (FORTRAN format : E9.2)
//    private String formInt( double cy ) {
//
//        StringBuffer sb;
//        int ie;
//        sb = new StringBuffer(dfi.format(cy));                       // format
//        if( sb.charAt(0) != '-' ) {                                  // if not beginning with a -
//            sb.insert(0, ' ');                                       // insert a space
//        }
//        ie = sb.indexOf("E");
//        ie ++;
//        if( sb.charAt(ie) != '-' ) {                                 // if positive exponent
//            sb.insert(ie, '+');                                      // insert a +
//        }
//        return sb.toString();
//    }

	/**
	 * Set local simulation basics.
	 *
	 * @param cumin area min frequency
	 * @param cumax area max frequency
	 */
	public void setLsBasic(double cumin, double cumax) {

		jftfumin.setValue((float) cumin);
		jftfumax.setValue((float) cumax);
	}

	/**
	 * Simulation parameters status.
	 */
	public boolean isSimParmSet() {

		if (!testCJP(!verbose)) { // test data
			// NOT set
			JOptionPane.showMessageDialog(null, "You have to define local simulation parameters");
			setVisible(true);
			return false;
		}
		// set
		return true;
	}

	/**
	 * Get loc-sim spectrum X.
	 */
	public double[] getLsx() {

		return lsx;
	}

	/**
	 * Get loc-sim spectrum Y.
	 */
	public double[] getLsy() {

		return lsy;
	}

}
