package org.phosphoresce.commons.lang;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.phosphoresce.commons.util.ExceptionUtil;
import org.phosphoresce.commons.util.StringUtil;

/**
 * コマンド実行プロセッサクラス<br>
 * 
 * @deprecated 暫定
 * @author Kitagawa<br>
 * 
 *<!--
 * 更新日		更新者			更新内容
 * 2010/11/23	Kitagawa		新規作成
 *-->
 */
public class CommandProcessor {

	/** 実行コマンド */
	private String command;

	/** キャラクタセット */
	private String charset;

	/** プロセスオブジェクト */
	private Process process;

	/** プロセス入力オブジェクト */
	private Writer sysin;

	/** プロセス標準出力オブジェクト */
	private Writer sysout;

	/** プロセス標準出力スレッド */
	private Thread sysoutThread;

	/** プロセスエラー出力オブジェクト */
	private Writer syserr;

	/** プロセスエラー出力スレッド */
	private Thread syserrThread;

	/** プロセス実行待機スレッド */
	private Thread waitforThread;

	/** コンソール出力バッファ */
	private List consoleBuffer;

	/**
	 * コンストラクタ<br>
	 * @param command 実行コマンド
	 * @param charset キャラクタセット
	 * @param sysout コマンド入力オブジェクト
	 * @param syserr エラー出力オブジェクト
	 * @throws UnsupportedEncodingException サポートされないキャラクタセットが指定された場合にスローされます
	 */
	public CommandProcessor(String command, String charset, OutputStream sysout, OutputStream syserr) throws UnsupportedEncodingException {
		super();
		this.command = StringUtil.isEmpty(command) || StringUtil.isEmpty(command.trim()) ? "java --version" : command.trim();
		this.charset = StringUtil.isEmpty(charset) || StringUtil.isEmpty(charset.trim()) ? "MS932" : charset.trim();
		this.process = null;
		this.sysin = null;
		this.sysout = new BufferedWriter(new OutputStreamWriter(sysout == null ? System.out : sysout));
		this.sysoutThread = null;
		this.syserr = new BufferedWriter(new OutputStreamWriter(syserr == null ? System.err : syserr));
		this.syserrThread = null;
		this.waitforThread = null;
		this.consoleBuffer = new LinkedList();
	}

	/**
	 * コンストラクタ<br>
	 * @param command 実行コマンド
	 * @param charset キャラクタセット
	 * @throws UnsupportedEncodingException サポートされないキャラクタセットが指定された場合にスローされます
	 */
	public CommandProcessor(String command, String charset) throws UnsupportedEncodingException {
		this(command, charset, null, null);
	}

	/**
	 * コンストラクタ<br>
	 * @param command 実行コマンド
	 * @throws UnsupportedEncodingException サポートされないキャラクタセットが指定された場合にスローされます
	 */
	public CommandProcessor(String command) throws UnsupportedEncodingException {
		this(command, null, null, null);
	}

	/**
	 * コンストラクタ<br>
	 * @throws UnsupportedEncodingException サポートされないキャラクタセットが指定された場合にスローされます
	 */
	private CommandProcessor() throws UnsupportedEncodingException {
		this(null, null, null, null);
	}

	/**
	 * プロセスが実行中であるか判定します。<br>
	 * @return プロセスが実行中である場合にtrueを返却
	 */
	public boolean isProcessing() {
		return process != null;
	}

	/**
	 * コマンドプロセスを実行します。<br>
	 * @throws Throwable コマンド実行中に予期せぬエラーが発生した場合にスローされます
	 */
	public synchronized void execute() throws Throwable {
		if (isProcessing()) {
			throw new IllegalThreadStateException("process has not exited");
		}

		// コマンドプロセス実行
		process = Runtime.getRuntime().exec(command);

		// 出力リダイレクトスレッド開始
		sysoutThread = new StreamOutputThread(process.getInputStream(), sysout);
		syserrThread = new StreamOutputThread(process.getErrorStream(), syserr);
		sysoutThread.start();
		syserrThread.start();

		// コマンド入力オブジェクト生成
		sysin = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

		// プロセス待機スレッド開始
		waitforThread = new WaitForThread();
		waitforThread.start();
	}

	/**
	 * プロセス入力待ちに対して文字列を入力します。<br>
	 * プロセスが未実行状態、若しくは終了処理中に入力された場合、それらの入力値は破棄されます。<br>
	 * 即ち、文字列の入力は必ず、プロセスが待機待ちとなる場合に対して入力する必要があります。<br>
	 * @param input 入力文字列
	 */
	public void input(String input) {
		if (!isProcessing()) {
			return;
		}
		try {
			sysin.write(input);
			sysin.write("\n");
			sysin.flush();
			//consoleBuffer.add(input);
		} catch (IOException e) {
			return;
		}
	}

	/**
	 * コンソール出力バッファを取得します。<br>
	 * @return コンソール出力バッファ
	 */
	public List getConsoleBuffer() {
		return consoleBuffer;
	}

	/**
	 * コンソール出力バッファを取得します。<br>
	 * @return コンソール出力バッファ
	 */
	public String getConsoleBufferString() {
		StringBuffer buffer = new StringBuffer();
		List list = new LinkedList();
		list.addAll(consoleBuffer);
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			String line = (String) iterator.next();
			buffer.append(line).append("\n");
		}
		return buffer.toString();
	}

	/**
	 * 実行プロセス待機スレッドクラス<br>
	 * 
	 * @author Kitagawa<br>
	 * 
	 *<!--
	 * 更新日		更新者			更新内容
	 * 2010/11/23	Kitagawa		新規作成
	 *-->
	 */
	private class WaitForThread extends Thread {

		/**
		 * コンストラクタ<br>
		 * @param process プロセスオブジェクト
		 */
		public WaitForThread() {
			super();
		}

		/**
		 * プロセスの終了を待機します。<br>
		 * @see java.lang.Thread#run()
		 */
		public void run() {
			try {
				process.waitFor();
				sysoutThread.join();
				syserrThread.join();
				//waitforThread.join();
				//sysin.close();
				//sysout.close();
				//syserr.close();
				//sysin = null;
				//sysout = null;
				//syserr = null;
				sysoutThread = null;
				syserrThread = null;
				waitforThread = null;
				process = null;
			} catch (Throwable e) {
				throw new RuntimeException(e);
			}
		}
	}

	/**
	 * ストリーム出力スレッドクラス<br>
	 * 
	 * @author Kitagawa<br>
	 * 
	 *<!--
	 * 更新日		更新者			更新内容
	 * 2010/11/23	Kitagawa		新規作成
	 *-->
	 */
	private class StreamOutputThread extends Thread {

		/** リーダーオブジェクト */
		private BufferedReader reader;

		/** リダイレクトライターオブジェクト */
		private PrintWriter redirect;

		/**
		 * コンストラクタ<br>
		 * @param stream ストリームオブジェクト
		 * @param redirect リダイレクトライターオブジェクト
		 * @throws UnsupportedEncodingException サポートされないキャラクタセットが指定された場合にスローされます
		 */
		private StreamOutputThread(InputStream stream, Writer redirect) throws UnsupportedEncodingException {
			super();
			if (stream == null) {
				throw new NullPointerException("stream");
			}
			if (redirect == null) {
				throw new NullPointerException("redirect");
			}
			this.reader = new BufferedReader(new InputStreamReader(stream, charset));
			this.redirect = new PrintWriter(redirect);
		}

		/**
		 * ストリーム内容の出力を行います。<br>
		 * @see java.lang.Thread#run()
		 */
		public void run() {
			String line = null;
			try {
				while ((line = reader.readLine()) != null) {
					redirect.println(line);
					redirect.flush();
					consoleBuffer.add(line);
				}
			} catch (Throwable e) {
				redirect.println(ExceptionUtil.getStackTraceValue(e));
				redirect.flush();
			}
		}
	}
}
