package de.hsadmin.core.qserv;

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

public class CommandShell
{
	private static boolean bExecute = true; // really execute or just store command and stdin?
	private static String executedCommands; // stored command and stdin
	private static String[] aEnvironment; // stored environment

	/** Set mode of real execution or just storing the command and stdin.
	 * 
	 * 	@param bExec
	 * 		specifies whether shell commands should really be executed (true) or not (false) 
	 */
	public static void setExecute( boolean bExec )
	{
		bExecute = bExec;
	}
	
	/** Returns and clears the last command which should have been executed.
	 * 
	 * @return
	 * 		Last command, plus "<<EOF\n" + stdin + "EOF" if stdin was given. 
	 */
	public static String popLastCommand() 
	{ 
		String aLastCommand = executedCommands;
		executedCommands = null;
		return aLastCommand != null ? aLastCommand.trim() : null;
	}
	
	/** Returns and clears the environment which ist currently used for commands. 
	 * 
	 * @return
	 * 		Most recent command environment. 
	 */
	public static String[] popEnvironment()
	{
		String[] aLastEnvironment = aEnvironment;
		aEnvironment = null;
		return aLastEnvironment;
	}

	public static void setEnvironment( String[] env )
	{
		aEnvironment = env;
	}

	/** Execute the given command by a shell.
	 * 
	 * 	@throws ShellException
	 * 		if an execution error has occured.
	 */
	public static String execute( String command ) 
		throws ShellException 
	{
		return execute( command, null );
	}
	
	/** Execute the given command by a shell including stdin.
	 * 
	 * 	@throws ShellException
	 * 		if an execution error has occured.
	 */
	public static String execute( String command, String stdInput ) 
		throws ShellException 
	{
		Process backend = null;
		String callOutput = null;
		int exitCode = 0;
		try 
		{
			String logCommand = command;
			if ( stdInput != null ) //&& stdInput.length() > 0 )
				logCommand += "<<EOF\n" + stdInput + "EOF";
			logCommand += "\n";
			
			// add to command buffer (used in tests)
			if ( executedCommands == null )
				executedCommands = logCommand;
			else
				executedCommands += logCommand;
			
			if ( bExecute )
			{
				// TODO logging
				System.out.println("--- shell command: ---" );
				System.out.println( logCommand );
				
				
				String[] cmdArray = { "/bin/bash", "-c", command };
				backend = Runtime.getRuntime().exec(cmdArray, aEnvironment);
				if ( stdInput != null )
				{
					OutputStream stdInputStream = backend.getOutputStream();
					PrintWriter stdInputWriter = new PrintWriter(stdInputStream);
					stdInputWriter.print(stdInput);
					stdInputWriter.close();
					stdInputStream.close();
				}
				callOutput = readProcessStream(backend.getInputStream());
				exitCode = backend.waitFor();

				// TODO logging
				System.out.println( "RET: " + exitCode );

				if (exitCode != 0) 
				{
					String aErr = readProcessStream(backend.getErrorStream());
					
					// TODO logging
					System.out.println( "ERR: " + aErr  );
					System.out.println("--- done. ---" );

					throw new ShellException( exitCode, aErr );
				}
				
				// TODO logging
				System.out.println("--- done. ---" );				
			}
		} 
		catch (IOException e) 
		{
			throw new ShellException();
		} 
		catch (InterruptedException e) 
		{
			throw new ShellException();
		}
		
		if ( callOutput != null )
		{
			// int nLen = callOutput.length();
			// if ( nLen > 0 && callOutput.charAt(nLen-1) == '\n' )
			// callOutput = callOutput.substring(0, nLen-1);
			return callOutput.trim();
		}
		return null;
	}

	private static String readProcessStream(InputStream stream) throws IOException 
	{
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(stream));
		StringBuffer textBuff = new StringBuffer();
		String textLine = reader.readLine();
		while (textLine != null) 
		{
			textBuff.append(textLine);
			textBuff.append('\n');
			textLine = reader.readLine();
		}
		reader.close();
		return textBuff.toString();
	}
}
