package scireum.common;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Executes an external program.
 */
public class Exec {

	private static class StreamEater implements Runnable {

		private final InputStream stream;
		private final StringBuffer logger;
		private final ValueHolder<IOException> exHolder = new ValueHolder<IOException>(
				null);

		public StreamEater(InputStream stream, StringBuffer log) {
			this.stream = stream;
			logger = log;
		}

		public void run() {
			try {
				InputStreamReader isr = new InputStreamReader(stream);
				BufferedReader br = new BufferedReader(isr);
				String line = br.readLine();
				while (line != null) {
					logger.append(line);
					logger.append("\n");
					line = br.readLine();
				}
				br.close();
			} catch (IOException e) {
				logger.append(Tools.asString(e));
				exHolder.setValue(e);
			}
		}

		public static StreamEater eat(InputStream stream, StringBuffer logger) {
			StreamEater eater = new StreamEater(stream, logger);
			new Thread(eater).start();
			return eater;
		}
	}

	public static class ExecException extends Exception {

		private static final long serialVersionUID = -4736872491172480346L;
		private String log;

		public ExecException(Exception root, String log) {
			super(root);
			this.log = log;
		}

		public String getLog() {
			return log;
		}

	}

	/**
	 * Executes the given command and returns a transcript of stderr and stdout.
	 */
	public static String exec(String command) throws ExecException {
		StringBuffer logger = new StringBuffer();
		try {
			Process p = Runtime.getRuntime().exec(command);
			StreamEater errEater = StreamEater.eat(p.getErrorStream(), logger);
			StreamEater outEater = StreamEater.eat(p.getInputStream(), logger);
			try {
				p.waitFor();
			} catch (InterruptedException e) {
				throw new ExecException(e, logger.toString());
			}
			if (errEater.exHolder.getValue() != null) {
				throw new ExecException(errEater.exHolder.getValue(), logger
						.toString());
			}
			if (outEater.exHolder.getValue() != null) {
				throw new ExecException(outEater.exHolder.getValue(), logger
						.toString());
			}
			return logger.toString();
		} catch (IOException e) {
			throw new ExecException(e, logger.toString());
		}
	}
}
