package usda.weru.weps;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileWriter;
import java.awt.Desktop;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import usda.weru.util.ConfigData;

/**
 *
 * @author maxerdwien
 */
public class ScriptWriter {
    
    private static final Logger LOGGER = Logger.getLogger(ScriptWriter.class);
    
    /**
     * Creates a batch file (.sh) that can make a weps run using a weps.run file.
     *
     * @param runDirFile
     * @param configData
     * @param WinExe
     * @param WinGenNumber
     * @param CliExe
     * @param CliGenState
     * @param CliGenStation
     * @param WepsExe
     * @param TotalYears
     * @param commandsArr 
     */
    public static void writeBatchScript(TFile runDirFile, ConfigData configData,
            String WinExe, String WinGenNumber, String CliExe, String CliGenState,
            String CliGenStation, String WepsExe, String TotalYears, String[][] commandsArr) {
        
        //prepare the variables
        Map<String, String> variables = new LinkedHashMap<String, String>();
        //windgen exe
        TFile windgenExe = new TFile(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");
        TFile windgenDb = new TFile(configData.getDataParsed(ConfigData.WinData));
        variables.put("windgen_db_path", unixifyPath(windgenDb.getParentFile().getAbsolutePath()));
        variables.put("windgen_db_file", windgenDb.getName());

        //cligen exe
        TFile cligenExe = new TFile(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");
        TFile cligenDb = new TFile(configData.getDataParsed(ConfigData.CliData));
        variables.put("cligen_db_path", unixifyPath(cligenDb.getParentFile().getAbsolutePath()));
        variables.put("cligen_db_file", cligenDb.getName());

        //weps exe
        TFile wepsExe = new TFile(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 {
            TFile shFile = new TFile(runDirFile, "run.sh");
            sh = new BufferedWriter(new TFileWriter(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");

            StringBuilder variableOptions = new StringBuilder();
            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
            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 (IOException e) {
                    LOGGER.error("Error closing file.", e);
                }
            }
        }
        try {
            Desktop.getDesktop().open(runDirFile);
        } catch (IOException ioe) {
            LOGGER.warn("Unable to open run directory.", ioe);
        }
    }
    
    private static String unixifyPath(String path) {
        return path.replace("\\", "/");
    }
}
