/*
 * Galatea Dialog Manager:
 * (c)2003 Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
 * Based on Phoenix By Takuya NISHIMOTO and Mitsuhiro KIZU
 *
 * $Id: SubProcess.java,v 1.10 2008/02/14 02:00:07 nishi Exp $
 */
package galatea.util;


import galatea.logger.Logger;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;

public class SubProcess {
	private Logger dbg = new Logger("SubProcess"); 
	private String module_command;   // サブモジュールのコマンド
	private Runtime rt;              // 現アプリケーションの Runtime オブジェクト
	private Process child;           // サブモジュールの Process オブジェクト
	private InputStream out;         // サブモジュールの標準出力
	private InputStream err;         // サブモジュールの標準エラー
	private OutputStream in;         // サブモジュールの標準入力
	private PrintStream stdin;
	private BufferedReader stdout;
	private BufferedReader stderr;
	private boolean running = false;
	private String error_;
	int maxCommandLength_ = 99999;
	
	public SubProcess() {
		maxCommandLength_ = Property.getForPlatformAsInt( "SubProcess.MaxCommandLength", 99999);
		error_ = "";
	}
	
	public SubProcess(String com) {
		this();
		module_command = com;
	}
	
	public void setCommand(String com) {
		module_command = com;
	}
	
	public boolean runWithShell() {
		String[] com;
		if (module_command.length() == 0){
			dbg.err("SubProcess.Run(): 'module_command' is undefined.");
			return false;
		}
		if (Util.isWindows()) {
//			com = new String[3];
//			com[0] = "c:\\cygwin\\bin\\sh";
//			com[1] = "-c";
//			com[2] = module_command;
			com = new String[3];
			com[0] = "cmd";
			com[1] = "/C";
			com[2] = module_command;
		} else {
			com = new String[3];
			com[0] = Property.getAsStr("SubProcess.shell", "/bin/sh");
			com[1] = "-c";
			com[2] = module_command;
		}
		
		rt = Runtime.getRuntime();
		if (rt == null) {
			error_ += "SubProcess.Run(): getRuntime() failed.";
			dbg.err("SubProcess.Run(): getRuntime() failed.");
			return false;
		}
		try {
			child = rt.exec(com);
		} catch(IOException e) {
			error_ += e.toString();
			dbg.err("SubProcess.Run(): Runtime.exec() failed.");
			return false;
		}
		in = child.getOutputStream();
		out = child.getInputStream();
		err = child.getErrorStream();
		stdin = new PrintStream(in);
		stdout = new BufferedReader(new InputStreamReader(out));
		stderr = new BufferedReader(new InputStreamReader(err));
		running = true;
		return true;
	}
	
	/**
	 * do Runtime.exec
	 * @param dirpath : current directory path
	 * @return false if error
	 */
	public boolean run(String dirpath) {
		if (module_command.length() == 0){
			String s ="SubProcess.run(): 'module_command' is undefined."; 
			dbg.err(s);
			error_ += s;
			return false;
		}
		if (module_command.length() > maxCommandLength_){
			String s = "SubProcess.run(): command too long:" + module_command + "\n";
			dbg.err(s);
			error_ += s;
			return false;
		}
		rt = Runtime.getRuntime();
		if (rt == null) {
			dbg.err("SubProcess.run(): getRuntime() failed.");
			error_ += "SubProcess.run(): getRuntime() failed.";
			return false;
		}
		File dir = new File(dirpath);
		String[] com = module_command.split(" ");
		try {
			child = rt.exec(com, null, dir);
		} catch (IOException e) {
			error_ += "SubProcess.run(): Runtime.exec() failed.\n";
			error_ += "dir=" + dir.toString() + "\n";
			error_ += e.toString();
			dbg.err("SubProcess.run(): Runtime.exec() failed.");
			return false;
		}
		in = child.getOutputStream();
		out = child.getInputStream();
		err = child.getErrorStream();
		stdin = new PrintStream(in);
		stdout = new BufferedReader(new InputStreamReader(out));
		stderr = new BufferedReader(new InputStreamReader(err));
		running = true;
		return true;
	}
	
	public boolean isRunning() {
		return running;
	}
	
	public void sendToStdin(String str) {
		dbg.print("ToStdin: "+str, 9);
		stdin.println(str);
		stdin.flush();
	}
	
	public String receiveFromStdout() {
		if (stdout == null) return "";
		String s;
		try {
			s = stdout.readLine(); // ストリームの終わりに達している場合は null
			dbg.print("FromStdout: "+s, 9);
		} catch (IOException e){
			error_ += e.toString();
			dbg.err("SubProcess.FromStdout(): BufferedReader.readLine() failed.", 8);
			return null;
		}
		return s;
	}
	
	public String receiveFromStdoutNB() {
		String s;
		try {
			if (stdout != null && stdout.ready()) {
				s = stdout.readLine();
				dbg.print("FromStdoutNB: "+s, 9);
			} else {
				s = "";
			}
		} catch(IOException e) {
			error_ += e.toString();
			dbg.err("SubProcess.FromStdout(): BufferedReader.readLine() failed.");
			return null;
		}
		return s;
	}
	
	public String receiveFromStderr() {
		if (stderr == null) return "";
		String s;
		try {
			s = stderr.readLine();
		} catch(IOException e){
			error_ += e.toString();
			dbg.err("SubProcess.FromStderr(): BufferedReader.readLine() failed.");
			return null;
		}
		return s;
	}
	
	public String receiveFromStderrNB() {
		String s;
		try {
			if (stderr != null && stderr.ready())
				s = stderr.readLine();
			else
				s = "";
		} catch (IOException e) {
			error_ += e.toString();
			dbg.err("SubProcess.FromStderr(): BufferedReader.readLine() failed.");
			return null;
		}
		return s;
	}
	
//	public InputStream GetStdout() {
//		if (running)
//			return out;
//		else
//			return null;
//	}
//	
//	public InputStream GetStderr() {
//		if (running)
//			return err;
//		else
//			return null;
//	}
//	
//	public OutputStream GetStdin() {
//		if(running)
//			return in;
//		else
//			return null;
//	}
	
	public void waitFor() throws InterruptedException {
		child.waitFor();
		running = false;
	}
	
	public void destroy() {
		child.destroy();
		running = false;
	}
	
	public int exitValue() throws IllegalThreadStateException{
		return child.exitValue();
	}

	public String getError() {
		return error_;
	}
	
	public void clearError() {
		error_ = "";
	}
	
}
