package usda.weru.weps.serverControl;

import de.schlichtherle.truezip.file.TFileReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import usda.weru.util.Mantis;
import usda.weru.util.WepsMessage;
import usda.weru.util.WepsMessageLog;
import csip.utils.Client;
import csip.utils.JSONUtils;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.zip.ZipFile;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import systems.uom.common.USCustomary;
import usda.weru.gis.latlong.LatLong;
import usda.weru.util.ConfigData;
import static usda.weru.util.ConfigData.isNRCSmode;
import usda.weru.util.Util;
import static usda.weru.util.Util.extractZipFile;
import usda.weru.weps.wepsRunControl.WrcPipeIn;
import weps.ws.Csip.utils.WepsCsipUtil;
import usda.weru.weps.wepsRunControl.WrcBackgroundIf;
import usda.weru.weps.wepsRunControl.WrcRunModelClimate;
import usda.weru.weps.wepsRunControl.WrcRunModelWeps.WepsIntegratedParms;

/**
 *
 * @author mhaas
 */
public class ServerControlExec {
    
    private static final Logger LOGGER = LogManager.getLogger(ServerControlExec.class);

    private final WepsMessageLog log;
    private final PrintWriter writerStdOut;
    private final PrintWriter writerStdErr;
    private final WrcBackgroundIf bgIf;
    private final String outDir;
    private final ServerControlData scData;
    private final String manageFname;
    private final String soilFname;
    private String windInputFileName;
    
    private Process execExeProcess;
    private boolean isWepsIntegrated = false;
    WepsIntegratedParms integratedParms = null;

    //
    // Need to control RunDir for the Runtime.exec function. Calling exec with run dir specified allows a
    // dialog to show at completion stating "Fortran program complete". If exec w/ no rundir specified is used,
    // the dialog does not appear, which is desired behavior.  (documentation says that the exec call w/ no rundir
    // is a "convenience" definition and equates to calling the exec call w/ rundir=null. This behavior differs
    // in this way between the two call invocations - so there is some difference between the two forms)
    // However, the MakeInterpolatedStation runProgram
    // is designed to need a rundir specified (no dialogs appear from those exec calls ... ).  So there are 
    // 2 modes for this serrvice: runDir=null and we make the exec w/o that parm, or runDir set and we use that parm.
    //
    // outDir is the same as runDir.  We need that specified for every invocation, even if runDir is being set to null.
    
    public ServerControlExec (WepsMessageLog log, 
                              PrintWriter writerStdOut, 
                              PrintWriter writerStdErr, 
                              WrcBackgroundIf bgIf, 
                              String outDir,
                              String manageFname, 

                              String soilFname) {
        this.log = log;
        this.writerStdOut = writerStdOut;
        this.writerStdErr = writerStdErr;
        this.bgIf = bgIf;
        this.outDir = outDir;
        this.manageFname = manageFname;
        this.soilFname = soilFname;
        
        scData = new ServerControlData ();
        
        // default windgen input filename for weps
        windInputFileName = "win_gen.win";
    }

    /*
    ** Only for using the file functions, NOT for executing.  Use above.
    */
    public ServerControlExec (String outDir) {
        this (null,null,null,null,outDir,null,null);
    }
    
    public void setWepsWindInputFilename (String name) {
        windInputFileName = name;
    }

    //
    // The main 2 modes of calling: 
    //   local (the machine on which the WEPS GUI is executing)
    //   remote (an internet server hosting the model via the CSIP interface)
    //
    public int doExec (String[] cmdLine) throws ServerControlException {
        return (scData.chkExecOnServerSet(cmdLine[0]) ? doExecCsip (cmdLine) : doExecLocal (cmdLine));
    }
    public int doExec (ArrayList<String> cmds) throws ServerControlException {
        return (doExec(cmds.toArray(new String[0])));
    }
                
    private int doExecLocal (String[] cmdLine) {
        int rtnVal = -1;
        WrcPipeIn pipeStdOut = null;
        WrcPipeIn pipeStdErr = null;

        try {
            // MEH: need to use canonical or absolute path (not relative) in order for System.setProperty("user.dir" .... to function
            // need this so the -working option works, so we can function in s WebStart scenario
            cmdLine[0] = cmdLine[0].replace("\""," ").trim();  //some cmdLines are enclosed in quotes
            cmdLine[0] = new TFile(cmdLine[0]).getCanOrAbsPath();

            if (scData.findCdBaseFromExe(cmdLine[0]) == ServerControlData.cdKeyCligenBase) {
                // if this is a local cligen exec, 
                // check if we need to add local Prism data into the specified cligen input record.
                if (ConfigData.getDefault().getData("CD-SC-button-cligenPrismMode").toLowerCase().contains("prism")) {
                    
                }
            }
            
            ArrayList<String> cmdList = new ArrayList<String>();
            for (String s : cmdLine) {
                cmdList.add(s);
            }            
            ProcessBuilder pb = new ProcessBuilder(cmdList);
            pb.directory(new File(outDir));
            
            execExeProcess = pb.start();
            pipeStdOut = new WrcPipeIn(execExeProcess.getInputStream(), writerStdOut, bgIf, 1);
            pipeStdOut.start();
            pipeStdErr = new WrcPipeIn(execExeProcess.getErrorStream(), writerStdErr, null, 1);
            pipeStdErr.start();
            rtnVal = execExeProcess.waitFor(); //
            
            while (execExeProcess.isAlive()) {
                if (bgIf.bkgndGetStopFlag()) {
                    execExeProcess.destroy();
                    break;
                }
                Thread.sleep(100);
            }
            if (bgIf.bkgndGetStopFlag()) {
                rtnVal = -1;
            } else {
                rtnVal = execExeProcess.exitValue();
            }
            
            //Wait for the pipes to finish writing their logs.
            while (pipeStdErr.isAlive() || pipeStdOut.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, outDir);

        } catch (java.lang.InterruptedException j) {
            //System.err.println("rP: Error " + j);
            log.logMessage(new WepsMessage(j));
        }

        if (pipeStdOut != null) {
            log.mergeLog(pipeStdOut.getLog());
        }
        if (pipeStdErr != null) {
            log.mergeLog(pipeStdErr.getLog());
        }

        return (rtnVal);
    }
    

    /*
    // Required library additions for using the csip interface code
    //
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\dist\csip-core.jar
    //Java EE 6 API Library
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\json-org.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\httpclient-4.3.3.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\httpcore-4.3.2.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\httpmime-4.3.3.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\commons-logging-1.1.1.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\commons-io-2.3.jar
    //C:\Users\mhaas\Documents\NetBeansProjects\csip-core\lib\csip-jersey-2.16\jettison-1.3.3.jar
    */
    
    // Execute the model on the remote server
    private int doExecCsip (String[] cmdLine) throws ServerControlException {
        int rtnVal = -1;
        Client client;
        JSONObject modelParms;
        JSONObject modelResult;
        String modelStatusStr;
        String modelUrl;
        File[] files;
        String downloadRes[];
        parmRet loadParms = new parmRet();

        // Client need a java logger
        java.util.logging.Logger clientLog = java.util.logging.Logger.getLogger(ServerControlExec.class.getName());
        clientLog.setLevel(java.util.logging.Level.INFO);
        client = new Client(clientLog);

        boolean toDispose = false;
        try {
            bgIf.bkgndDlgWriteStatus("running on cloud");

            // Determine which model we are executing
            modelUrl = scData.getEndpoint(cmdLine[0]);
            // Load the parms for the specific model
            switch (scData.findCdBaseFromExe(cmdLine[0])) {
                case (ServerControlData.cdKeyInterpBase):
                    loadParms = loadParmsInterpolate(cmdLine);
                    writeJsonToFile(loadParms.modelParms, new File(outDir,"interpolateReqJson.txt"), modelUrl);
                    break;
                case (ServerControlData.cdKeyInterpGendataBase):
                    loadParms = loadParmsInterp_wdb(cmdLine);
                    break;
                case (ServerControlData.cdKeyWindgenBase):
                    loadParms = loadParmsWind(cmdLine);
                    writeJsonToFile(loadParms.modelParms, new File(outDir,"windgenReqJson.txt"), modelUrl);
                    break;
                case (ServerControlData.cdKeyCligenBase):
                    if (modelUrl.toLowerCase().contains("cligen_prism") && modelUrl.toLowerCase().contains("wepp") ) {
                        // if this is a wepp call, load those parms
                        loadParms = loadParmsClimateWepp(cmdLine);
                    } else {
                        loadParms = loadParmsClimate(cmdLine);
                    }
                    writeJsonToFile(loadParms.modelParms, new File(outDir,"cligenReqJson.txt"), modelUrl);
                    break;
                case (ServerControlData.cdKeyWepsBase):
                    loadParms = loadParmsWeps(cmdLine);
                    
                    if (isWepsIntegrated) {
                        loadParms = modParmsWepsForWepsIntegrated(loadParms);
                    }
                    
                    writeJsonToFile(loadParms.modelParms, new File(outDir,"wepsReqJson.txt"), modelUrl);
                    break;
            }
            
            modelParms = loadParms.modelParms;
            files = loadParms.files;
            
            try {
                modelResult = doCsipAsync(client, modelUrl, modelParms, files);
            } catch (ServerControlException e) {
                throw new ServerControlException("doCsipAsync() in doExecCsip failed:\n"+e.getMessage(),e.getCause(),"doExecCsip");
            }
            
            int ttttt = modelUrl.indexOf("/m/")+3;
            writeJsonToFile(modelResult, 
                    new File(outDir,"csip-"+modelUrl.substring(ttttt,ttttt+4)+"-result.txt"), 
                    "Debug");
            
            // get the protocol from the url specifying the model call
            // (it may need to be substituted into the reult file specifiers).
            String CsipUrlProtocol = WepsCsipUtil.getUrlProtocol(modelUrl);
            // Get the result files from the service
            downloadRes = downloadCsipResults (modelResult,new File(outDir),client,CsipUrlProtocol);

            // if this was a wepp call, need to rename output files to match what the GUI is using
             if (modelUrl.toLowerCase().contains("cligen_prism") && modelUrl.toLowerCase().contains("wepp") ) {
                Files.move(new File(outDir, "wepp.cli").toPath(), new File(outDir, "cli_gen.cli").toPath(), StandardCopyOption.REPLACE_EXISTING);
                Files.move(new File(outDir, "base.par").toPath(), new File(outDir, "cligenWeppReturnCli.par").toPath(), StandardCopyOption.REPLACE_EXISTING);
            }
            
            if (isWepsIntegrated) {
                File results = new File(outDir, "results.zip");
                if (results.exists()) {
                    File zipDir = new File(outDir);
                    ZipFile resultsZip = new ZipFile(results);
                    try {
                        extractZipFile(resultsZip, zipDir);
                        resultsZip.close();
                    } catch (IOException ex) {
                        log.logMessage(WepsMessage.MessageSeverity.ERROR, "UnZip CSIP results failed");
                        throw new ServerControlException("UnZip CSIP results failed", ex,"doExecCsip");
                    }
                    
                    // move all interpolate results into the interpolate subdir
                    File interpZip = new File(outDir, "interpolate.zip");
                    if (interpZip.exists()) {
                        try {
                            ZipFile interpResultsZip = new ZipFile(interpZip);
                            File srcDir = zipDir;
                            File destDir = new File(zipDir,"interpolate");

                            final Enumeration<? extends de.schlichtherle.truezip.zip.ZipEntry> entries = interpResultsZip.entries();
                            while (entries.hasMoreElements()) {
                                final de.schlichtherle.truezip.zip.ZipEntry entry = entries.nextElement();
                                final String entryName = entry.getName();
                                File srcFile = new File(srcDir, entryName);
                                File destFile = new File(destDir, entryName);
                                Files.move(srcFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                            }
                            interpResultsZip.close();

                            // find the stdout & stderr files from the wepsIntegrated
                            // results and put them in the expected variables (downloadRes).
                            File [] stdFiles;
                            stdFiles = zipDir.listFiles(new FilenameFilter() {
                                @Override
                                public boolean accept(File dir, String name) {
                                    return name.matches("weps.*?stdout.txt");
                                }
                            });
                            downloadRes[0] = stdFiles[0].getName();
                            stdFiles = zipDir.listFiles(new FilenameFilter() {
                                @Override
                                public boolean accept(File dir, String name) {
                                    return name.matches("weps.*?stdout.txt");
                                }
                            });
                            downloadRes[1] = stdFiles[0].getName();
                        } catch (IOException ex) {
                            log.logMessage(WepsMessage.MessageSeverity.ERROR, "moving integrated interp results failed");
                        }
                    }
                    
                    // delete zips
                    try {
                        Files.deleteIfExists(new File(zipDir,"interpolate.zip").toPath());
                    } catch (IOException ex) {
                        log.logMessage(WepsMessage.MessageSeverity.ERROR, "deleting integrated interpolate.zip failed");
                    }
                    try {
                        Files.deleteIfExists(new File(zipDir,"logFiles.zip").toPath());
                    } catch (IOException ex) {
                        log.logMessage(WepsMessage.MessageSeverity.ERROR, "deleting integrated logFiles.zip failed");
                    }
                    try {
                        Files.deleteIfExists(new File(zipDir,"results.zip").toPath());
                    } catch (IOException ex) {
                        log.logMessage(WepsMessage.MessageSeverity.ERROR, "deleting integrated results.zip failed");
                    }
                }
            }
            
            // Append the downloaded stdout & stderr to the existing stdout & stderr
            if (downloadRes[0] != null) {
                File serverOutFile = new File (outDir + "/" + downloadRes[0]);
                appendFile (serverOutFile, writerStdOut);
                // Need to extract log info from stdout & stderr (see comment @ scanExecOutFiles)
                log.mergeLog(scanExecOutFiles (new FileInputStream(serverOutFile)));
            }
            if (downloadRes[1] != null) {
                File serverErrFile = new File (outDir + "/" + downloadRes[1]);
                appendFile (serverErrFile, writerStdErr);
                // Need to extract log info from stdout & stderr (see comment @ scanExecOutFiles)
                log.mergeLog(scanExecOutFiles (new FileInputStream(serverErrFile)));
            }
            
            modelStatusStr = ((JSONObject)(modelResult.get("metainfo"))).getString("status");
            rtnVal = modelStatusStr.toLowerCase().contains("finished") ? 0 : -1 ;
            
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "Csip execution failed");
            if (ex instanceof ServerControlException) {
                throw ((ServerControlException)ex);
            } else {
                throw new ServerControlException("Csip execution failed", ex,"doExecCsip");
            }
        }
        
        bgIf.bkgndDlgWriteStatus("");
        return (rtnVal);
    }

    //
    // A return class for returning multiple results from loadParams() call
    //
    private class parmRet {
        public final JSONObject modelParms;
        public final File [] files;
        parmRet (JSONObject modelParms, File [] files) {
            this.modelParms = modelParms;
            this.files = files;
        }
        parmRet () {
            this (new JSONObject(), new File[0]);
        }
    }
      
    private JSONArray initParmsArrCommon () throws JSONException {
        JSONArray jsonParmsArr = new JSONArray();
        jsonParmsArr.put(new JSONObject().put("name","isNRCS").put("value",
                ConfigData.getDefault().getData(isNRCSmode).startsWith("1") ? true : false));
        return jsonParmsArr;
    }
    
    private parmRet loadParmsClimate (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
        ArrayList<File> fileList = new ArrayList<>();
        String[] latlonStr = null;
        String stateId = "";
        
        ConfigData configData = ConfigData.getDefault();
        String parmInputFile = null;
        boolean sendDbFileWithRequest = configData.getData("CD-SC-button-cligenSendDb").toLowerCase().startsWith("yes");

        for (String cmdOpt : cmdLine) {
            String argType = cmdOpt.substring(0, 2);
            //System.out.println("argType: " + argType);
            if (argType.contains("-S")) {
                stateId = cmdOpt.substring(2).trim();
                jsonParmsArr.put(new JSONObject().put("name","stateID").put("value",stateId) );
            } else if (argType.contains("-s")) {
                jsonParmsArr.put(new JSONObject().put("name","stationID").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-b")) {
                jsonParmsArr.put(new JSONObject().put("name","startyear").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-y")) {
                jsonParmsArr.put(new JSONObject().put("name","duration").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-i")) {
                if (sendDbFileWithRequest) {
                    parmInputFile = cmdOpt.substring(2);
                    jsonParmsArr.put(new JSONObject().put("name","dataFile").put("value", new File(parmInputFile).getName() ));
                    fileList.add(parmInputFile.startsWith("/") ? new File(parmInputFile) : new File(outDir + "/" + parmInputFile));
                }
            } else if (argType.contains("-L")) {
                // this is the data required by cligen_prism and is passed on to the prism_normal service
                latlonStr = cmdOpt.substring(2).trim().split(";");
            }
        }
        
        String cligenDataVer = configData.getData("CD-SC-button-cligenDataVer");
        if (cligenDataVer.contentEquals("1992") || cligenDataVer.contentEquals("2015 International")) {
            // Add the prism mode parm
            jsonParmsArr.put( new JSONObject().put("name","dataVersion").put("value",cligenDataVer) );
        }
        
        if (sendDbFileWithRequest && parmInputFile == null) {
            parmInputFile = configData.getData("CD-cligen database");
            parmInputFile = Util.parse(parmInputFile);
            jsonParmsArr.put(new JSONObject().put("name","dataFile").put("value", new File(parmInputFile).getName() ));
        }
        
        String cligenVer = configData.getData("CD-SC-cligen-endpoint");
        if (cligenVer.contains("cligen_prism")) {
            
            // If this is a "new" version of the service, it needs additional parms.
            
            // Add the prism mode parm
            if (WrcRunModelClimate.isContinentalUS(stateId)) {
                jsonParmsArr.put( new JSONObject().put("name","usePRISM").put("value",
                                  configData.getData("CD-SC-button-cligenPrismMode").toLowerCase().contains("prism")) );
            } else {
                log.logMessage(WepsMessage.MessageSeverity.INFORMATION, "Cligen: not continental US, skipping PRISM");
                jsonParmsArr.put( new JSONObject().put("name","usePRISM").put("value", false) );
            }

            // add the input_zone_features parm, which contains the lat lon
            // this is the data required by cligen_prism and is passed on to the prism_normal service
            jsonParmsArr.put(getNewFeatureOBject(latlonStr));
        }
        
        if (configData.getData("CD-SC-button-cligenRetParFiles").contentEquals("Yes")) {
            jsonParmsArr.put( new JSONObject().put("name","returnParFiles").put("value", true) );
        }
        
        // output filename is a fixed paramter
        jsonParmsArr.put( new JSONObject().put("name","outputFile").put("value", "cli_gen.cli" ));

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // Input files to the service call
        files = fileList.toArray(new File[]{});
        
        return (new parmRet (modelParms,files));
    }

    private JSONObject getNewFeatureOBject (String latlonStr[]) throws JSONException {
        return new JSONObject (
                "{" +
                "\"name\": \"input_zone_features\"," +
                "\"value\": {" +
                "    \"type\": \"FeatureCollection\"," +
                "    \"features\": [{" +
                "        \"type\": \"Feature\"," +
                "        \"properties\": {" +
                "            \"name\": \"pt one\"," +
                "            \"gid\": 1" +
                "        }," +
                "        \"geometry\": {" +
                "            \"type\": \"Point\"," +
                "            \"coordinates\": [" +
                "                " + latlonStr[1] + "," +
                "                " + latlonStr[0] +
                "            ]," +
                "            \"crs\": {" +
                "                \"type\": \"name\"," +
                "                \"properties\": {\"name\": \"EPSG:4326\"}" +
                "            }" +
                "        }" +
                "    }]" +
                "}" +
                "}"
            );
    }
    
    private parmRet loadParmsClimateWepp (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
        String[] latlonStr = null;
        
        ConfigData configData = ConfigData.getDefault();
                
        for (String cmdOpt : cmdLine) {
            if (cmdOpt.contains("-L")) {
                // this is the data required by cligen_prism and is passed on to the prism_normal service
                latlonStr = cmdOpt.substring(2).trim().split(";");
            }
        }
        
        // Add the prism mode parm
        jsonParmsArr.put( new JSONObject().put("name","usePRISM").put("value",
                          configData.getData("CD-SC-button-cligenPrismMode").toLowerCase().contains("prism")) );
        // Add the climateDataVersion parm : 1992 or 2015
        jsonParmsArr.put( new JSONObject().put("name","climateDataVersion").put("value", "1992") );

        // add the input_zone_features parm, which contains the lat lon
        // this is the data required by cligen_prism and is passed on to the prism_normal service
        jsonParmsArr.put(getNewFeatureOBject(latlonStr));

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // Input files to the service call
        files = new File[]{};
        
        return (new parmRet (modelParms,files));
    }

    
    private parmRet loadParmsWind (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
        ArrayList<File> fileList = new ArrayList<>();
            
        //String parmDBFile = "win_gen.wdb";
        String parmDBFile = "";
        
        ConfigData configData = ConfigData.getDefault();
        String wepsVer = configData.getData("CD-SC-button-windgen-scienceCodeServerVer");
       
        // verify exactly syntax between configPanel and CSIP
        wepsVer = (wepsVer == null) ? "" : wepsVer.toLowerCase();
        if (wepsVer.startsWith("old")) {
            wepsVer = "windgen4.win";
        } else if  (wepsVer.startsWith("new")) {
            wepsVer = "windgen5.lin64";
        } else {
            wepsVer = "windgen5.lin64";
        }
        jsonParmsArr.put( new JSONObject().put("name","modelVersion").put("value",wepsVer) );
        
        String s = configData.getData("CD-SC-button-windgenSendDb");
        boolean sendDbFileWithRequest = (s != null) ? s.toLowerCase().startsWith("yes") : true;
        
        // MakeInterpolatedStation.java makes the cmdLine with distinct strings for the 
        //  command option specifier and its value.
        // The wep/RunProgram makes the commandline with each command option specifier and its value in the same string.
        // Go figure.
        String cmdOpt;
        for (int i=1; i<cmdLine.length; i++) {
            cmdOpt = cmdLine[i];
            String argType = cmdOpt.substring(0,2);
            System.out.println("wind argType: " + argType);
            if (argType.contains("-s")) {
                jsonParmsArr.put(new JSONObject().put("name","WBANnum").put("value", cmdOpt.substring(2).trim() ));
            } else if (argType.contains("-b")) {
                jsonParmsArr.put(new JSONObject().put("name","startyear").put("value", cmdOpt.substring(2).trim() ));
            } else if (argType.contains("-y")) {
                jsonParmsArr.put(new JSONObject().put("name","duration").put("value", cmdOpt.substring(2).trim() ));
            } else if (argType.contains("-o")) {
                jsonParmsArr.put(new JSONObject().put("name","outputFile").put("value", new File(cmdOpt.substring(2)).getName() ));
            } else if (argType.contains("-f")) {
                parmDBFile = cmdOpt.substring(2).trim();
                if (sendDbFileWithRequest || parmDBFile.toLowerCase().contains("interpolate")) {
                    jsonParmsArr.put(new JSONObject().put("name","dataFile").put("value", new File(parmDBFile).getName() ));
                    fileList.add(parmDBFile.startsWith("/") ? new File(parmDBFile) : new File(outDir + "/" + parmDBFile));
                }
            }
        }

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // Input files to the service call
        files = fileList.toArray(new File[]{});

        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsInterpolate (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
        ArrayList<File> fileList = new ArrayList<>();
        
        ConfigData configData = ConfigData.getDefault();
        String wepsVer = configData.getData("CD-SC-button-interpolate-scienceCodeServerVer");
       
        // verify exactly syntax between configPanel and CSIP
        wepsVer = (wepsVer == null) ? "" : wepsVer.toLowerCase();
        if (wepsVer.startsWith("old")) {
            wepsVer = "interpolate.win";
        } else if  (wepsVer.startsWith("new")) {
            wepsVer = "interpolate.lin64";
        } else {
            wepsVer = "interpolate.lin64";
        }
        jsonParmsArr.put( new JSONObject().put("name","modelVersion").put("value",wepsVer) );
            
        String parmIdxFile = "interpolate.idx";
        String parmPolygonFile = "interpolate.pol";
        String parmOutputFile = "weights.txt";
        
        String s = configData.getData("CD-SC-button-windgen-interpolateSendDb");
        boolean sendDbFileWithRequest = (s != null) ? s.toLowerCase().startsWith("yes") : true;
        // The call to interpolate formats the cmdLine with distinct strings for the 
        //  command option specifier and its value.
        // It also has additional quotes around the parameter values (if they are string parms)...
        String cmdOpt;
        String cmdParm = "";
        for (int i=1; i<cmdLine.length; i++) {
            cmdOpt = cmdLine[i];
            if (i+1 < cmdLine.length) {
                cmdParm = cmdLine[i+1].replace("\"", " ").trim();
            }
            
            if (cmdOpt.contains("-f")) {
                parmIdxFile = cmdParm;
                i++; //used cmdParm, bump to next cmd
                
                JSONObject idxObject = new JSONObject().put("name","idxFile").put("value", new File(parmIdxFile).getName() );
// Don't use
// Either specify a file and send it or specify nothing
//  and let the CSIP service use its internal file.
//                if (!sendDbFileWithRequest) {
//                    idxObject.put("useInternal","true");
//                }
                
                if (sendDbFileWithRequest) {
                    jsonParmsArr.put(idxObject);
                    fileList.add(parmIdxFile.startsWith("/") ? new File(parmIdxFile) : new File(outDir + "/" + parmIdxFile));
                }
            } else if (cmdOpt.contains("-p")) {
                parmPolygonFile = cmdParm;
                i++; //used cmdParm, bump to next cmd
                if (sendDbFileWithRequest) {
                    jsonParmsArr.put(new JSONObject().put("name","polygonFile").put("value", new File(parmPolygonFile).getName() ));
                    fileList.add(parmPolygonFile.startsWith("/") ? new File(parmPolygonFile) : new File(outDir + "/" + parmPolygonFile));
                }
            } else if (cmdOpt.contains("-o")) {
                parmOutputFile = cmdParm;
                i++; //used cmdParm, bump to next cmd
                jsonParmsArr.put(new JSONObject().put("name","outputFile").put("value", new File(parmOutputFile).getName() ));
            } else if (cmdOpt.contains("-lat")) {
                i++; //used cmdParm, bump to next cmd
                jsonParmsArr.put(new JSONObject().put("name","lat").put("value", cmdParm ));
            } else if (cmdOpt.contains("-lon")) {
                i++; //used cmdParm, bump to next cmd
                jsonParmsArr.put(new JSONObject().put("name","lon").put("value", cmdParm ));
            }
        }

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);
        
        // Input files to the service call
        files = fileList.toArray(new File[]{});
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsInterp_wdb (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
        String parmIn2File = null;
        String parmIn3File = null;
        //ArrayList<String> fileNames = new ArrayList<String>(8);
        //int weightValue = 1;
        
        // Are we execing on server, then which service
        boolean isNewServerVer = false;
        if (ConfigData.getDefault().getDataParsed("CD-SC-windgen-interpolate-genData-execOnServer").contentEquals("1") &&
           !ConfigData.getDefault().getDataParsed("CD-SC-windgen-interpolate-genData-endpoint").endsWith("1.0")) {
            isNewServerVer = true;
        }
            
        // The call to interp_wdb has no command option specifiers, parmaters are simply position dependent.

        String parmWdbFile = cmdLine[1];
        jsonParmsArr.put(new JSONObject().put("name","outputFile").put("value", new File(parmWdbFile).getName() ));
        
        if (!isNewServerVer) {
            String parmIn1File = cmdLine[2];
            jsonParmsArr.put(new JSONObject().put("name","inputFile1").put("value", new File(parmIn1File).getName() ));
            jsonParmsArr.put(new JSONObject().put("name","weight1").put("value", cmdLine[3] ));

            // This is a temporary fix for when there are less than three stations
            if(cmdLine.length < 6){
                parmIn2File = cmdLine[2];
                jsonParmsArr.put(new JSONObject().put("name","inputFile2").put("value", new File(parmIn2File).getName() ));
                jsonParmsArr.put(new JSONObject().put("name","weight2").put("value", "0" ));
            }
            else {
                parmIn2File = cmdLine[4];
                jsonParmsArr.put(new JSONObject().put("name","inputFile2").put("value", new File(parmIn2File).getName() ));
                jsonParmsArr.put(new JSONObject().put("name","weight2").put("value", cmdLine[5] ));
            }


            if(cmdLine.length < 8){
                parmIn3File = cmdLine[2];
                jsonParmsArr.put(new JSONObject().put("name","inputFile3").put("value", new File(parmIn3File).getName() ));
                jsonParmsArr.put(new JSONObject().put("name","weight3").put("value", "0" ));
            }
            else {
                parmIn3File = cmdLine[6];
                jsonParmsArr.put(new JSONObject().put("name","inputFile3").put("value", new File(parmIn3File).getName() ));
                jsonParmsArr.put(new JSONObject().put("name","weight3").put("value", cmdLine[7] ));
            }

            modelParms = new JSONObject ();
            modelParms.put("metainfo", new JSONObject().put("mode","async"));
            modelParms.put("parameter", jsonParmsArr);

            // Input files to the service call
            files = new File[3];
            files[0] = new File(parmIn1File);
            if (files[0].getParent() == null) {
                files[0] = new File(outDir + "/" + parmIn1File);
            }
            files[1] = new File(parmIn2File);
            if (files[1].getParent() == null) {
                files[1] = new File(outDir + "/" + parmIn2File);
            }
            files[2] = new File(parmIn3File);
            if (files[2].getParent() == null) {
                files[2] = new File(outDir + "/" + parmIn3File);
            }
        
        } else {
            // is new server version ( > 1.1 )
            // Loops over any amount of windgen stations to prepare the weights
            JSONArray stationData = new JSONArray();
            for(int i = 2; i < cmdLine.length; ++i){
                JSONArray stationDataElem = new JSONArray();
                stationDataElem.put(new JSONObject().put("name","stationId").put("value", new File(cmdLine[i]).getName() ));
                stationDataElem.put(new JSONObject().put("name","stationWeight").put("value", cmdLine[++i] ));
                stationData.put(stationDataElem);

            }
            jsonParmsArr.put(new JSONObject().put("name","stationData").put("value", stationData ));

            modelParms = new JSONObject ();
            modelParms.put("metainfo", new JSONObject().put("mode","async"));
            modelParms.put("parameter", jsonParmsArr);

            // Data files are extracted within the new service.
            files = new File[0];

            // Input files to the service call
            // Not needed for new service
            // (The new service extracts the records internally)
//            files = new File[fileNames.size()];
//            for(int i = 0; i < fileNames.size(); ++i){
//                files[i] = new File(fileNames.get(i));
//                if (files[i].getParent() == null) {
//                    files[i] = new File(outDir + "/" + fileNames.get(i));
//                }
//            }
            
        }
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsWeps (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = initParmsArrCommon();
        File[] files;
            
        String manFileName = new File(manageFname).getName();
        
        ConfigData configData = ConfigData.getDefault();
        String wepsVer = configData.getData("CD-SC-button-weps-scienceCodeServerVer");
       
        // verify exactly syntax between configPanel and CSIP
        wepsVer = wepsVer.toLowerCase();
        if (wepsVer.startsWith("windows")) {
            wepsVer = "desktop.win";
        }else if  (wepsVer.startsWith("dev")) {
            wepsVer = "dev-version.lin64";
        } else {
            wepsVer = "default.lin64";
        }
        jsonParmsArr.put( new JSONObject().put("name","modelVersion").put("value",wepsVer) );

                
        for (String cmdOpt : cmdLine) {
            String argType = cmdOpt.substring(0,2);
            if (argType.contains("-W")) {
                jsonParmsArr.put( new JSONObject().put("name","hydrologyMethod").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-u")) {
                jsonParmsArr.put( new JSONObject().put("name","resurfacing").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-I")) {
                jsonParmsArr.put( new JSONObject().put("name","initialization").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-t")) {
                jsonParmsArr.put( new JSONObject().put("name","confidenceInterval").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-T")) {
                jsonParmsArr.put( new JSONObject().put("name","furrowEffect").put("value",cmdOpt.substring(2).trim()) );
            } else if (argType.contains("-C")) {
                jsonParmsArr.put( new JSONObject().put("name","calibrationMode").put("value",cmdOpt.substring(2).trim()) );
                    // And management file name changes if this is a calibration run
                //String appendName = "";
                //if(!manFileName.contains("_calib")){
                    //appendName = "_calib";
                //}
                //manFileName = Util.incrementFileName(new TFile(manFileName), appendName, ".man").getName();

            } else if (argType.contains("-Z")) {
                jsonParmsArr.put( new JSONObject().put("name","parmCalibrationCycles").put("value",cmdOpt.substring(2).trim()) );
            }
        }

        modelParms = new JSONObject ();
//        JSONObject modelMeta = new JSONObject ();
//        modelMeta.put("mode","async");
//        if (configData.getData("CD-detailed hydro output").contentEquals("1") ||
//            configData.getData("CD-detailed soil output").contentEquals("1") ||
//            configData.getData("CD-detailed manage output").contentEquals("1") ||
//            configData.getData("CD-detailed crop output").contentEquals("1") ||
//            configData.getData("CD-detailed decomp output").contentEquals("1") ||
//            configData.getData("CD-detailed erosion output").contentEquals("1")                
//                ) {
//            String nowDate = Dates.nowISO();
//            modelMeta.put(KEY_TSTAMP, Dates.nowISO());
//            String expDate = Dates.newISOFormat().format(Dates.futureDate(600));
//            modelMeta.put(KEY_EXPIRATION_DATE,expDate);
//
//            modelMeta.put(CSIP_SESSION_TTL, "PT300S");
//            //modelMeta.put("keep_results","PT300S");
//        }
//        modelParms.put("metainfo", modelMeta);
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // modelVersion is being moved from parms to metainfo.
        // for now, put it in both places.
        modelParms.getJSONObject("metainfo").put("modelVersion",wepsVer);


        // Input files to the service call
        // For the weps call, the input filenames are specified in the weps.run input file, which is always required
        files = new File[5];
        files[0] = new File(outDir + "/" + "weps.run");
        files[1] = new File(outDir + "/" + windInputFileName);
        files[2] = new File(outDir + "/" + "cli_gen.cli");
        files[3] = new File(outDir + "/" + manFileName); //new File(manageFname).getName());
        files[4] = new File(outDir + "/" + new File(soilFname).getName());
        
        return (new parmRet (modelParms,files));
    }
     
    private parmRet modParmsWepsForWepsIntegrated (parmRet currentParms) throws JSONException {
        if (integratedParms == null) {
            throw new JSONException("modParmsWepsForWepsIntegrated: integratedParms NOT initialized");
        }
        
        File[] files = new File[3];
        files[0] = currentParms.files[0];
        files[1] = currentParms.files[3];
        files[2] = currentParms.files[4];
        
        JSONArray parmArr = currentParms.modelParms.getJSONArray("parameter");
        LatLong ll = integratedParms.wepsLatLng;
        parmArr.put( new JSONObject().put("name","latitude").put("value",ll.latitudeValue(USCustomary.DEGREE_ANGLE)) );
        parmArr.put( new JSONObject().put("name","longitude").put("value",ll.longitudeValue(USCustomary.DEGREE_ANGLE)) );
        
        int simYears = integratedParms.simYears;
        parmArr.put( new JSONObject().put("name","simulationYears").put("value",simYears));
        
        parmArr.put( new JSONObject().put("name","stateID").put("value",integratedParms.cligenStateId) );
        parmArr.put( new JSONObject().put("name","stationID").put("value",integratedParms.cligenStationId) );
        
        if (integratedParms.isWindgenInterpolated) {
            ll = integratedParms.windLatLng;
            parmArr.put( new JSONObject().put("name","windlatitude").put("value",ll.latitudeValue(USCustomary.DEGREE_ANGLE)) );
            parmArr.put( new JSONObject().put("name","windlongitude").put("value",ll.longitudeValue(USCustomary.DEGREE_ANGLE)) );
        } else {
            parmArr.put( new JSONObject().put("name","WBANnum").put("value",integratedParms.windWBANnum) );
        }
        
        
        // need to tell science the name since it is already in the runfile
        parmArr.put( new JSONObject().put("name","windGenFileName").put("value",integratedParms.windGenFileName));
        
        JSONObject metainfo = currentParms.modelParms.getJSONObject("metainfo");
        metainfo.put( "zipResults", true);
        metainfo.put( "wepsWindCliParallel", true);

        return (new parmRet (currentParms.modelParms,files));
    }

    
    //
    // Copied from csip-core package
    //        from file ServiceTest.java ( Method download() )
    //
    // MEH: added return of stdout & stderr names
    //
    private String[] downloadCsipResults (JSONObject resp, File resultFolder, Client client, String urlProtocol) throws Exception {
        String res[] = new String[2];
        JSONArray arr = resp.getJSONArray("result");
        // download files using the 'q' service.
        Map<String, JSONObject> rmap = JSONUtils.preprocess(arr);
        for (String key : rmap.keySet()) {
            JSONObject val = rmap.get(key);
            if (!val.has("value")) {
                continue;
            }
            String url = val.getString("value");
//            if (url == null) {
//                throw new Exception("Expecting file in result: " + key);
//            }
            if (url != null && url.startsWith("http")) {
                // if the value is a URL, then download that file
                // (Not all return values will be files)
                
                // substitute the protocol from the initial call onto the result file urls
                // (happens when the ARS server is proxied behind a https address)
                // (the https is stripped in the proxy process, so does not end up
                //  in the result file specifier).
                if (urlProtocol != null) {
                    url = urlProtocol + WepsCsipUtil.stripUrlProtocol(url);            }
                if (url.startsWith("http") && url.contains("/q/") && url.endsWith(key)) {
                    if (!resultFolder.exists()) {
                        resultFolder.mkdirs();
                    }
                    File outFile = new File(resultFolder, key);
                    client.doGET(url, outFile);
                    if (!outFile.exists()) {
                        throw new Exception("Missing output file: " + key);
                    }
                }
            }
            if (key.contains("stdout")) {
                res[0] = key;
            } else if (key.contains("stderr")) {
                res[1] = key;
            }
        }
        return (res);
    }
    
    private JSONObject doCsipAsync (Client client, String modelUrl, JSONObject modelParms, File[] files) throws ServerControlException {
        JSONObject modelResult = null;
        JSONObject modelMeta;
        String modelSuid;
        String getUrl;
        String resultStr = "";
        int pollFirst;
        int pollInterval;
        int poll;
        final int sleepInterval = 100;
        
        // set progress to none
        bgIf.bkgndDlgSetPbarCurrent(0);
        bgIf.bkgndDlgSetPbarTotal(1);
        
        try {
            modelResult = client.doPOST(modelUrl, modelParms, files);
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "client.doPOST failed in ServerControlExec.doCsipAsync()");
            throw new ServerControlException("client.doPOST failed in ServerControlExec.doCsipAsync()\n"
                                             +"url:" + modelUrl + "\n"
                                             +ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        }

        try {
            modelMeta = modelResult.getJSONObject("metainfo");
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "JSON parse error getting metainfo in ServerControlExec.doCsipAsync()");
            throw new ServerControlException("JSON parse error getting metainfo in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        }
        
        try {
            modelSuid = modelMeta.getString("suid");
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "JSON parse error getting suid in ServerControlExec.doCsipAsync()");
            throw new ServerControlException("JSON parse error getting suid in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        }
        // occasionally, the modelResult contains "status running" immediately instead of "status submitted"
        // if it is "status running" immediately, there are no "first_poll" nor "next_poll" parameters
        // so set a default value for this case
        pollFirst = modelMeta.optInt("first_poll", 200);
        if (pollFirst == 200) {
            //System.err.println(" +++++ Used default pollFirst value +++ , result status="+modelMeta.optString("status", ""));
            log.logMessage(WepsMessage.MessageSeverity.INFORMATION, " +++++ Used default pollFirst value +++ , result status="+modelMeta.optString("status", ""));
        }
        pollInterval = modelMeta.optInt("next_poll", 200);
        if (pollFirst == 200 && pollInterval == 200 && (modelUrl.contains("csip-weps") || modelUrl.contains("ars-weps"))) {
            pollFirst = 1000;
            pollInterval = 750;
        }
        //progressMaxSave = rp.runDialog.JPB_weps.getMaximum();
        //rp.runDialog.JPB_weps.setValue(0);
        bgIf.bkgndDlgSetPbarCurrent(0);
        
        getUrl = modelUrl.substring(0, modelUrl.indexOf("/m/"))+ "/q/" + modelSuid;
        
        for (poll = 0;  poll < pollFirst; poll += sleepInterval) {
            try {
                Thread.sleep(sleepInterval);
            } catch (InterruptedException ex) {
            }
        }
        
        try {
            //modelResult = new JSONObject(client.doGET(getUrl));
            resultStr = client.doGET(getUrl);
            modelResult = new JSONObject(resultStr);
        } catch (JSONException ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "JSON parse error after goGET in ServerControlExec.doCsipAsync(): " + resultStr);
            throw new ServerControlException("JSON parse error after goGET in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "client.doGET failed in ServerControlExec.doCsipAsync()");
            throw new ServerControlException("client.doGET failed in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        }
        
        try {
            String statusStr = modelResult.getJSONObject("metainfo").getString("status").toLowerCase();
            while( (!statusStr.contains("finished")) && !statusStr.contains("failed")) {
                String progressStr = extractProgress (modelResult);
                if (progressStr.length() > 0) {
                    parseProgressResult resProgress = parseProgress (progressStr);
                    bgIf.bkgndDlgSetPbarCurrent(resProgress.year);
                    bgIf.bkgndDlgSetPbarTotal(resProgress.total);
//                    bgIf.bkgndDlgWriteStatus(progressStr + " (on the cloud)");
                    bgIf.bkgndDlgWriteStatus(progressStr);
                    if (isWepsIntegrated) {
                        if (progressStr.contains("Cligen")) {
                            bgIf.bkgndDlgWriteCliMsg("running");
                        } else if (progressStr.contains("Windgen")) {
                            bgIf.bkgndDlgWriteCliMsg("finished");
                            bgIf.bkgndDlgWriteWindMsg("running");
                        } else if (progressStr.contains("Subregion") || progressStr.contains("Erosion")) {
                            bgIf.bkgndDlgWriteCliMsg("finished");
                            bgIf.bkgndDlgWriteWindMsg("finished");
                        } else if (progressStr.contains("Weps Complete") || progressStr.contains("Building result data")) {
                            bgIf.bkgndDlgSetPbarTotal(100);
                            bgIf.bkgndDlgSetPbarCurrent(100);
                        }
                    }
                }
                try {
                    Thread.sleep(pollInterval);
                } catch (InterruptedException ex) {
                }

                resultStr = client.doGET(getUrl);
                modelResult = new JSONObject(resultStr);
                statusStr = modelResult.getJSONObject("metainfo").getString("status").toLowerCase();
            }
        } catch (JSONException ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "JSON parse error after goGET 2 in ServerControlExec.doCsipAsync(): " + resultStr);
            throw new ServerControlException("JSON parse error after goGET 2 in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        } catch (Exception ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "client.doGET 2 failed in ServerControlExec.doCsipAsync()");
            throw new ServerControlException("client.doGET 2 failed in ServerControlExec.doCsipAsync()\n"+ex.getMessage(),
                    ex.getCause(),"doCsipAsync");
        }
        
        try {
            if (!modelResult.getJSONObject("metainfo").getString("status").toLowerCase().contains("finished")) {
                //throw new Exception("CSIP service timeout, no finished in status:\n" + modelResult.toString(4));
                log.logMessage(WepsMessage.MessageSeverity.ERROR, "CSIP service timeout, no finished in status:" + modelResult.toString(4));
                throw new ServerControlException("CSIP service timeout, no finished in status:\n" + modelResult.toString(4),null,"doCsipAsync");
            }
        } catch (JSONException ex) {
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "JSON parse error getting metainfo / finished in ServerControlExec.doCsipAsync()");
            throw new ServerControlException(ex.getMessage(),ex.getCause(),"doCsipAsync");
        }
        
        try {
            log.logMessage(WepsMessage.MessageSeverity.INFORMATION, "model result for:"+modelUrl);
            log.logMessage(WepsMessage.MessageSeverity.INFORMATION, modelResult.toString(2));
        } catch (JSONException ex) {
        }
        // Arbitrary value, just make the match so par is 'complete'
        bgIf.bkgndDlgSetPbarTotal(100);
        bgIf.bkgndDlgSetPbarCurrent(100);
        
        if (isWepsIntegrated) {
            bgIf.bkgndDlgWriteWepsMsg("finished");
        }
        bgIf.bkgndDlgWriteStatus("downloading");

        return (modelResult);
    }
    
    private String extractProgress (JSONObject modelResult) {
        String res = "";
        try {
            JSONObject meta;
            meta = modelResult.getJSONObject("metainfo");
            res = meta.getString("progress");
        } catch (JSONException ex) {
            //System.err.println("ServerControlExec, failed in parsing progress result");
        }
        return res;
    }
    
    private class parseProgressResult {
        protected final int year;
        protected final int total;
        parseProgressResult (int year, int total) {
            this.year = year;
            this.total = total;
        }
    }
    
    
    private parseProgressResult parseProgress (String progress) {
        int year = 0;
        int total = 0;
        String elems [] = progress.split(" +");
        if (elems[1].startsWith("Year")) {
            year = Integer.valueOf(elems[2]);
            total = Integer.valueOf(elems[4]);
        } else if (elems[1].startsWith("Subregion")) {
            year = Integer.valueOf(elems[4]);
            total = Integer.valueOf(elems[6]);
        } else if (elems[1].startsWith("Erosion")) {
            year = Integer.valueOf(elems[3]);
            total = Integer.valueOf(elems[5]);
        }
        return new parseProgressResult (year, total);
    }
    
    void appendFile (File fileIn, PrintWriter out) {
        BufferedReader copyReader;
        String rdLine;
        
        try {
            copyReader = new BufferedReader(new TFileReader(fileIn));
        } catch (FileNotFoundException ex) {
            LOGGER.log(Level.ERROR, ex);
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "Could not open append file \" + fileIn.getAbsolutePath() + \" in ServerControlExec");
            return;
        }
        
        try {
            while ((rdLine=copyReader.readLine()) != null) {
                out.println(rdLine);
            }
             out.flush();
        } catch (IOException ex) {
            LOGGER.log(Level.ERROR, ex);
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "Error copying append file \" + fileIn.getAbsolutePath() + \" in ServerControlExec");
        }
        finally {
            try {
                copyReader.close();
            } catch (IOException ex) {
                LOGGER.log(Level.ERROR, ex);
                log.logMessage(WepsMessage.MessageSeverity.ERROR, "Error closing append file \" + fileIn.getAbsolutePath() + \" in ServerControlExec");
            }
            
        }
    }

    protected void writeJsonToFile (JSONObject jsonObj, File outFile, String info) {
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(outFile));
            writer.write(info+"\n");
            writer.write(jsonObj.toString(4));
            writer.close();
        } catch (IOException ex) {
        } catch (JSONException ex) {
        }
    }
    
    
    
    //
    // MEH
    // This is copied from the PipeIn class (WepsRunControlPipeIn), from its run method.
    // The PipeIn class creates the stdout and stderr files when the external science models are executed locally.
    // It filters the output and simultaneously copies Error and Warning messages to the log.
    // These messages are filtered from the log file after execution and written to the warnings.txt file.
    //
    // When the external modules are executed remotely on a server, this filtering does not take place, we only
    // get returned the entire stdout and stderr files.  This copied method is then used to extract these messages
    // from the files after the run so that the rest of the program execution can continue seamlessly
    // and the remote execution will remain transparent to the rest of the Weps GUI code.
    //
    
    public WepsMessageLog scanExecOutFiles (InputStream from) {
        String inpstr;
        String trimstr;
        String message;
        BufferedReader bid = new BufferedReader(new InputStreamReader(from), 10);
        WepsMessageLog c_log;
        c_log = new WepsMessageLog();

        try {
            while ((inpstr = bid.readLine()) != null) {

                trimstr = inpstr.trim();
                //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()));
                }

            }
            bid.close();
        } catch (IOException e) {
            //System.err.println("PipeIn: " + e);
        }
        return (c_log);
    }
    
    public void setIsWepsIntegrated() {
        isWepsIntegrated = true;
    }
    
    public void setWepsIntegratedData(WepsIntegratedParms integratedParms) {
        this.integratedParms = integratedParms;
    }
}

