package fireTester.tests.batch;


import java.io.File;
import java.io.IOException;
import java.io.Serializable;

import com.dragonsoft.tryapp.common.SubmissionObj;

import fireTester.interfaces.ClientController;
import fireTester.interfaces.SubmissionTest;
import fireTester.messages.MaxDirectorySizeExceededException;
import fireTester.messages.TimeoutExceededException;
import fireTester.messages.UnknownException;



/**
 * A SubmissionTest is the object that will be executed on a single client on the 
 * distributed network.  It may be comprised of multiple test units.  Each unit
 * may report TestUnitResults back to the server before executing the remaining units.
 * 
 * This is a BatchTest, or a SubmissionTest which is comprised of a series of
 * command line instructions.
 */
public class BatchTest implements SubmissionTest, Serializable {	
	private SubmissionObj _submission;
	private BatchTestUnit[] _units; // 1+
	private long _max_directory_b;
	private String[] _environment;
	private BatchCommand[] _pre_command; // executed before any unit is executed
	private BatchCommand[] _post_command; // after all units complete
	private File _saveDir;
	
	/**
	 * Constructor.  Creates a test that may be saved for future submissions.
	 * 
	 * @param units 1 or more test units.
	 * @param max_directory_b the max size of working directory.
	 * @param environment the enironment variables for shell commands.
	 * @param pre_command any number of commands to execute before test units are executed.
	 * @param post_command any number of commands to execute after test units are executed.
	 */
	public BatchTest(BatchTestUnit[] units,
			long max_directory_b, String[] environment, BatchCommand[] pre_command, 
			BatchCommand[] post_command) {
		assert units != null;
		assert units.length > 0;
		assert max_directory_b > 0;
		
		this._units = units;
		this._max_directory_b = max_directory_b;
		this._environment = environment;
		
		if(pre_command != null)
			this._pre_command = pre_command;
		else
			this._pre_command = new BatchCommand[0];
		
		if(post_command != null)
			this._post_command = post_command;
		else 
			this._post_command = new BatchCommand[0];
	}

	/**
	 * @param submission The submission object.
	 * @param saveDir the directory to save the results too.
	 */
	public void set_submission(SubmissionObj submission, File saveDir) {
		assert submission != null;
		assert saveDir != null;
		assert saveDir.isDirectory();
		
		this._submission = submission;
		this._saveDir = saveDir;
	}
	
	/**
	 * @return The submission object.
	 */
	public SubmissionObj get_submission() {
		return this._submission;
	}
	
	/**
	 * @param unitID the unit to check
	 * 
	 * @return the maximum number of ms the specified unit is allowed to run
	 */
	public long getUnitTimeout(int unitID) {
		assert unitID >= 0;
		assert unitID < this._units.length;
		
		return this._units[unitID].get_timeout_ms();
	}
		
	/**
	 * Begins test execution.  This call will block until all test units
	 * have completed - or till the first required test fails.
	 * 
	 * @param results_controller the object needed to send results
	 * 
	 * @throws TimeoutExceededException the executing command was halted because 
	 * 	the timeout had been violated.
	 * @throws MaxDirectorySizeExceededException the executing command was halted
	 * 	because the max directory size had been violated.
	 * @throws UnknownException an unknown exception has occured.
	 */
	public void execute(ClientController results_controller) 
		throws TimeoutExceededException, MaxDirectorySizeExceededException, UnknownException {
		assert results_controller != null;
		
		try {
			for(int i = 0; i < this._pre_command.length; i++) {
				this._pre_command[i].execute(this._environment, results_controller.get_working_directory(), 
						this._max_directory_b, BatchReturnType.exitCode);
			}

			for(int i = 0; i < this._units.length; i++) {
				if(this._units[i].execute(results_controller, this._environment,
						results_controller.get_working_directory(), this._max_directory_b, this._submission)) {
					// testing complete
					break;
				}
			}
	
			for(int i = 0; i < this._post_command.length; i++) {
				this._post_command[i].execute(this._environment, results_controller.get_working_directory(), 
						this._max_directory_b, BatchReturnType.exitCode);
			}
		} catch(TimeoutExceededException e) {
			e.set_submission(this._submission);
			throw e;
		} catch(MaxDirectorySizeExceededException e) {
			e.set_submission(this._submission);
			throw e;
		} catch(IOException io_ex) {
			UnknownException e = new UnknownException(this._submission, io_ex);
			throw e;
		} catch(Exception ex) {
			UnknownException e = new UnknownException(this._submission, ex);
			throw e;
		} 
	}
	
	
	/**
	 * The directory to save the results too.
	 * 
	 * @return directory.
	 */
	public File getSaveDir() {
		return this._saveDir;
	}

}
