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 java.util.logging.Level;
import java.util.logging.Logger;
import usda.weru.util.Mantis;
import usda.weru.util.WepsMessage;
import usda.weru.util.WepsMessageLog;
import csip.Client;
import csip.utils.JSONUtils;
import de.schlichtherle.truezip.file.TFile;
import java.io.FileInputStream;
import java.util.Map;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import weps.ws.Csip.utils.WepsCsipUtil;

/**
 *
 * @author mhaas
 */
public class ServerControlExec {
    
    public static enum interpolateState {
        none,
        wepsWithMIS,
        inMIS;
    }
 
    private final WepsMessageLog log;
    private final PrintWriter outout;
    private final PrintWriter outerr;
    private final RunProgramBase rp;
    private final String outDir;
    private final ServerControlData scData;
    private final String manageFname;
    private final String soilFname;
    private final interpolateState interpState;
    
    private Process execExeProcess;

    //
    // 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 outout, PrintWriter outerr, 
                              RunProgramBase rp, 
                              String outDir,
                              String manageFname, String soilFname, 
                              interpolateState interpState
                              ) {
        this.log = log;
        this.outout = outout;
        this.outerr = outerr;
        this.rp = rp;
        this.outDir = outDir;
        this.manageFname = manageFname;
        this.soilFname = soilFname;
        this.interpState = interpState;
        
        scData = new ServerControlData ();
        
        if (interpState != interpolateState.none) {
            scData.setInterpolateMode();
        }
    }

    /*
    ** Only for using the file functions, NOT for executing.  Use above.
    */
    public ServerControlExec (String outDir) {
        this (null,null,null,null,outDir,null,null,interpolateState.none);
    }
    

    //
    // 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) {
        return (scData.chkExecOnServerSet(cmdLine[0]) ? doExecCsip (cmdLine) : doExecLocal (cmdLine));
    }
            
    // Execute the model locally
    // This code is taken/copied from the RunProgram.java 
    //  (both: the one in the weps dir and the one in MakeInterpolatedStatin)
    private int doExecLocal (String[] cmdLine) {
        int rtnVal = -1;
        RunProgramBase.PipeIn pipout = null;
        RunProgramBase.PipeIn piperr = 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 (interpState==interpolateState.inMIS) {
                execExeProcess = Runtime.getRuntime().exec(cmdLine, null, new File(outDir) );
            } else {
                execExeProcess = Runtime.getRuntime().exec(cmdLine);
            }
            pipout = new RunProgramBase.PipeIn(execExeProcess.getInputStream(), outout, rp.runDialog, 1);
            pipout.start();
            piperr = new RunProgramBase.PipeIn(execExeProcess.getErrorStream(), outerr, null, 1);
            piperr.start();
            rtnVal = execExeProcess.waitFor(); //
            
            while (execExeProcess.isAlive()) {
                if (rp.getStopFlag()) {
                    execExeProcess.destroy();
                    break;
                }
                Thread.sleep(100);
            }
            if (rp.getStopFlag()) {
                rtnVal = -1;
            } else {
                rtnVal = execExeProcess.exitValue();
            }
            
            //Wait for the pipes to finish writing their logs.
            while (piperr.isAlive() || pipout.isAlive()) {
                Thread.sleep(100);
            }
            rtnVal = execExeProcess.exitValue();

        } 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 (pipout != null) {
            log.mergeLog(pipout.getLog());
        }
        if (piperr != null) {
            log.mergeLog(piperr.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) {
        int rtnVal = -1;
        Client client;
        JSONObject modelParms;
        JSONObject modelResult;
        String modelStatusStr;
        String modelUrl;
        File[] files;
        String downloadRes[];
        parmRet loadParms = new parmRet();
        

        Logger clientLog = Logger.getLogger(ServerControlExec.class.getName());
        client = new Client(clientLog);

        try {
            rp.runDialog.JTF_status.setText("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);
                    break;
                case (ServerControlData.cdKeyInterpGendataBase):
                    loadParms = loadParmsInterp_wdb(cmdLine);
                    break;
                case (ServerControlData.cdKeyWindgenBase):
                    loadParms = loadParmsWind(cmdLine);
                    break;
                case (ServerControlData.cdKeyCligenBase):
                    loadParms = loadParmsClimate(cmdLine);
                    break;
                case (ServerControlData.cdKeyWepsBase):
                    loadParms = loadParmsWeps(cmdLine);
                    break;
            }

            modelParms = loadParms.modelParms;
            files = loadParms.files;
            
            modelResult = doCsipAsync(client, modelUrl, modelParms, files);

            // 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);
            
            // Append the downloaded stdout & stderr to the existing stdout & stderr
            File serverOutFile = new File (outDir + "/" + downloadRes[0]);
            File serverErrFile = new File (outDir + "/" + downloadRes[1]);
            appendFile (serverOutFile, outout);
            appendFile (serverErrFile, outerr);
            
            // Need to extract log info from stdout & stderr (see comment @ scanExecOutFiles)
            log.mergeLog(rp.scanExecOutFiles (new FileInputStream(serverOutFile)));
            log.mergeLog(rp.scanExecOutFiles (new FileInputStream(serverErrFile)));
            
            // If windgen interrpolate mode, rundir is a temp dir and we need to move the result file
            // to the system working dir.  The system path is specified in the command output paramter
            if (this.interpState == interpolateState.inMIS && cmdLine[0].contains("wind_gen")) {
                if (cmdLine[3] != null && cmdLine[3].contains("-o") && cmdLine[4] != null) {
                    File destFile = new File (cmdLine[4]);
                    File runDirFile = new File(outDir + "/" + destFile.getName());
                    boolean test = runDirFile.renameTo(destFile);
                    if (!test) {
                        log.logMessage(WepsMessage.MessageSeverity.ERROR, "Windgen interpolate move file failed");
                    }
                }
            }

            modelStatusStr = ((JSONObject)(modelResult.get("metainfo"))).getString("status");
            rtnVal = modelStatusStr.toLowerCase().contains("finished") ? 0 : -1 ;
            
        } catch (Exception ex) {
            clientLog.log(Level.SEVERE, null, ex);
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "Server execution failed");
        }
        
        rp.runDialog.JTF_status.setText("");
        
        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 parmRet loadParmsClimate (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = new JSONArray();
        File[] files;
            
        String parmInputFile = "t.par";
        boolean useServiceDbFile = true; // MEH This is debug only. Need to add a real configData option for this

        for (String cmdOpt : cmdLine) {
            if (cmdOpt.contains("-S")) {
                jsonParmsArr.put(new JSONObject().put("name","stateID").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-s")) {
                jsonParmsArr.put(new JSONObject().put("name","stationID").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-b")) {
                jsonParmsArr.put(new JSONObject().put("name","startyear").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-y")) {
                jsonParmsArr.put(new JSONObject().put("name","duration").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-i")) {
                if (!useServiceDbFile) {
                    parmInputFile = cmdOpt.substring(2);
                    jsonParmsArr.put(new JSONObject().put("name","dataFile").put("value", new File(parmInputFile).getName() ));
                }
            }
        }
        
        // 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
        if (useServiceDbFile) {
            files = new File[]{};
        } else {
            files = new File[1];
            files[0] = new File(parmInputFile);
            if (files[0].getParent() == null) {
                files[0] = new File(outDir + "/" + parmInputFile);
            }
        }
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsWind (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = new JSONArray();
        File[] files;
            
        String parmDBFile = "win_gen.wdb";
        
        // 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.
        boolean isInterp = (this.interpState == interpolateState.inMIS);

        String cmdOpt;
        for (int i=1; i<cmdLine.length; i++) {
            cmdOpt = cmdLine[i];
            if (cmdOpt.contains("-s")) {
                jsonParmsArr.put(new JSONObject().put("name","WBANnum").put("value", isInterp ? cmdLine[++i] : cmdOpt.substring(2).trim() ));
            } else if (cmdOpt.contains("-b")) {
                jsonParmsArr.put(new JSONObject().put("name","startyear").put("value", isInterp ? cmdLine[++i] : cmdOpt.substring(2).trim() ));
            } else if (cmdOpt.contains("-y")) {
                jsonParmsArr.put(new JSONObject().put("name","duration").put("value", isInterp ? cmdLine[++i] : cmdOpt.substring(2).trim() ));
            } else if (cmdOpt.contains("-o")) {
                jsonParmsArr.put(new JSONObject().put("name","outputFile").put("value", new File(isInterp ? cmdLine[++i] : cmdOpt.substring(2)).getName() ));
            } else if (cmdOpt.contains("-f")) {
                parmDBFile = isInterp ? cmdLine[++i] : cmdOpt.substring(2);
                jsonParmsArr.put(new JSONObject().put("name","dataFile").put("value", new File(parmDBFile).getName() ));
            }
        }

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // Input files to the service call
        files = new File[1];
        files[0] = new File(parmDBFile);
        if (files[0].getParent() == null) {
            files[0] = new File(outDir + "/" + parmDBFile);
        }
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsInterpolate (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = new JSONArray();
        File[] files;
            
        String parmIdxFile = "interpolate.idx";
        String parmPolygonFile = "interpolate.pol";
        String parmOutputFile = "weights.txt";
        
        // 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
                jsonParmsArr.put(new JSONObject().put("name","idxFile").put("value", new File(parmIdxFile).getName() ));
            } else if (cmdOpt.contains("-p")) {
                parmPolygonFile = cmdParm;
                i++; //used cmdParm, bump to next cmd
                jsonParmsArr.put(new JSONObject().put("name","polygonFile").put("value", new File(parmPolygonFile).getName() ));
            } 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 = new File[2];
        files[0] = new File(parmIdxFile);
        if (files[0].getParent() == null) {
            files[0] = new File(outDir + "/" + parmIdxFile);
        }
        files[1] = new File(parmPolygonFile);
        if (files[1].getParent() == null) {
            files[1] = new File(outDir + "/" + parmPolygonFile);
        }
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsInterp_wdb (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = new JSONArray();
        File[] files;
            
        // The call to interp_wdb has no command option specifiers, parmaters are simply poosition dependent.

        String parmWdbFile = cmdLine[1];
        jsonParmsArr.put(new JSONObject().put("name","outputFile").put("value", new File(parmWdbFile).getName() ));
        
        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] ));
        
        String 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] ));

        String 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);
        }
        
        return (new parmRet (modelParms,files));
    }
    
    private parmRet loadParmsWeps (String[] cmdLine) throws JSONException {
        JSONObject modelParms;
        JSONArray jsonParmsArr = new JSONArray();
        File[] files;
            
        String manFileName = new File(manageFname).getName();
        
        for (String cmdOpt : cmdLine) {
            if (cmdOpt.contains("-W")) {
                jsonParmsArr.put( new JSONObject().put("name","hydrologyMethod").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-u")) {
                jsonParmsArr.put( new JSONObject().put("name","resurfacing").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-I")) {
                jsonParmsArr.put( new JSONObject().put("name","initialization").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-t")) {
                jsonParmsArr.put( new JSONObject().put("name","confidenceInterval").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.contains("-T")) {
                jsonParmsArr.put( new JSONObject().put("name","furrowEffect").put("value",cmdOpt.substring(2).trim()) );
            } else if (cmdOpt.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
                int i = manFileName.indexOf(".man");
                String a = manFileName.substring(0, i);
                String b = a + "_calib" + ".man";
                manFileName = b;
            } else if (cmdOpt.contains("-Z")) {
                jsonParmsArr.put( new JSONObject().put("name","parmCalibrationCycles").put("value",cmdOpt.substring(2).trim()) );
            }
        }

        modelParms = new JSONObject ();
        modelParms.put("metainfo", new JSONObject().put("mode","async"));
        modelParms.put("parameter", jsonParmsArr);

        // 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");
        if (interpState==interpolateState.wepsWithMIS) {
           files[1] = new File(outDir + "/" + "interpolated.win");
        } else {
           files[1] = new File(outDir + "/" + "win_gen.win");
        }
        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));
    }

    
    //
    // 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);
            String url = val.getString("value");
            if (url == null) {
                throw new Exception("Expecting file in result: " + key);
            }
            // 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 Exception {
        JSONObject modelResult;
        JSONObject modelMeta;
        String modelSuid;
        String getUrl;
        String resultStr = "";
        int progress = 0;
        final int progressLimit = scData.getProgressLen();
        int pollFirst;
        int pollInterval;
        int poll;
        final int sleepInterval = 100;
        int progressMaxSave;
        
        modelResult = client.doPOST(modelUrl, modelParms, files);

        modelMeta = modelResult.getJSONObject("metainfo");
        modelSuid = modelMeta.getString("suid");
        // 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.getString("status"));
        }
        pollInterval = modelMeta.optInt("next_poll", 200);
        if (pollFirst == 200 && pollInterval == 200 && modelUrl.contains("csip-weps")) {
            pollFirst = 1000;
            pollInterval = 750;
        }
        progressMaxSave = rp.runDialog.JPB_weps.getMaximum();
        rp.runDialog.JPB_weps.setValue(0);
        
        getUrl = modelUrl.substring(0, modelUrl.indexOf("/m/"))+ "/q/" + modelSuid;
        
        for (poll = 0;  poll < pollFirst; poll += sleepInterval) {
            Thread.sleep(sleepInterval);
        }
        
        modelResult = new JSONObject(client.doGET(getUrl));
        while(modelResult.optJSONObject("metainfo") != null &&
              !modelResult.getJSONObject("metainfo").getString("status").toLowerCase().contains("finished")) {
            for (poll = 0;  poll < pollInterval; poll += sleepInterval) {
                String progressStr = extractProgress (modelResult);
                if (progressStr.length() > 0) {
                    parseProgressResult resProgress = parseProgress (progressStr);
                    rp.runDialog.JPB_weps.setValue(resProgress.year);
                    rp.runDialog.JPB_weps.setMaximum(resProgress.total);
                    rp.runDialog.JTF_status.setText(progressStr + " (on the cloud)");
                }
                Thread.sleep(sleepInterval);
            }
            
            try {
                resultStr = client.doGET(getUrl);
                modelResult = new JSONObject(resultStr);
            } catch (JSONException ex) {
                log.logMessage(WepsMessage.MessageSeverity.WARNING, "Non JSON return from client.doGET in ServerControlExec.doCsipAsync(): " + resultStr);
                System.err.println("Non JSON return from client.doGET in ServerControlExec.doCsipAsync(): " + resultStr);
            } catch (Exception ex) {
                log.logMessage(WepsMessage.MessageSeverity.WARNING, "client.doGET failed in ServerControlExec.doCsipAsync()");
                System.err.println("client.doGET failed in ServerControlExec.doCsipAsync()");
            }
            
        }
        
        if (modelResult.optJSONObject("metainfo") == null) {
            throw new Exception("No metainfo in CSIP result:\n" + modelResult.toString(4));
        }
        if (!modelResult.getJSONObject("metainfo").getString("status").toLowerCase().contains("finished")) {
            throw new Exception("CSIP service timeout, no finished in status:\n" + modelResult.toString(4));
        }
        
        rp.runDialog.JPB_weps.setMaximum(progressMaxSave);
        rp.runDialog.JPB_weps.setValue(progressMaxSave);

        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) {
        String elems [] = progress.split(" +");
        int year = Integer.valueOf(elems[1]);
        int total = Integer.valueOf(elems[3]);
        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.getLogger(ServerControlExec.class.getName()).log(Level.SEVERE, null, 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.getLogger(ServerControlExec.class.getName()).log(Level.SEVERE, null, ex);
            log.logMessage(WepsMessage.MessageSeverity.ERROR, "Error copying append file \" + fileIn.getAbsolutePath() + \" in ServerControlExec");
        }
        finally {
            try {
                copyReader.close();
            } catch (IOException ex) {
                Logger.getLogger(ServerControlExec.class.getName()).log(Level.SEVERE, null, ex);
                log.logMessage(WepsMessage.MessageSeverity.ERROR, "Error closing append file \" + fileIn.getAbsolutePath() + \" in ServerControlExec");
            }
            
        }
    }

}

