package taskspaces.system;

import java.io.*;
import java.util.*;
import java.net.*;

/**
 * Enables execution and reading of output from an
 * external executable.
 * @author Rob Markel, Matthew Keoshkerian
 * @version 2.0
 */
public class ProcessManager implements Serializable
{ 
   /** Holds output from process. */
   public static String output=""; 

   /** The value if completed. <code>1</code> for done, <code>0</code> if not. */
   public static int done;

   /** 
   * Downloads the given file name and installs in the given path location.
   * Can make the file executable on Linux or Unix.
   * @param fullPath path and file name for downloaded file.
   * @param fileName the file to download.
   * @param executable make the file executable.
   * @param deleteOnExit delete the file on Virtual Machine termination.
   * @return <code>true</code> if successful. 
   */
   public boolean getResource(String fullPath,String fileName,boolean executable,boolean deleteOnExit)
   {
      try
      {
         File f=new File(fullPath);
         if(!f.exists())
         {
            if(deleteOnExit)
            {
               f.deleteOnExit();
            }
            FileOutputStream fos=new FileOutputStream(f);
            URLClassLoader u=(URLClassLoader)this.getClass().getClassLoader();
            URL location=u.findResource(fileName);
            BufferedInputStream data=new BufferedInputStream(location.openStream());
            int b;
            while((b=data.read())!=-1) {fos.write(b);}
            data.close();
            fos.close();
            if(executable)
            {
               String os=System.getProperty("os");
               if(os.equals("Linux") || os.equals("Unix"))
               { 
                  Process p=Runtime.getRuntime().exec("chmod 700 "+f);
                  p.waitFor();
                  if(!(p.exitValue()==0)) return false;
               }
            }
         }
      }
      catch(Exception e) {e.printStackTrace();}
      return true;
   }

   /** 
   * Executes a native program.
   * @param command program to execute.
   * @return data written to standard out. 
   */
   public String execute(String command) throws Exception
   {
      Process p;
      OutputStream outStream;
      InputStream errorStream;

      try
      {
	  output="";
          done=0;
	  //System.out.println("clearing output...");
	  // The './' was added for systems that won't execute unless that is there.
	  p=Runtime.getRuntime().exec("./" + command);
	  outStream = p.getOutputStream();
	  errorStream = p.getErrorStream();
	  (new Thread(new ProcessReader(p.getInputStream()))).start();
	  //(new Thread(new ProcessReader(p.getErrorStream()))).start();
      }
      catch(java.io.IOException e)
      {
         System.out.println("ERROR executing: "+command+" "+e+".\n");   
	 e.printStackTrace();
         return null;
      }
      try
      {
         p.waitFor();
         //p.destroy();
         outStream.close();
         errorStream.close();
      }
      catch(InterruptedException e){}

      while(done==0){}

      if(p.exitValue()==0)
      {
         return output;
      }
      else
      {
         System.out.println("ERROR in executing: "+command+".");
         return null;
      }
   }
}

/**
 * Reads text written to standard output and standard error
 * from native process in separate thread.
 */
class ProcessReader implements Runnable
{
   /** Reference to the passed standard output or error stream of an executing process. */
   private java.io.BufferedReader myStream;

   public ProcessReader(java.io.InputStream inStream)
   {
      myStream=new java.io.BufferedReader(new java.io.InputStreamReader(inStream));
   }

   /**
    * Monitors output streams in independent thread. 
    * @return void
    */
   public void run()
   {
      try
      {
         char[] cbuf=new char[5096];
         int numRead=myStream.read(cbuf);
         while(numRead!=-1)
         {
            ProcessManager.output=(new String(cbuf,0,numRead));
            numRead=myStream.read(cbuf);
         }
	 ProcessManager.done=1;
      }
      catch(java.io.IOException e)
      {
         System.out.println("ERROR reading from process stream of:"+e);
      }
      finally
      {
         try
         {
            myStream.close();
            //System.out.println(" closing the stream.... \n");   
         }
         catch(java.io.IOException e) {}
         myStream=null;
      } 
   }
}
