package usda.weru.weps;

import de.schlichtherle.io.File;
import de.schlichtherle.io.FileReader;
import de.schlichtherle.io.FileWriter;




import java.awt.Component;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


import java.util.StringTokenizer;
import javax.help.CSH;
import javax.measure.unit.NonSI;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import usda.weru.mcrew.ManageData;
import usda.weru.util.ConfigData;
import usda.weru.util.Mantis;
import usda.weru.util.Util;
import usda.weru.util.WepsFileChooser;
import usda.weru.util.WepsMessage;
import usda.weru.util.WepsMessageDialog;
import usda.weru.util.WepsMessageLog;
import usda.weru.util.windgen.MakeInterpolatedStation;
import usda.weru.weps.location.CligenStation;
import usda.weru.weps.location.FileStation;
import usda.weru.weps.location.InterpolatedStation;
import usda.weru.weps.location.Station;
import usda.weru.weps.location.StationMode;
import usda.weru.weps.location.WindgenStation;
import usda.weru.weps.reports.ReportManager;
import usda.weru.weps.reports.ReportPack;

/**
 * This class makes simulation RUNS for the wind and climate generation data files using the model and shows all the
 * analysis and report genration screens with the data from it.
 */
public final class RunProgram implements Runnable, PropertyChangeListener {

    private String CurrentProj = "";
    private String lastSuccessfulRun = "";
    private String ManageFile = "";
    private String SoilFile = "";
    private String WinGen = "";
    private String CliGen = "";
    private String WinExe = "";
    private String WinGenNumber = "";
    private String WinCmd = "";
    private String TotalYears = "";
    private String startYear = "";
    private String CliExe = "";
    private String CliGenState = "";
    private String CliGenStation = "";
    private String CliCmd = "";
    private String WepsExe = "";
    private String WepsCmd = "";
    private String calWepsCmd = "";
    private String WinFlg = "";
    private String CliFlg = "";
    private String notesText = "";
    private String oldNotes = "";
    
    private final Logger LOGGER = Logger.getLogger(RunProgram.class);
    String[][] commandsArr = new String[3][];
    String runDir;
    Component parent;
    RunDialog runDialog;
    Thread mainRunThread;
    Process exeProcess;
    private RunFileData runFileData;
    ConfigData configData;
    private String newDir;
    private boolean stopFlag = false;
    private boolean calMode;
    //This boolean is used to check if the copying of the files is done properly for the run. - neha
    private boolean copyResult = false;
    private String xLength;
    private String yLength;
    boolean noteLock = false;
    /**
     * On=bject handle for the calibration factor dialog box for accessing its components within the scope
     * of RUN Program class.
     */
    public CalFactorDialog calibFactors;
    /**
     * The current project name to which this simulation RUN is associated.
     */
    public String projName = "";
    /**
     * Wind station interpolation exe part 1
     */
    private String winIntp1Exe = "";
    /**
     * Wind station interpolation exe part 2
     */
    private String winIntp2Exe = "";
    
    
    

    /**
     * Interpolated wind station builder -- null if not interpolating station
     */
    private MakeInterpolatedStation mis = null;

    /**
     * Copies files into run directory for weps.exe and starts a thread to make a simulation run.
     *
     */
    public RunProgram() {
    }

    /**
     * Copies files into run directory for weps.exe and starts a thread to make a simulation run.
     * @param parent The dialog in which this GUI operates.
     * @param rfd The object handle to runfiledata for accessing items and information through
     * the object modifiers
     * @param cd The configuraion data object handle to make use of setting details.
     * @param cm True if calibration mode is ON else false.
     */
    public RunProgram(Component parent, RunFileData rfd, ConfigData cd, boolean cm) {

        this.runFileData = rfd;
        this.configData = cd;
        this.parent = parent;
        this.calMode = cm;

        if (parent instanceof PropertyChangeListener) {
            addPropertyChangeListener((PropertyChangeListener) parent);
        }

        this.addPropertyChangeListener(rfd);
        rfd.updateDates();
        cd.fireAll(this);
        String rtnExe = checkExesExist();
        if (rtnExe != null) {
            JOptionPane.showMessageDialog(parent, "File not found " + rtnExe,
                    "Exe Not Found Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        rfd.fireAll(this);
        //Make sure that X length and Y length are not zero
        try {
            double test = Double.parseDouble(xLength);
            if (Double.isNaN(test) || Math.abs(test) == 0) {
                throw new NumberFormatException();
            }
        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(parent, "0.0 is an invalid value for the field X-Length.");
            return;
        }

        try {
            double test = Double.parseDouble(yLength);
            if (Double.isNaN(test) || Math.abs(test) == 0) {
                throw new NumberFormatException();
            }
        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(parent, "0.0 is an invalid value for the field Y-Length.");
            return;
        }

        

        if (!checkManExists() && !checkSoilExists()) {
            //no management file or soil file.
            JOptionPane.showMessageDialog(parent, "Management and Soil files not selected or found.  Please select them.", "Files not found ", JOptionPane.ERROR_MESSAGE);
            return;
        } else if (!checkManExists()) {
            JOptionPane.showMessageDialog(parent, "Management file not selected or found.  Please select one.", "File not found ", JOptionPane.ERROR_MESSAGE);
            return;
        } else if (!checkSoilExists()) {
            JOptionPane.showMessageDialog(parent, "Soil file not selected or found.  Please select one.", "File not found ", JOptionPane.ERROR_MESSAGE);
            return;
        }

        //make sure we don't have an xml file
        if(!checkManFormat()){
            JOptionPane.showMessageDialog(parent, "The selected management file is in an unsupported and experimental XML format.\nReturn to MCREW and save as a *.man format.", "Unsupported File Format ", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        if(this.calMode && !checkManCalibration()){
            return;
        }


        // check if project exists -- if not, save it -- if unsaved, stop run

        if (CurrentProj.endsWith(RunFileData.UntitledProject)) {
            JOptionPane.showMessageDialog(parent,
                    "Untitled Project.  Save project first, then make a run.",
                    "Error (RP-000)", JOptionPane.ERROR_MESSAGE);
            removeListeners(rfd, cd);
            return;   // name not set -- do not run sim
        }

        if (ManageFile.equals(RunFileData.DefaultManageFileName)) {
            JOptionPane.showMessageDialog(parent,
                    "You must pick a management scenario before making a run.",
                    "Error (RP-002)", JOptionPane.ERROR_MESSAGE);
            removeListeners(rfd, cd);
            return;
        }

        if (SoilFile.equals(RunFileData.DefaultSoilFileName)) {
            JOptionPane.showMessageDialog(parent,
                    "You must pick a soil before making a run.",
                    "Error (RP-003)", JOptionPane.ERROR_MESSAGE);
            removeListeners(rfd, cd);
            return;
        }

        File tmp = new File(WinGen);
        if (!tmp.isAbsolute()) {
            tmp = new File(CurrentProj + "/" + lastSuccessfulRun + "/" + WinGen);
        }
        //WINDGEN PRECHECK LOGIC
        StationMode windgenMode = runFileData.getBean().getWindgenStationMode();
        Station windgenStation = runFileData.getBean().getWindgenStation();

        if( windgenMode == null){
            JOptionPane.showMessageDialog(parent, "No windgen station selected.","Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        else if(windgenStation instanceof WindgenStation){
            //normal station, we generate a wind file with windgen
            //make sure we have enough data to run
            WindgenStation test = (WindgenStation) windgenStation;
            if(test.getWBan() == 0){
                JOptionPane.showMessageDialog(parent, "The windgen station record is missing the wban parameter.  \nPlease contact your data provider for updated records.","Incomplete Windgen Record", JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
        else if (windgenStation instanceof FileStation){
            //file station, we use an already generated file.
            FileStation station = (FileStation) windgenStation;
            File file = station.getFile();
            if(file == null || !file.exists()){
                JOptionPane.showMessageDialog(parent, "Windgen file does not exist\n" + "File:  " + file.getAbsolutePath(),"Error (RP-004)", JOptionPane.ERROR_MESSAGE);
                return;
            }            
        }
        else if(windgenStation instanceof InterpolatedStation){
            //interpolated station
            //no checks yet.
        }
        else if (windgenStation == null){
            //no station selected
            JOptionPane.showMessageDialog(parent, "No windgen station selected.","Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        else{
            //don't know how to handle the windgen station
            LOGGER.error("Unexpected windgen station type: " + windgenStation.toString());
            return;
        }
        //END WINDGEN PRECHECK LOGIC

       //CLIGEN PRECHECK LOGIC
        StationMode cligenMode = runFileData.getBean().getCligenStationMode();
        Station cligenStation = runFileData.getBean().getCligenStation();

        if(cligenMode == null){
            LOGGER.error("Unexpected cligen station type: " + cligenStation.toString());
            return;
        }
        else if(cligenStation instanceof CligenStation){
            //normal station, we generate a climate file with cligen
            //make sure we have enough data to run
            CligenStation test = (CligenStation) cligenStation;
            if(test.getState() == 0 || test.getId() == 0){
                JOptionPane.showMessageDialog(parent, "The cligen station record is missing the state id or station id.  \nPlease contact your data provider for updated records.","Incomplete Cligen Record", JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
        else if (cligenStation instanceof FileStation){
            //file station, we use an already generated file.
            FileStation station = (FileStation) cligenStation;
            File file = station.getFile();
            if(file == null || !file.exists()){
                JOptionPane.showMessageDialog(parent, "Cligen file does not exist\n" + "File:  " + file.getAbsolutePath(),"Error (RP-004)", JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
        else if (cligenStation == null){
            //no station selected
            JOptionPane.showMessageDialog(parent, "No cligen station selected.","Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        else{
            //don't know how to handle the cligenStation station
            LOGGER.error("Unexpected cligen station type: " + cligenStation.toString());
            return;
        }
        //END CLIGEN PRECHECK LOGIC

        tmp = new File(CliGen);
        if (!tmp.isAbsolute()) {
            tmp = new File(CurrentProj + "/" + lastSuccessfulRun + "/" + CliGen);
        }
        if (CliFlg.equals("1") && !(tmp.exists())) {
            JOptionPane.showMessageDialog(parent,
                    "Climate file does not exist\nSpecify file or enable climate generation.\n" +
                    "File:  " + new File(CliGen).getAbsolutePath(),
                    "Error (RP-005)", JOptionPane.ERROR_MESSAGE);
            removeListeners(rfd, cd);
            return;
        }

        //Run Name Selector
        RunNameChooser rnc = new RunNameChooser(runFileData);
        File runDirFile = rnc.showRunNameChooser(parent);
        if (runDirFile == null) {
            removeListeners(rfd, cd);
            //user cancelled
            return;
        }
        runFileData.setData(RunFileData.LastRunAttempt, rnc.getRunFile().getName());

        newDir = runDirFile.getName();
        runDir = runDirFile.getAbsolutePath();

        if (mis != null) {
            mis.setParameter(MakeInterpolatedStation.WINFILE_NAME, new File(runDir, "win_gen.win").getCanOrAbsPath());
        }

        //make the new run directory
        if (!runDirFile.mkdir()) {
            JOptionPane.showMessageDialog(parent, "Cannot create run directory with name " + newDir,
                    "Error (RP-005)", JOptionPane.ERROR_MESSAGE);
            removeListeners(rfd, cd);
            return;
        }

        //If there occurs a change in the rotation years in Mcrew,then that
        // change sould be reflected in the Run Dialog and also in the weps.run file [EndDate in weps.run should change accordingly]
        rfd.setRotationYears(ManageFile);
        rfd.updateDates();

        if (!rnc.getCreateBatch()) {
            this.runDialog = new RunDialog(parent, newDir, this);
            //	//System.out.println("Total years as in RunProgram:"+TotalYears);
            this.runDialog.setTotalYears(TotalYears);
            this.runDialog.setVisible(true);
        }

//        rfd.showRunFileData("RP:");

// add -b# for starting year flag

        //WINDGEN EXECUTABLE LOGIC
        if(windgenStation instanceof WindgenStation){
            WindgenStation station = (WindgenStation) windgenStation;
            //normal station, we generate a wind file with windgen
            List <String> windCmd = new ArrayList<String>();
            windCmd.add(WinExe);
            windCmd.add("-s " + String.valueOf(station.getWBan()));	// station number
            //Add the data file from the config
            WinCmd = Util.parse(WinCmd);
            parseCmd(WinCmd, windCmd);          // misc params
            if (startYear.length() > 0) {
                windCmd.add("-b" + startYear);	// start year
            }
            windCmd.add("-y" + TotalYears);     // number of years
            windCmd.add("-o" + new File(runDir, RunFileData.DefaultWinGenName).getAbsolutePath());	// output file name, always uses the default name
            String[] winArr = windCmd.toArray(new String[windCmd.size()]);
            commandsArr[0] = winArr;
            
        }
        else if (windgenStation instanceof FileStation){
            FileStation station = (FileStation) windgenStation;
            //file station, we use an already generated file.
            copyResult = copyFileStation(runDir, station);
            if (copyResult == false) {
                JOptionPane.showMessageDialog(parent,"Error in copying the Wind File. Aborting Run. ", "Error Copying Files", JOptionPane.ERROR_MESSAGE);
                return;
            }
            runDialog.JTF_win.setText("copied");

        }
        else if(windgenStation instanceof InterpolatedStation){
            //interpolated station
            runDialog.JTF_win.setText("interpolating...");
            mis = new MakeInterpolatedStation(null);
            mis.setParameter(MakeInterpolatedStation.INTERPOLATE1_EXE, winIntp1Exe);
            mis.setParameter(MakeInterpolatedStation.INTERPOLATE2_EXE, winIntp2Exe);
            mis.setParameter(MakeInterpolatedStation.WINDGEN_EXE, WinExe);
            mis.setParameter(MakeInterpolatedStation.LAT, String.valueOf(windgenStation.getLatLong().latitudeValue(NonSI.DEGREE_ANGLE)));
            mis.setParameter(MakeInterpolatedStation.LON, String.valueOf(windgenStation.getLatLong().longitudeValue(NonSI.DEGREE_ANGLE)));

            String winIdxFile = ConfigData.getDefault().getDataParsed(ConfigData.WinIndex);
            mis.setParameter(MakeInterpolatedStation.IDX_FILE, winIdxFile);
            mis.setParameter(MakeInterpolatedStation.SIM_START, startYear);
            mis.setParameter(MakeInterpolatedStation.SIM_LENGTH, TotalYears);

            String polFile = null;
            File pol = Util.findWindgenProjectionFile();
            if(pol == null || !pol.exists()){
                LOGGER.error("Unable to find windgen projection file.");
                return;
            }
            polFile = pol.getAbsolutePath();
            mis.setParameter(MakeInterpolatedStation.POL_FILE, polFile);

            String winDB = ConfigData.getDefault().getDataParsed(ConfigData.WinData);
            mis.setParameter(MakeInterpolatedStation.WDB, winDB);
            System.out.println("runDir " + runDir);
            mis.setParameter(MakeInterpolatedStation.WINFILE_NAME, new File(runDir, "interpolated.win").getCanOrAbsPath());
            mis.setParameter(MakeInterpolatedStation.WEIGHTSFILE_NAME, new File(runDir, "interpolated_weights.out").getCanOrAbsPath());
            

        }
        //END WINDGEN EXECUTABLE LOGIC


        //CLIGEN EXECUTABLE LOGIC
        if(cligenStation instanceof CligenStation){
            CligenStation station = (CligenStation) cligenStation;
            List<String> cliCmd = new ArrayList<String>();
            cliCmd.add(CliExe);
            cliCmd.add("-S" + station.getState());// state number
            cliCmd.add("-s" + String.valueOf(station.getId()));		// state name
            //Add the data file from the config
            CliCmd = Util.parse(CliCmd);
            parseCmd(CliCmd, cliCmd);			// misc params
            if (startYear.length() > 0) {
                cliCmd.add("-b" + startYear);           // start year
            }
            cliCmd.add("-y" + TotalYears);              // number of years
            cliCmd.add("-o" + new File(runDir, RunFileData.DefaultCliGenName).getAbsolutePath());	// output file name
            String[] cliArr = cliCmd.toArray(new String[cliCmd.size()]);
            commandsArr[1] = cliArr;
            
        }
        else if (cligenStation instanceof FileStation){
            FileStation station = (FileStation) cligenStation;
            copyResult = copyFileStation(runDir, station);
            if (copyResult == false) {
                JOptionPane.showMessageDialog(parent, "Error in copying the Climate File. Aborting Run. ", "Error Copying Files", JOptionPane.ERROR_MESSAGE);
                return;
            }
            runDialog.JTF_cli.setText("copied");
        }

        //END CLIGEN EXECUTABLE LOGIC


        List<String> wepsVec = new LinkedList<String>();
        wepsVec.add(WepsExe);
        if (calMode) {
            parseCmd(calWepsCmd, wepsVec);											// misc calib cmds
        }
        parseCmd(WepsCmd, wepsVec);													// misc cmds
        wepsVec.add("-P" + runDir);													// output file name
        String[] wepsArr = wepsVec.toArray(new String[1]);
        commandsArr[2] = wepsArr;

        //check that none of the commands are too long
        if(Util.isWindows()){
            for (String[] cmd : commandsArr){
                if(cmd == null){
                    continue;
                }
                for(String arg : cmd){
                    if (arg.length() > 259){
                        runDialog.setVisible(false);
                        JOptionPane pane = new JOptionPane("Command argument too long.\n" + cmd[0] + "\n" + arg, JOptionPane.ERROR_MESSAGE){

                            @Override
                            public int getMaxCharactersPerLineCount() {
                                return 80;
                            }

                        };

                        JDialog dialog = pane.createDialog("Command Argument Error");
                        dialog.setVisible(true);

                        return;
                    }
                }
            }
        }

        copyResult = copyManIFCNotes(runDir);
        if (copyResult == false) {
            JOptionPane.showMessageDialog(parent,
                    "Error in copying the Management or Soil Files. Aborting Run.", "Error Copying Files", JOptionPane.ERROR_MESSAGE);
            return;
        }
        runFileData.writeRunDataXML(CurrentProj);
        runFileData.writeRunFile(runDir);



        mainRunThread = new Thread(this, "RunProgram");

        if (rnc.getCreateBatch()) {
            writeBatchScript(runDirFile);
        } else {
            //start executing
            mainRunThread.start();
        }

        removeListeners(rfd, cd);

//		//System.out.println("RP_constructor at the END:");
    }

    private void writeBatchScript(File runDirFile) {
        //prepare the variables
        Map<String, String> variables = new LinkedHashMap<String, String>();
        //windgen exe
        File windgenExe = new File(WinExe);
        variables.put("windgen_path", unixifyPath(windgenExe.getParentFile().getAbsolutePath()));
        variables.put("windgen_exe", windgenExe.getName());
        variables.put("windgen_station", WinGenNumber);
        variables.put("windgen_output", "win_gen.win");
        File windgenDb = new File(configData.getDataParsed(ConfigData.WinData));
        variables.put("windgen_db_path", unixifyPath(windgenDb.getParentFile().getAbsolutePath()));
        variables.put("windgen_db_file", windgenDb.getName());

        //cligen exe
        File cligenExe = new File(CliExe);
        variables.put("cligen_path", unixifyPath(cligenExe.getParentFile().getAbsolutePath()));
        variables.put("cligen_exe", cligenExe.getName());
        variables.put("cligen_state", CliGenState);
        variables.put("cligen_station", CliGenStation);
        variables.put("cligen_output", "cli_gen.cli");
        File cligenDb = new File(configData.getDataParsed(ConfigData.CliData));
        variables.put("cligen_db_path", unixifyPath(cligenDb.getParentFile().getAbsolutePath()));
        variables.put("cligen_db_file", cligenDb.getName());

        //weps exe
        File wepsExe = new File(WepsExe);
        variables.put("weps_path", unixifyPath(wepsExe.getParentFile().getAbsolutePath()));
        variables.put("weps_exe", wepsExe.getName());

        //number of rotation years
        variables.put("totalyears", TotalYears);

        //the random number seeds
        variables.put("random", null);
        variables.put("random_cli", null);
        variables.put("random_wind", null);
        //end preparing the variables

        BufferedWriter sh = null;
        try {
            File shFile = new File(runDirFile, "run.sh");
            sh = new BufferedWriter(new FileWriter(shFile, false));
            //Script header information
            //Use the bash interpretor
            sh.write("#!/bin/bash\n");
            //Date created
            sh.write("# Created: " + new Date().toString() + "\n");
            //brief summary of the magic this script can do
            sh.write("# Makes a complete WEPS run, including generation of windgen and cligen files.\n");
            sh.write("\n");

            //script variables
            //name of the script
            sh.write("# ---------- SCRIPT VARIABLES----------\n");
            sh.write("name=\"" + shFile.getName() + "\"\n");
            sh.write("stdout=\"stdout.txt\"\n");
            sh.write("stderr=\"stderr.txt\"\n");
            sh.write("force=0\n");
            sh.write("\n");

            //usage information
            sh.write("# ---------- USAGE INFORMATION ----------\n");
            sh.write("function printUsage {\n");
            sh.write("\techo \"Usage: $name [OPTIONS] [--VARIABLE=VALUE]\"\n");
            sh.write("\techo \"Makes a complete WEPS run, including generation of windgen and cligen files.\"\n");
            sh.write("\techo\n");
            sh.write("\techo \"  -f, --force		force execution when data already exists\"\n");
            sh.write("\techo \"  -h, --help		display this help and exit\"\n");
            sh.write("\techo \"      --version		display version information and exit\"\n");
            sh.write("\techo\n");

            //print out all the variables that are defined
            sh.write("\techo \"Supported Variables:\"\n");
            for (String key : variables.keySet()) {
                sh.write("\techo \"      --" + key + "\"\n");
            }

            sh.write("}\n");    //end usage function
            sh.write("\n");

            sh.write("function printVersion {\n");
            sh.write("\techo \"$name 0.9\"\n");
            sh.write("\techo \"Written by Joseph Levin and Larry Wagner\"\n");
            sh.write("}\n");    //end version function
            sh.write("\n");

            //command line arguments
            sh.write("# ---------- COMMAND LINE ARGUMENTS ----------\n");
            sh.write("function parseArguments {\n");

            StringBuffer variableOptions = new StringBuffer();
            for (String key : variables.keySet()) {
                variableOptions.append("," + key + ":");
            }

            sh.write("\targs=`getopt -o hf -l help,force,version" + variableOptions.toString() + "  -n \"$name\" -- \"$@\"`\n");
            sh.write("\tif test $? != 0 ; then\n");
            sh.write("\t\techo \"Try '$name --help' for more information.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("\teval set -- \"$args\"\n");
            sh.write("\twhile true ; do\n");
            sh.write("\t\tcase \"$1\" in\n");

            sh.write("\t\t\t--) shift ; break ;;\n");
            sh.write("\t\t\t-h|--help) printUsage ; exit ; shift ;;\n");
            sh.write("\t\t\t--version) printVersion ; exit ; shift ;;\n");
            sh.write("\t\t\t-f|--force) force=1 ; shift ;;\n");

            //handle the run variables
            sh.write("\t\t\t#handle the run variables\n");
            for (String key : variables.keySet()) {
                sh.write("\t\t\t--" + key + ") " + key + "=$2 ; shift 2 ;;\n");
            }

            sh.write("\t\t\t#Throw error if an option is not handled\n");
            sh.write("\t\t\t*) echo \"Internal error parsing command line arguments.\" ; exit 1 ;;\n");

            sh.write("\t\tesac\n");
            sh.write("\tdone\n");
            sh.write("}\n");    //end parseArguments function
            sh.write("\n");

            //test if the current directory is a run
            sh.write("function testIsRun {\n");
            sh.write("\t# Test if currently in a run directory.\n");
            sh.write("\tif [ ! -r weps.run ]; then\n");
            sh.write("\t\techo \"Script must be run from within a weps run directory.  Unable to read file weps.run.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end is run test
            sh.write("\n");

            //test if the run is clean
            sh.write("function testIsClean {\n");
            sh.write("\t#Test if this is a clean run\n");
            sh.write("\tif ([ -e ${stderr} ] || [ -e ${stdout} ]) && [ $force != 1 ]; then\n");
            sh.write("\t\techo \"Run directory already contains output data. Force execution with the -f, --force flag.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end clean test
            sh.write("\n");

            sh.write("# ---------- DEFAULT VARIABLE VALUES----------\n");
            sh.write("# If a variable is not provided through a command line argument \n");
            sh.write("# the default value is used.  These default values are generated\n");
            sh.write("# from the configuration used to create this script.\n");
            sh.write("function applyDefaults {\n");

            //write the default values for the variables.  Variables are only printed if they actually have a default value.
            for (Map.Entry<String, String> entry : variables.entrySet()) {
                String value = entry.getValue();
                if (value != null && value.length() > 0) {
                    //If the variable is not passed as an argument use the default from this run.
                    sh.write("\tif [ ! ${" + entry.getKey() + "} ] ; then\n");
                    sh.write("\t\t" + entry.getKey() + "=\"" + value + "\"\n");
                    sh.write("\tfi\n");
                    sh.write("\n");
                }
            }

            //default windgen random seed
            sh.write("\t#If the windgen or cligen seeds are not set and use the global random seed when specified.\n");
            sh.write("\tif [ ! $random_wind ] && [ $random ] ; then\n");
            sh.write("\t\trandom_wind=$random;\n");
            sh.write("\tfi\n");
            sh.write("\n");

            //default cligen random seed            
            sh.write("\tif [ ! $random_cli ] && [ $random ] ; then\n");
            sh.write("\t\trandom_cli=$random;\n");
            sh.write("\tfi\n");
            sh.write("\n");

            sh.write("}\n");    //end apply defaults
            sh.write("\n");

            sh.write("# ---------- UTILS----------\n");
            //clean
            sh.write("function clean {\n");
            sh.write("\t#Clean up the run directory\n");
            sh.write("\tif [ $force == 1 ]; then\n");
            sh.write("\t\techo \"Cleaning run directory...\"\n");
            sh.write("\t\trm -f *.out\n");
            sh.write("\t\trm -f *.win\n");
            sh.write("\t\trm -f *.cli\n");
            sh.write("\t\trm -f logfil.txt\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end clean
            sh.write("\n");

            sh.write("# ---------- EXECUTABLES----------\n");
            //windgen
            StringBuffer windArgs = new StringBuffer();
            for (int i = 1; i < commandsArr[0].length; i++) {
                String arg = commandsArr[0][i];
                if (windArgs.length() > 0) {
                    windArgs.append(" ");
                }

                if (arg.startsWith("-s")) {
                    windArgs.append("-s${windgen_station}");
                } else if (arg.startsWith("-f")) {
                    windArgs.append("-f${windgen_db}");
                } else if (arg.startsWith("-o")) {
                    windArgs.append("-o${windgen_output}");
                } else if (arg.startsWith("-y")) {
                    windArgs.append("-y${totalyears}");
                } else if (arg.startsWith("-r")) {
                    //ignore the random seed number argument because it is handle by the script
                } else {
                    windArgs.append(arg);
                }
            }

            sh.write("function execWindgen {\n");
            sh.write("\twindgen=\"${windgen_path}" + "/" + "${windgen_exe}\"\n");
            sh.write("\twindgen_db=\"${windgen_db_path}" + "/" + "${windgen_db_file}\"\n");
            sh.write("\n");

            sh.write("\tif [ ${random_wind} ] ; then\n");
            sh.write("\t\twindgen_args=\"" + windArgs.toString().trim() + " -r${random_wind}\"\n");
            sh.write("\telse\n");
            sh.write("\t\twindgen_args=\"" + windArgs.toString().trim() + "\"\n");
            sh.write("\tfi\n");
            sh.write("\n");
            windArgs = null;

            sh.write("\techo \"Generating wind data...\"\n");
            sh.write("\techo \"${windgen} ${windgen_args}\" >>${stdout}\n");
            sh.write("\t${windgen} ${windgen_args} 2>${stderr} 1>>${stdout}\n");
            sh.write("\tif [ $? -gt 0 ]; then\n");
            sh.write("\t\techo \"Error generating wind data.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end windgen
            sh.write("\n");

            //cligen
            //cligen
            StringBuffer cliArgs = new StringBuffer();
            for (int i = 1; i < commandsArr[1].length; i++) {
                String arg = commandsArr[1][i];
                if (cliArgs.length() > 0) {
                    cliArgs.append(" ");
                }

                if (arg.startsWith("-S")) {
                    cliArgs.append("-S${cligen_state}");
                }
                if (arg.startsWith("-s")) {
                    cliArgs.append("-s${cligen_station}");
                } else if (arg.startsWith("-i")) {
                    cliArgs.append("-i${cligen_db}");
                } else if (arg.startsWith("-o")) {
                    cliArgs.append("-o${cligen_output}");
                } else if (arg.startsWith("-y")) {
                    cliArgs.append("-y${totalyears}");
                } else if (arg.startsWith("-r")) {
                    //ignore the random seed number argument because it is handle by the script
                } else {
                    cliArgs.append(arg);
                }
            }
            sh.write("function execCligen {\n");
            sh.write("\tcligen=\"${cligen_path}" + "/" + "${cligen_exe}\"\n");
            sh.write("\tcligen_db=\"${cligen_db_path}" + "/" + "${cligen_db_file}\"\n");
            sh.write("\n");

            sh.write("\tif [ ${random_cli} ] ; then\n");
            sh.write("\t\tcligen_args=\"" + cliArgs.toString().trim() + " -r${random_cli}\"\n");
            sh.write("\telse\n");
            sh.write("\t\tcligen_args=\"" + cliArgs.toString().trim() + "\"\n");
            sh.write("\tfi\n");
            sh.write("\n");
            cliArgs = null;

            sh.write("\techo \"Generating climate data...\"\n");
            sh.write("\techo \"${cligen} ${cligen_args}\" >>${stdout}\n");
            sh.write("\t${cligen} ${cligen_args} 2>>${stderr} 1>>${stdout}\n");
            sh.write("\tif [ $? -gt 0 ]; then\n");
            sh.write("\t\techo \"Error generating climate data.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end cligen
            sh.write("\n");

            //weps
            StringBuffer wepsArgs = new StringBuffer();
            for (int i = 1; i < commandsArr[2].length; i++) {
                String arg = commandsArr[2][i];
                if (wepsArgs.length() > 0) {
                    wepsArgs.append(" ");
                }
                if (arg.startsWith("-P")) {
                    //ignore the location command argument, the script runs from the run directory
                } else {
                    wepsArgs.append(arg);
                }
            }

            sh.write("function execWeps {\n");
            sh.write("\n");
            sh.write("\tweps=\"${weps_path}" + "/" + "${weps_exe}\"\n");
            sh.write("\tweps_args=\"" + wepsArgs.toString().trim() + "\"\n");
            sh.write("\n");
            wepsArgs = null;
            sh.write("\techo \"Executing weps model...\"\n");
            sh.write("\techo \"${weps} ${weps_args}\" >>${stdout}\n");
            sh.write("\t${weps} ${weps_args} 2>>${stderr} 1>>${stdout}\n");
            sh.write("\tif [ $? -gt 0 ]; then\n");
            sh.write("\t\techo \"Error executing weps model.\"\n");
            sh.write("\t\texit 1\n");
            sh.write("\tfi\n");
            sh.write("}\n");    //end weps
            sh.write("\n");

            sh.write("# ---------- MAIN SCRIPT ENTRY POINT ----------\n");
            sh.write("testIsRun\n");
            sh.write("parseArguments \"$@\"\n");
            sh.write("testIsClean\n");
            sh.write("applyDefaults\n");
            sh.write("clean\n");
            sh.write("#execute the models\n");
            sh.write("echo \"Batch Script Execution:\" >${stdout}\n");
            sh.write("execWindgen\n");
            sh.write("execCligen\n");
            sh.write("execWeps\n");
            sh.write("echo \"Run script finished sucessfully.\"\n");
        } catch (IOException ioe) {
            LOGGER.error("Error writing sh file.", ioe);
        } finally {
            if (sh != null) {
                try {
                    sh.close();
                } catch (Exception e) {
                    LOGGER.error("Error closing file.", e);
                }
            }
        }
        try {
            Desktop.getDesktop().open(runDirFile);
        } catch (IOException ioe) {
            LOGGER.warn("Unable to open run directory.", ioe);
        }
    }

    private String unixifyPath(String path) {
        return path.replace("\\", "/");
    }

    private void parseCmd(String cmdLin, List cmdVec) {
        String[] args = cmdLin.split("(^-)|(\\s+-)", -1);
        for(String arg : args){
            if(arg.trim().length() > 0){
                cmdVec.add("-" + arg.trim());
            }
        }
    }

//    /**
//     * Copies files into run directory for weps.exe and starts a thread to make a simulation run.
//     * @param parent The dialog in which this GUI operates.
//     * @param rfd The object handle to runfiledata for accessing items and information through
//     * the object modifiers
//     * @param cd The configuraion data object handle to make use of setting details.
//     * @param cm True if calibration mode is ON else false.
//     * @param p The name of the project that this RUN file belongs to.
//     */
//    public RunProgram(Component parent, RunFileData rfd, ConfigData cd, boolean cm, String p) {
//        this(parent, rfd, cd, true);
//        projName = p;
//    //System.out.println("RP_Costructor 5 : Project Name : " + projName);
//    }
    private String checkExesExist() {
        File chkFil = new File(WinExe);
        if (!chkFil.exists()) {
            return WinExe;
        }
        chkFil = new File(CliExe);
        if (!chkFil.exists()) {
            return CliExe;
        }
        chkFil = new File(WepsExe);
        if (!chkFil.exists()) {
            return WepsExe;
        }
        return null;
    }

    private boolean checkManFormat() {
        File chkFile = new File(ManageFile);
        if(chkFile.getName().toLowerCase().endsWith(".xml")){
            return false;
        }
        else{
            return true;
        }
    }

    private boolean checkManExists() {
        File chkFile = new File(ManageFile);
        return chkFile.exists();
    }

    private boolean checkSoilExists() {
        File chkFile = new File(SoilFile);
        return chkFile.exists();
    }
    
    private boolean checkManCalibration(){
        ManageData man = new ManageData();
        man.readDataFile(ManageFile);
        
        List <String> msgs = new LinkedList<String>();
        if (man.checkCalibration(msgs) == ManageData.CHECK_FAILED) {
            WepsMessageLog log = new WepsMessageLog();
            for (String msg : msgs) {
                log.logMessage(WepsMessage.MessageSeverity.ERROR, msg);
            }
            WepsMessageDialog.showMessageList(null, "Management File Errors", "Unable to proceed with calibration run.\nErrors were detected with the management file.", CurrentProj, log.getMessages());
            return false;
        }
        return true;
    }

    /**
     * Entry point for testing standalone. Used if this dialog is rum as an independent application. if
     * executed, makes the GUI visible.
     * @param args These are the command line arguments passed to the main method.
     */
    static public void main(String args[]) {
        String projectsDir = "";
        WepsFileChooser wfc = new WepsFileChooser(WepsFileChooser.PROJECT, projectsDir,
                WepsFileChooser.SELECT);
        wfc.setCurrentDirectory(new de.schlichtherle.io.File("c:/weps/weps.install/projects"));
        RunFileData rfd = new RunFileData();
        //rfd.setSubmodelOutput(runFileData.getSubmodelOutput());
        //setOldSMOutput(rfd);
        if (wfc.showDialog(null) == JFileChooser.APPROVE_OPTION) {
            de.schlichtherle.io.File sf = new File(wfc.getSelectedFile());
            try {
                projectsDir = sf.getCanonicalPath();
            } catch (IOException e) {//System.err.println(
                // "Error getting canoncial path of projectsDir");
            }
            rfd.readRunData(projectsDir);
            rfd.showRunFileData("this is a test");
        } else {
            //System.err.println("No project directory selected");
            System.exit(1);
        }
        ConfigData cd = ConfigData.getDefault();


        RunProgram rp = new RunProgram();
//		private PropertyChangeSupport changes = new PropertyChangeSupport(this);
        rp.addPropertyChangeListener(cd);
        rp.changes.firePropertyChange(ConfigData.CurrentProj, null, projectsDir);
        rp.removePropertyChangeListener(cd);
        rfd.showRunFileData("Standalone test");
        JFrame jf = new JFrame();
        new RunProgram(jf, rfd, cd, false);
        try {
            Thread.sleep(50000);
        } catch (java.lang.InterruptedException x) {
        }
        System.exit(1);
    }

    private void removeListeners(RunFileData rfd, ConfigData cd) {

        this.addPropertyChangeListener(rfd);
        rfd.addPropertyChangeListener(this);
        this.addPropertyChangeListener(cd);
        cd.addPropertyChangeListener(this);

    }

    private boolean copyManIFCNotes(String runDir) {
        String fromFile;
        String toFile;
        String tmp;
        boolean retValue = false;

        // copy management file
        tmp = ManageFile;



        fromFile = tmp;
        if (tmp.lastIndexOf(File.separator) > 0) {
            toFile = runDir + tmp.substring(tmp.lastIndexOf(File.separator));
        } else {
            toFile = runDir + File.separator + tmp;
        }
        retValue = Util.copyFile(fromFile, toFile);

        // copy IFC file
        tmp = SoilFile;
        fromFile = tmp;
        if (tmp.lastIndexOf(File.separator) > 0) {
            toFile = runDir + tmp.substring(tmp.lastIndexOf(File.separator));
        } else {
            toFile = runDir + File.separator + tmp;
        }
        retValue = Util.copyFile(fromFile, toFile);
        return retValue;

    }




    private boolean copyFileStation( String runDir, FileStation station) {
        File fromFile = station.getFile();
        File toFile = new File(runDir, fromFile.getName());
        
        return Util.copyFile(fromFile, toFile);
    }

    

    /*********************************************************************** wjr */
    private void displayCalibReport(File calibFile, String myRunDir) {
        calibFactors = new CalFactorDialog(this, configData, runFileData);
//        int numCols = 0;
//        numCols = calibFactors.JTA_cropAttributes.getColumns();
        calibFactors.setLocation(50, 50);
        try {

            BufferedReader in = new BufferedReader(new FileReader(calibFile));
            String inpLin = null;
            while (true) {
                String tmpLin = in.readLine();
                if (tmpLin == null) {
                    break;
                }
                inpLin = tmpLin;
            }
            in.close();
            if (inpLin != null) {
                StringTokenizer st = new StringTokenizer(inpLin, "|");
                StringBuffer sb = new StringBuffer();
                String myNew = "";
                String adjustmentDate = "";
                String adjustmentCrop = "";
                String adjustmentValue = "";

                //System.out.println( " RP_displayCalibReport : Number of Columns: " + numCols );
                while (st.hasMoreTokens()) {
                    st.nextToken();
                    adjustmentDate = st.nextToken();
                    sb.append(" " + adjustmentDate + "\t");
                    st.nextToken();
                    adjustmentCrop = st.nextToken();
                    myNew = adjustmentCrop;

                    /* if( myNew.length() < ( numCols - 23 ) ){
                    for(int i = 0; i < ( ( numCols - 23 ) - myNew.length() ); i++ ){
                    myNew += " ";
                    }
                    }*/
                    sb.append(myNew);
                    adjustmentValue = st.nextToken();
                    sb.append("\t\t" + adjustmentValue + " " + "\n");
                    calibFactors.addCalibrationAdjustment(adjustmentDate, adjustmentCrop, adjustmentValue);
                }
//                oldNotes = new String(notesText);
                notesText = sb.toString() + "\n" + notesText;
//                changes.firePropertyChange(RunFileData.NotesText, null, notesText);
                                /*JOptionPane.showMessageDialog(parent,
                outLin,
                "Calibration Factors",
                JOptionPane.INFORMATION_MESSAGE);
                JOptionPane.showMessageDialog(parent,
                outLin,
                "Calib Factors",
                JOptionPane.ERROR_MESSAGE);*/

                String myCurrProjName = new File(myRunDir).getParentFile().getAbsolutePath();
                //if (myCurrProjName.lastIndexOf('.') > 0){
                //    myCurrProjName = myCurrProjName.substring(0, myCurrProjName.lastIndexOf('.'));
                //}
                //JTF_ProjectName.setText(myCurrProjName);
                if (calibFactors == null) {


                    String[] myFiles = new File(myRunDir).list();
                    String myManageFile = "";
                    for (int i = 0; i < myFiles.length; i++) {
                        if (myFiles[i].endsWith(".man")) {
                            myManageFile = myFiles[i];
                        }
                    }
                    if (myManageFile.lastIndexOf('.') > 0) {
                        myManageFile = myManageFile.substring(0, myManageFile.lastIndexOf('.'));
                    }

                    calibFactors.JTF_projName.setText(myCurrProjName);
                    calibFactors.JTF_runName.setText(newDir);
                    calibFactors.JTF_manFile.setText(myManageFile);
                    calibFactors.JTA_cropAttributes.setText(sb.toString());
                    calibFactors.toFront();
                    calibFactors.setVisible(true);
                } else {

                    java.io.File[] myFiles = new File(myRunDir).listFiles();

                    String myManageFile1 = "";
                    for (int i = 0; i < myFiles.length; i++) {
                        if (myFiles[i].getName().endsWith(".man")) {
                            calibFactors.setManagementFile(new File(myFiles[i]));
                            myManageFile1 = myFiles[i].getName();
                        }
                    }
                    if (myManageFile1.lastIndexOf('.') > 0) {
                        myManageFile1 = myManageFile1.substring(0, myManageFile1.lastIndexOf('.'));
                    }
                    calibFactors.JTF_projName.setText(myCurrProjName);
                    calibFactors.JTF_runName.setText(newDir);
                    calibFactors.JTF_manFile.setText(myManageFile1);
                    calibFactors.JTA_cropAttributes.setText(sb.toString());
                    calibFactors.updateManagementFile();
                    calibFactors.toFront();
                    calibFactors.setVisible(true);
                }
            }
        } catch (IOException e) {
            //System.err.println("RFD_rRF: " + e);
        } catch (java.lang.NullPointerException f) {
            JOptionPane.showMessageDialog(parent,
                    "Error loading run file",
                    "Error (RFD-001)",
                    JOptionPane.ERROR_MESSAGE);
            f.printStackTrace();
            return;
        }
    }

    /**
     * The runnable thread method ( for implements part of Runnable) used to concurrently execute similar
     * programs for additional load bearing like using multiple run files to check more results and generate
     * reports on different files, etc.
     *
     */
    @Override
    public void run() {
        //Log for this thread.
        WepsMessageLog log = new WepsMessageLog();

        PipeIn pipout = null;
        PipeIn piperr = null;
        PrintWriter outerr = null;
        PrintWriter outout = null;
        oldNotes = notesText;
        noteLock = true;
        boolean aborted = false;
        try {

            outerr = new PrintWriter(new BufferedWriter(new FileWriter(new File(runDir, RunFileData.StdErr).getAbsolutePath()))); // SEP01	RUN08
            outout = new PrintWriter(new BufferedWriter(new FileWriter(
                    new File(runDir, RunFileData.StdOut).getAbsolutePath())));// SEP01	RUN08
        } catch (IOException ex) {
            LOGGER.error("Could not open stdout or stderr", ex);
            JOptionPane.showMessageDialog(null, "Could not open stdout or stderr in RunProgram\nRun aborted", "Error",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }

        if (mis != null) {
            runDialog.JTF_win.setText("interpolating");
            mis.setOut(outout, outerr);
            mis.run();
            runDialog.JTF_win.setText("finished");
        }
        int rtnVal = -1;
        for (int idx = 0; idx < commandsArr.length; idx++) {
            if (idx == 0 && "2".equals(WinFlg)) {
                continue;
            }
            outout.println("");
            outerr.println("");
            if (commandsArr[idx] == null) {
                outout.println((idx == 0) ? "Wind file supplied" : "Climate file supplied");
                outerr.println((idx == 0) ? "Wind file supplied" : "Climate file supplied");
                continue;
            }
            for (int jdx = 0; jdx < commandsArr[idx].length; jdx++) {
                outout.println(commandsArr[idx][jdx]);
            }
            for (int jdx = 0; jdx < commandsArr[idx].length; jdx++) {
                outerr.println(commandsArr[idx][jdx]);
            }

            runDialog.JTF_arr[idx].setText("running");

            try {
                exeProcess = Runtime.getRuntime().exec(commandsArr[idx]);
                pipout = new PipeIn(outout, exeProcess.getInputStream(), this, idx);
                pipout.start();
                piperr = new PipeIn(outerr, exeProcess.getErrorStream(), null, idx);
                piperr.start();
                rtnVal = exeProcess.waitFor();
                //Wait for the pipes to finish writing their logs.

                while (piperr.isAlive() || pipout.isAlive()) {
                    Thread.sleep(100);
                }

            } catch (java.io.IOException i) {
                //System.err.println("rP: Error " + i);
                log.logMessage(new WepsMessage(i));
                Mantis.error("WEPS Error", i, runDir);

            } catch (java.lang.InterruptedException j) {
                //System.err.println("rP: Error " + j);
                log.logMessage(new WepsMessage(j));
            }

            if (pipout != null) {
                log.mergeLog(pipout.getLog());
            }
            if (piperr != null) {
                log.mergeLog(piperr.getLog());
            }

            if (log.getCount(WepsMessage.MessageSeverity.ERROR) > 0) {
                WepsMessageDialog.showMessageList(null, "Run Errors", CurrentProj, log.getMessages(WepsMessage.MessageSeverity.ERROR));
                aborted = true;
            }

            if (rtnVal != 0) {
                if (!stopFlag) {
                    JOptionPane.showMessageDialog(parent, "Error executing " + commandsArr[idx][0], "Abnormal Termination " + rtnVal, JOptionPane.ERROR_MESSAGE);
                    Mantis.message("WEPS Error", "Error executing " + commandsArr[idx][0], runDir);
                    aborted = true;
                    break;
                } else {
                    JOptionPane.showMessageDialog(parent, "Run canceled by user " + runDir, "Abnormal Termination", JOptionPane.WARNING_MESSAGE);
                    aborted = true;
                    break;
                }
            }

            runDialog.JTF_arr[idx].setText("finished");
        }

        outout.close();
        outerr.close();

        runDialog.runFlag = false;
        runDialog.JB_can.setEnabled(false);

        /* weps run completed successfully */
        if (exeProcess.exitValue() == 0 && !aborted) {
            File calibFile = new File(runDir, RunFileData.CalibFileName);
            if (calibFile.exists() && calibFile.length() != 0) {
                displayCalibReport(calibFile, runDir);
            }
            //Display Dialog of warnings. and update the notes variable with each warning.
            if (log.getCount(WepsMessage.MessageSeverity.WARNING) > 0) {
                runDialog.JTF_status.setText("Warnings...");
                //oldNotes = new String(notesText);
                //for(WepsMessage message : log.getMessages(WepsMessage.MessageSeverity.WARNING)){
                //notesText += message.toString() + "\n";
                //}
                notesText = "This WEPS Run generated one or more Warning messages. For detailed information about these Warnings, see this run's '" + RunFileData.WarningsFile + "' output file.\n\n" + notesText;
                try {
                    BufferedWriter out = new BufferedWriter(new FileWriter(new File(runDir, RunFileData.WarningsFile)));
                    out.write("Project: " + CurrentProj);
                    out.newLine();
                    out.write("Run: " + runDir);
                    out.newLine();
                    out.write("Timestamp: " + new Date().toString());
                    out.newLine();
                    out.newLine();
                    for (WepsMessage message : log.getMessages(WepsMessage.MessageSeverity.WARNING)) {
                        out.write(message.toString());
                        out.newLine();
                    }
                    out.close();

                } catch (IOException ioe) {
                    //System.err.println("Error writing weps warnings to log file.");
                    }
                WepsMessageDialog.showMessageList(null, "Warning Messages Generated", "", CurrentProj, WepsMessageDialog.OK_OPTION, log.getMessages(WepsMessage.MessageSeverity.WARNING));
            }
            noteLock = false;
            changes.firePropertyChange(RunFileData.NotesText, null, notesText);
            runFileData.writeNotesFile(runDir);
            changes.firePropertyChange(RunFileData.NotesText, null, oldNotes);
            //Delete files we've been told are not needed after a good run.
            purgeRunFiles();
            //WepsEvent.fireRun(this, new File(runDir), true );
            changes.firePropertyChange(Weps.WEPS_RUNCREATED, null, runDir);
            //Update the last run variable
            changes.firePropertyChange(RunFileData.LastRun, null, new File(runDir).getAbsolutePath());


            //display each of the reports selected for immediate viewing
            String reportsToViewString = configData.getData(ConfigData.ReportsView);
            String[] reportsToView = reportsToViewString.split(",", -1);
            for (String reportName : reportsToView) {
                reportName = reportName.trim();
                if (reportName.length() > 0) {
                    ReportPack pack = ReportManager.getDefault().getReportPack(new File(runDir), reportName);
                    pack.startFill();
                    ReportManager.getDefault().displayReport(runDir, reportName);
                }
            }


            // create pdf's for all reports
            Thread task = new Thread("RunProgram pdfs") {

                int state = 0;

                public void run() {
                    switch (state) {
                        case 0:
                            state = 1;

                            try {
                                EventQueue.invokeAndWait(this);
                            } catch (Exception ex) {
                            }

                            String reportsToPDFString = configData.getData(ConfigData.ReportsGeneratePDF);
                            String[] reportsToPDF = reportsToPDFString.split(",", -1);
                            for (String reportName : reportsToPDF) {
                                reportName = reportName.trim();
                                if (reportName.length() > 0) {
                                    try{
                                        ReportPack pack = ReportManager.getDefault().getReportPack(new File(runDir), reportName);
                                        pack.waitWhileFilling();
                                        ReportManager.getDefault().generateReportPDF(runDir, reportName);
                                    }
                                    catch(Throwable t){
                                        LOGGER.error("Unable to generate \"" + reportName + "\".pdf.", t);
                                        JOptionPane.showMessageDialog(parent, "Unable to generate \"" + reportName + "\".", "PDF Error", JOptionPane.ERROR_MESSAGE);
                                    }
                                }
                            }

                            state = 2;
                            EventQueue.invokeLater(this);

                            return;

                        case 1:
                            runDialog.JTF_status.setText("Generating reports...");
                            return;

                        case 2:
                            runDialog.setVisible(false);
                            runDialog.dispose();
                            runDialog = null;
                            break;
                    }
                }
            };

            task.start();

        } else {
            runDialog.setVisible(false);
            runDialog.dispose();
            runDialog = null;
            changes.firePropertyChange(Weps.WEPS_RUNCREATED, null, runDir);
        }

//        } catch (Exception e) {
//            runDialog.setVisible(false);
//            runDialog.dispose();
//            runDialog = null;
//        } 

    }

    /**
     * Inner class with a JDialog as the GUI interface to be used for the text messages to be
     * used as required for the Run program messages to be delivered.
     */
    public static class TextDialog extends JDialog implements ActionListener {

        /**
         * Tells if the text dialog box is active and running.
         */
        protected boolean active = false;
        /**
         * True, tells that thetext dialog is closed and not being used.
         */
        protected JButton closeb;
        /**
         * Label that will be placed as a component on the text/Jdialog
         */
        protected JLabel l1;
        // protected Button disableButton;  // the button which fires up this dialog

        /**
         * Default constructor for the dialog box that initializes it with some component specifics mentioned.
         *
         */
        public TextDialog() {
            //super();

            active = true;

            l1 = new JLabel();

            closeb = new JButton("Close");
            JPanel textp = new JPanel();
            textp.add(l1);
            JPanel closep = new JPanel();
            closep.add(closeb);

            getContentPane().add("North", textp);
            getContentPane().add("South", closep);
            //this.setLocationRelativeTo(runDialog);
            this.setLocation(200, 600);
            setSize(200, 100);
            //pack();
            closeb.addActionListener(this);
            setSize(this.getPreferredSize());

        }

        /**
         * Sets the title fo the dialog box in which the text dialog sits.
         * @param t The string that will be aasigned
         */
        public void mySetTitle(String t) {
            this.setTitle(t);
        }

        /**
         * This gets the label name from the string already assigned.
         * @return The current string that is assigned.
         */
        public String myLabelGetText() {
            return (l1.getText());
        }

        /**
         * This sets the label name to the string parameter passed.
         * @param labelText The sting value to be assigned
         */
        public void myLabelSetText(String labelText) {
            l1.setText(labelText);
        }

        /**
         * Recognizes event for keeping window visible/hidden.
         * @param e Action event to realize who the generator is.
         */
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == closeb) {
                active = false;
                // disableButton.setEnabled (true);
                setVisible(false);
                dispose();

            }
        }
    }

    /**
     * Wrapper for window tha t tracks exe progress. Replaces RunWindow class
     * @author wjr
     */
    class RunDialog extends usda.weru.weps.gui.RunDialog_n {

        RunProgram runProgram;
        boolean runFlag = true;
        JTextField[] JTF_arr = {JTF_win, JTF_cli, JTF_weps};

        RunDialog(Component parent, String title, RunProgram runProgram) {
            super();
            addHelp();
            this.runProgram = runProgram;
            setTitle("WEPS Run: " + title);
            setLocation(100, 100);

            setVisible(true);
            setResizable(true);
            JL_runName.setText(title);
            JL_runName.setToolTipText(title);

            pack();

            addWindowListener(new WindowAdapter() {

                @Override
                public void windowClosed(WindowEvent e) {
                    stopFlag = true;
                    if(exeProcess != null){
                        exeProcess.destroy();
                    }
                }
            });


        }

        /**
         * help stuff
         */
        private void addHelp() {
            CSH.setHelpIDString(JLabel4, "runWindGen_html");
            CSH.setHelpIDString(JTF_win, "runWindGen_html");

            CSH.setHelpIDString(JLabel2, "runCliGen_html");
            CSH.setHelpIDString(JTF_cli, "runCliGen_html");

            CSH.setHelpIDString(JLabel3, "runWepsModel_html");
            CSH.setHelpIDString(JTF_weps, "runWepsModel_html");

            CSH.setHelpIDString(JTF_status, "runStatusBar_html");
            CSH.setHelpIDString(JPB_weps, "runProgressBar_html");
            CSH.setHelpIDString(JB_can, "runCancelButton_html");

            CSH.setHelpIDString(this, "runDialog_html");

        }

        /**
         * Sets max number of years for JProgressBar
         *
         * @param tot
         */
        public void setTotalYears(String totstr) {
            try {
                JPB_weps.setMaximum(Integer.parseInt(totstr));
            } catch (java.lang.NumberFormatException e) {
                //System.err.println("RP: can't parse total years string " + e);
            }
        }

        /**
         * Sets current year from weps.exe
         *
         * @param cur year number
         */
        public void setCurrentYear(int cur) {
            JPB_weps.setValue(cur);
        }

        /**
         * Kills current weps.exe run
         *
         * @param event
         */
        public void JBCan_actionPerformed(java.awt.event.ActionEvent event) {
            // to do: code goes here.
            stopFlag = true;
            if(runProgram != null && runProgram.exeProcess != null){
                runProgram.exeProcess.destroy();
            }
            this.dispose();
        }
    }

    /**
     * Thread to receive weps.exe stdout or stderr.
     * Directs all output into a file.
     * Parses stdout and updates RunDialog
     * @author wjr
     */
    static class PipeIn extends Thread {

        private WepsMessageLog c_log;
        InputStream from;
        PrintWriter to;
        RunProgram rp;

        public PipeIn(PrintWriter to, InputStream from, RunProgram rp, int idx) {
            this.from = from;
            this.to = to;
            this.rp = rp;
//            this.idx = idx;

            c_log = new WepsMessageLog();
        }

        public WepsMessageLog getLog() {
            return c_log;
        }

        public void run() {

            String inpstr, trimstr, message = null;
            BufferedReader bid = new BufferedReader(new InputStreamReader(from), 10);

            try {
                while ((inpstr = bid.readLine()) != null) {
                    to.println(inpstr);
                    trimstr = inpstr.trim();

                    //Special run time catches
                    if (rp != null) {
                        //trim the input string so that the leading white spaces are removed-neha
                        if (trimstr.startsWith("Year")) {
                            rp.runDialog.JTF_status.setText(inpstr);
                            StringTokenizer st = new StringTokenizer(inpstr);
                            st.nextToken();
                            rp.runDialog.JPB_weps.setValue(Integer.parseInt(st.nextToken()));
                        }
                    }
                    //Catch Errors and warnings
                    if (trimstr.startsWith("Requested Station Not Found.")) {
                        //Cligen was not happy, unable to find desired station.
                        c_log.logMessage(WepsMessage.errorMessage("Selected cligen station does not exist in the cligen database being used."));
                    } else if (trimstr.startsWith("Error")) {
                        message = trimstr.replaceFirst("Error:", "");
                        message = message.replaceFirst("Error :", "");
                        c_log.logMessage(WepsMessage.errorMessage(message.trim()));
                    } else if (trimstr.startsWith("Warning")) {
                        message = trimstr.replaceFirst("Warning:", "");
                        message = message.replaceFirst("Warning :", "");
                        c_log.logMessage(WepsMessage.warningMessage(message.trim()));
                    }

                }
                to.flush();
            } catch (IOException e) {
                //System.err.println("PipeIn: " + e);
            }
        }
    }

    /**
     * Deletes wingen and cligen files
     * that are no longer needed after the weps simulation.
     */
    public void purgeRunFiles() {

        if(ConfigData.getDefault().isPurgeFlag()){
            //We have a filter to work with.

            String dirString = runDir;
            File dir = new File(dirString);
            String[] list = dir.list();            
            if (list == null) {
                return;
            }
            for (int i = 0; i < list.length; i++) {
                if (isPurgeable(list[i])){
                    File file = new File(dirString,list[i]);

                    LOGGER.info("Deleting: " + file.getAbsolutePath());
                    boolean deleted = file.delete();
                    if(!deleted){
                        LOGGER.warn("Unable to delete file: " + file.getAbsolutePath());
                    }
                }
            }
        }

       
    }

    private boolean isPurgeable(String name){
        String[] filters = ConfigData.getDefault().getPurgeFilenames();
        for(String filter : filters){
            if(filter.equalsIgnoreCase(name)){
                return true;
            }
        }
        return false;

    }

    /**
     * Recognizes and takes appropriate actions on registered properties from different
     * screens to update and synchronize data and GUI screens as needed.
     * @param e The event itself that is responsible for triggering the change required for registered
     * properties/components.
     */
    @Override
    public void propertyChange(PropertyChangeEvent e) {
////System.out.println("pC: " + e.getPropertyName());
        if (e.getPropertyName().equals(ConfigData.WinExe)) {
            WinExe = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.WinGenStation)) {
            WinGenNumber = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.WinCmd)) {
            WinCmd = (String) e.getNewValue();        
        } else if (e.getPropertyName().equals(ConfigData.WindInterp1EXE)) {
            winIntp1Exe = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.WindInterp2EXE)) {
            winIntp2Exe = (String) e.getNewValue();
        } 

        else if (e.getPropertyName().equals(RunFileData.TotalYears)) {
            TotalYears = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.StartDate)) {
            String StartDate = (String) e.getNewValue();
            String[] tmpArr = StartDate.split(" +");
            startYear = tmpArr[2];
        } else if (e.getPropertyName().equals(ConfigData.CliExe)) {
            CliExe = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.CliGenState)) {
            CliGenState = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.NotesText)) {
            if (!noteLock) {
                notesText = (String) e.getNewValue();
            } else {
                oldNotes = (String) e.getNewValue();
            }
        } else if (e.getPropertyName().equals(RunFileData.CliGenStation)) {
            CliGenStation = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.CliCmd)) {
            CliCmd = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.WepsExe)) {
            WepsExe = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.WepsCmd)) {
            WepsCmd = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.CalWepsCmd)) {
            calWepsCmd = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.WindFlag)) {
            WinFlg = (String) e.getNewValue();
        //System.out.println("RP_pC: winflg " + WinFlg);
        } else if (e.getPropertyName().equals(RunFileData.ClimateFlag)) {
            CliFlg = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(ConfigData.CurrentProj)) {
            CurrentProj = Util.parse(e.getNewValue().toString());
        } else if (e.getPropertyName().equals(RunFileData.SoilFile)) {
            SoilFile = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.ManageFile)) {
            ManageFile = (String) e.getNewValue();
        //changes.firePropertyChange(e);
        } else if (e.getPropertyName().equals(RunFileData.WindFile)) {
            WinGen = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.ClimateFile)) {
            CliGen = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.LastRun)) {
            lastSuccessfulRun = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.XLength)) {
            xLength = (String) e.getNewValue();
        } else if (e.getPropertyName().equals(RunFileData.YLength)) {
            yLength = (String) e.getNewValue();
        } 
    }
    private PropertyChangeSupport changes = new PropertyChangeSupport(this);

    /**
     * Allows the container to add or register some other components to recognize the changes that occur
     * on this component.
     * @param l The listener that listens and reacts towards the the changes to be reflected.
     */
    public void addPropertyChangeListener(PropertyChangeListener l) {
        changes.addPropertyChangeListener(l);
    }

    /**
     * Allows the container to remove or de-register some other components and stop recognizing the
     * changes that occur on this component.
     * @param l The listener that does not listen and react towards the the changes to be reflected.
     */
    public void removePropertyChangeListener(PropertyChangeListener l) {
        changes.removePropertyChangeListener(l);
    }
}

