package cnf;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 * A wrapper to a process in this CNF library.
 * 
 * @author Greg Dennis (gdennis@mit.edu)
 */
public final class CNFProcess {
    
    private static final Runtime RUNTIME = Runtime.getRuntime();
    
    private final String command;
    private ProcessListener listener;
    private BufferedWriter out = null;

    /**
     * Constructs a CNFProcess with the specified command string,
     * and the specified (potentially null) process listener.
     * Does NOT begin execution of the process.
     */
    public CNFProcess(String command) {
        //System.out.println(command);
        this.command = command;
    }
    
    /**
     * Sets the listener of this process.
     * Should only be called when the process is not executing.
     *
     * @throws IllegalStateException if process is executing
     */
    public void setListener(ProcessListener listener) {
        if (out != null) throw new IllegalStateException("process is executing");
        this.listener = listener;
    }

    /**
     * Begins execution of the process. Each line written to standard
     * output stream by the process is passed to the readLine method.
     *
     * @throws IOException if an I/O error occurs
     */
    public void execute() throws IOException {
        Process p = null;
        BufferedReader in = null;
        
        try {
	        p = RUNTIME.exec(command);
	        in  = new BufferedReader(new InputStreamReader(p.getInputStream()));
	        out = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
;
	        for (String line = in.readLine(); line != null; line = in.readLine()) {
	            if (listener != null && !listener.readLine(line)) break;
            }
        }
        finally {
            if (in != null) {
                try { in.close(); }
                catch (IOException ignore) {}
            }
            if (out != null) {
                try { out.close(); }
                catch (IOException ignore) {}
                out = null;
            }
            if (p != null) p.destroy();
	    }
    }

    /**
     * Write the specified line to the standard input stream of the process.
     * This should only be called in the context of the ProcessListener.readLine,
     * i.e. when the process is executing.
     * 
     * @throws IllegalStateException if the process is not running
     */
    public void writeLine(String str) throws IOException {
        if (out == null) throw new IllegalStateException("process not executing");
        out.write(str);
        out.newLine();
        out.flush();
    }
    
}
