public class Grass {

    /**
     * Operating system identifier.
     */
    private String os;

    /**
     * Operating system env.
     */
    private String osenv;

    /**
     * Command to execute.
     */
    private String cmd;

    /**
     * Talend SDI component identifier.
     */
    private String cid;
    
    /**
     * Type of command could be:
     * <ul>
     * <li>0 = Return grass output as is.</li>
     * <li>1 = Extract last element value from Grass output. In that case
     *  Grass output MUST be for a command returning an output with a pattern
     *  like A|...|X. The response returned by the Grass will be X. 
     *  That type is used for command like r.what which take as input an X and 
     *  Y parameter returning a value for that position.</li>
     * </ul>
     * 
     */
    private int type;

    /**
     * Response from Grass.
     */
    private String response;

    /**
     * OS Environment variables.
     */
    private String[] env;

    /**
     * Class constructor
     * 
     * @param cid       Talend SDI component id.
     * @param type      Type of command to execute.
     * <ul>
     * <li>0 = Return grass output as is.</li>
     * <li>1 = Extract last element value from Grass output. In that case
     *  Grass output MUST be for a command returning an output with a pattern
     *  like A|...|X. The response returned by the Grass will be X. 
     *  That type is used for command like r.what which take as input an X and 
     *  Y parameter returning a value for that position.</li>
     * </ul>
     * 
     */
    public Grass(String cid, int type) {
        this.cid = cid;
        this.type = type;
    }

    /**
     * Get reponse generated by Grass
     * 
     * @return The Grass output.
     */
    public String getResponse () {
        return this.response;
    }
    
    /**
     * Run a command.
     */
    public void run(String cmd) {
        try {
        	String exe = "";
        	//String[] exe = {};
            		
        	/* Check cmd is a Grass one starting with g., etc. 
        	 * [ d.* | db.* | g.* | i.* | m.* | photo.* | ps.* | r.* | r3.* | v.* ] 
        	 * */
        	if (cmd.startsWith("g.") || 
        			cmd.startsWith("db.") || 
        			cmd.startsWith("d.") || 
        			cmd.startsWith("g.") || 
        			cmd.startsWith("i.") || 
        			cmd.startsWith("m.") || 
        			cmd.startsWith("photo.") || 
        			cmd.startsWith("ps.") || 
        			cmd.startsWith("r.") || 
        			cmd.startsWith("r3.") || 
        			cmd.startsWith("v.")) {
        		
	        	/* System info */
	            Runtime runtime = Runtime.getRuntime();
	            String os = System.getProperty("os.name");
	            if (os.startsWith("Windows")) {
	            	exe = "cmd.exe /c " + cmd;
	                // FIXME : I guess WIN2000 & XP don't work the same way ... 
	            } else {
	            	exe = "/bin/sh -c \"\"" + cmd + "\"\"";
	            }
	            /* Exec & Error thread creation */
	            
	            String[] e = {"/bin/sh", "-c", "g.list type=rast"};
	            final Process psa = runtime.exec(e, this.env);
	            GrassThread tRuna = new GrassThread(psa, this.cid, this.type);
	            tRuna.start();
	            tRuna.interrupt();
	               
	            final Process ps = runtime.exec(exe, this.env);
	            GrassThread tRun = new GrassThread(ps, this.cid, this.type);
	            tRun.start();
	
	            GrassThreadError tError = new GrassThreadError(ps, this.cid);
	            tError.start();
	
	            ps.waitFor();
	            
	            this.response = tRun.getResponse();
	            //System.out.println (this.response);
	            
	            tRun.interrupt();
	            tError.interrupt();
	            if (ps.exitValue() != 0) {
	                System.exit(ps.exitValue());
	            }
        	} else {
        		System.err.println (this.cid + "|ERROR|The command seems to not to be one of the Grass command.");
        	}
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }
    
    
    /**
     * Define all environment variables needed by Grass
     * 
     *  @param gisBase        Directory where GRASS lives. 
     *  @param gisRcreate     Define if an existing GISRC file as to be used.
     *  @param gisRc          Name of .grassrc6 file. GISRCRC defines the system wide 
     *      value while in a GRASS session.
     *  @param mapset         Initial mapset directory which is a subdirectory of LOCATION_NAME
     *  @param location       Initial location directory which is a 
     *      subdirectory of GISDBASE
     *  @param gisDBase       Initial database directory which should 
     *      be a fully qualified path (eg /usr/local/share/grassdata).
     *  @param proj           Projection code.
     */
    public void setEnv(String gisBase, 
                        String gisRcreate, 
                        String gisRc, 
                        String mapset, 
                        String location,
                        String gisDBase, 
                        String proj) {
    	String os = System.getProperty("os.name");
        if (os.startsWith("Windows")) {
            this.osenv = ";";
        } else  {
        	this.osenv = ":";
        }

    	
        if (gisRcreate == null)
            gisRcreate = "GISRC";
        
        if (gisRcreate.equals("GISRC")) {
            String[] env = { 
            		"GISBASE=" + gisBase, 
            		"GIS_LOCK=" + this.cid,
                    "PATH=" + (os.startsWith("Windows")?"%PATH%":"$PATH:/usr/bin:/bin") + this.osenv + gisBase + "/bin" + 
                    this.osenv + gisBase + "/lib" + 
                    this.osenv + gisBase + "/scripts" + 
                    this.osenv + gisBase + "/etc",
                    "GISRC=" + gisRc, 
                    "SHELL=/bin/bash"};                
            this.env = env;
        } else {
            
            // Create the mapset
            /*
             * / create gisdbase and set env / http://www.grassbook.org/scripts/create_location.sh.txt /
             */
            // Creating location and mapset
            try {
                boolean bLoc = (new java.io.File(gisDBase + "/" + location)).mkdir();
                boolean bMap = (new java.io.File(gisDBase + "/" + location + "/" + mapset)).mkdir();
                if (bMap && bLoc) {
                    System.out.println(this.cid + "|INFO|Directory: " + gisDBase + " LOCATION and MAPSET created.");
                } else {
                    System.out.println(this.cid + "|INFO|Directory LOCATION and MAPSET existing.");
                }

            } catch (Exception e) { // Catch exception if any
                System.err.println(this.cid + "|ERROR|" + e.getMessage());
                // throw new Exception(e);
            }

            // Creating Grass env gisrc file
            try {
                java.io.FileWriter fstream = new java.io.FileWriter(gisRc);
                java.io.BufferedWriter out = new java.io.BufferedWriter(fstream);
                out.write("LOCATION_NAME: " + location + " \n");
                out.write("GISDBASE: " + gisDBase + "\n");
                out.write("MAPSET: " + mapset + " \n");
                out.write("DIGITIZER: none\n");
                out.write("OVERWRITE: 1\n");
                out.write("GRASS_GUI: text\n");

                out.close();
            } catch (Exception e) { // Catch exception if any
                System.err.println(this.cid + "|ERROR|" + e.getMessage());
                // throw new Exception(e);
            }

            // TODO : create proj
            String[] env = { 
            		"GISBASE=" + gisBase, 
            		"GIS_LOCK=cid",
                    "PATH=" + (os.startsWith("Windows")?"%PATH%":"$PATH:/usr/bin:/bin") + this.osenv + gisBase + "/bin" + 
                    	this.osenv + gisBase + "/lib" + 
                    	this.osenv + gisBase + "/scripts" + 
                    	this.osenv + gisBase + "/etc",
                    "GISRC=" + gisRc, 
                    "LOCATION_NAME=location", 
                    "GISDBASE=" + gisDBase , 
                    "SHELL=/bin/bash"};
            this.env = env;

        }
        
        
    }
}


/**
 * GrassThead is executing the command.
 * 
 * @author      Fxp
 */
final class GrassThread extends Thread {

    /**
     * System process
     */
    private Process ps;

    /**
     * Type of command could be:
     * <ul>
     * <li>0 = Return grass output as is.</li>
     * <li>1 = Extract last element value from Grass output. In that case
     *  Grass output MUST be for a command returning an output with a pattern
     *  like A|...|X. The response returned by the Grass will be X. 
     *  That type is used for command like r.what which take as input an X and 
     *  Y parameter returning a value for that position.</li>
     * </ul>
     * 
     */
    private int type;

    /**
     * Talend SDI component identifier
     */
    private String cid;
    
    /**
     * Grass response
     */
    private String response;
    
    /**
     * Class constructor
     */
    public GrassThread(Process ps, String cid, int type) {
        this.ps = ps;
        this.cid = cid;
        this.type = type;
    }
    
    /*
     * Return Grass response
     * 
     * @return The response
     */
    public String getResponse () {
        return this.response;
    }
    
    /**
     * Run the command. 
     */
    public void run () {
        String s = "";
        try {
            java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(this.ps
                    .getInputStream()));
            String line = "";
            try {
                while ((line = reader.readLine()) != null) {
                    switch (type)
                    {
                    case 0: // Return output from Grass
                        s += cid + "|" + line;
                        break;
                    case 1:
                        //Extract last element value from Grass output
                        // for a command output A|....|X. X is returned.
                        s = line.substring(
                                    line.lastIndexOf("|") + 1, 
                                    line.length()
                                    );   
                        
                        break;
                    }
                }
            } finally {
                reader.close();
            }
        } catch (java.io.IOException ioe) {
            ioe.printStackTrace();
        }
        this.response = s;
    }
};

/**
 * GrassTheadError is catching error output.
 * 
 * @author      Fxp
 */
final class GrassThreadError extends Thread {

    /**
     * Process
     */
    private Process ps;
    
    /**
     * Talend SDI component identifier
     */
    private String cid;

    /**
     * Class constructor
     * 
     * @param ps
     * @param cid
     */
    public GrassThreadError(Process ps, String cid) {
        this.ps = ps;
        this.cid = cid;
    }

    /**
     * Run the command. 
     */
    public void run() {
        try {
            java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(this.ps
                    .getErrorStream()));
            String line = "";
            try {
                while ((line = reader.readLine()) != null) {
                    System.err.println(this.cid + "|ERROR|" + line);
                }
            } finally {
                reader.close();
                if (this.ps.exitValue() != 0) {
                    // System.exit(this.ps.exitValue());
                    throw new RuntimeException(this.cid
                            + "|ERROR|Execution failure, there might be an error in your Grass command. [exit code="
                            + this.ps.exitValue() + "]");
                }
            }
        } catch (java.io.IOException ioe) {
            ioe.printStackTrace();
        }
    }
};
