import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Manager {

	public static final void main(String[] args) {
		try {
			HashMap<String, String> parameters = new HashMap<String, String>();

			// Count how many available cores we have
			int coreCount = Runtime.getRuntime().availableProcessors();

			// TODO: These should be read from args, or a config file, or something
			File workingDir = new File("/home/jamie/Development/oversim/OverSim-20101103/simulations");
			String configName = "EpiChordLarge";

			Manager manager = new Manager(coreCount, workingDir, configName, parameters);
			manager.start();
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static int countRuns(File workingDir, String configName) throws IOException
	{
		Process process = Runtime.getRuntime().exec("../src/OverSim -x" + configName, null, workingDir);

		BufferedReader in = null;
		int runs = 0;

		try {
			in = new BufferedReader(new InputStreamReader(process.getInputStream()));

			Pattern p = Pattern.compile("Number of runs: (\\d+)");
			for (String line;(line = in.readLine()) != null;) {
				Matcher m = p.matcher(line);
				if (m.find())
				{
					runs = Integer.parseInt(m.group(1));
					break;
				}
			}
		}
		finally {
			if (in != null)
				in.close();
		}

		return runs;
	}

	protected final File workingDir;
	protected final File resultRootDir;
	protected final File resultDir;
	protected final HashMap<String, String> parameters;
	protected final List<SimulationThread> threads;
	protected boolean finished;

	public Manager(int processCount, File workingDir, String configName) throws IOException {
		this (processCount, workingDir, configName, new HashMap<String, String>());
	}

	public Manager(int processCount, File workingDir, String configName, HashMap<String, String> parameters) throws IOException {
		this.workingDir = workingDir;
		this.parameters = parameters;

		finished = false;

		resultRootDir = new File(workingDir, parameters.containsKey("result-dir") ? parameters.get("result-dir") : "results");
		if (!resultRootDir.isDirectory())
			throw new RuntimeException("Invalid result directory: " + resultRootDir.getAbsolutePath());

		resultDir = new File(resultRootDir, configName + "-" + (System.currentTimeMillis() / 1000));
		if (!resultDir.mkdir())
			throw new RuntimeException("Unable to create result subdirectory.");

		parameters.put("result-dir", resultDir.getAbsolutePath());

		threads = new ArrayList<SimulationThread>(processCount);

		int totalRunCount = Manager.countRuns(workingDir, configName); // Fetch the total run count
		int perProcess = (int) Math.ceil((double)totalRunCount / (double)processCount);
		List<SimulationRun> totalRuns = new ArrayList<SimulationRun>(totalRunCount);

		// Create the list of simulation runs
		for (int i = 0;i < totalRunCount;i++) {
			SimulationRun run = new SimulationRun(configName, i);
			totalRuns.add(run);
		}

		// Shuffle the list so that we don't end up with all similar runs in the same thread
		Collections.shuffle(totalRuns);

		// Check we don't have more threads than we need
		if (processCount > totalRunCount)
			processCount = totalRunCount;

		for (int i = 0;i < processCount;i++)
		{
			int runCount = perProcess > totalRuns.size() ? totalRuns.size() : perProcess; // Check how many to delegate

			SimulationThread thread = new SimulationThread(this, workingDir, parameters);

			// Add runs to be handled by this thread
			for (int j = 0;j < runCount;j++)
				thread.queue(totalRuns.remove(0));

			threads.add(thread);
		}

		System.out.println("Initialized " + threads.size() + " threads, with a total of " + totalRunCount + " runs.");
	}

	public synchronized void start() throws InterruptedException {
		// Start all our threads
		for (SimulationThread thread : threads)
			thread.start();

		while (!finished)
			this.wait();

		// All child threads have now finished, so lets process the data

		System.out.println("Creating CSV file at: " + resultDir.getName() + ".csv");

		// TODO: Save the collated data to a CSV file

		System.out.println("Compressing raw data to: " + resultDir.getName() + ".tar.gz");

		// Save the raw results into an archive
		try {
			// This isn't really portable - really we should use apache commons compression library to create a .tar.gz ourself
			String[] cmd = {"tar", "-czvf", resultDir.getName() + ".tar.gz", resultDir.getName()};

			Process process = Runtime.getRuntime().exec(cmd, null, resultRootDir);
			process.waitFor();
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("Data compression completed, terminating.");
	}

	public synchronized void notifyCompletion(SimulationThread thread, Queue<SimulationRun> completed, Queue<SimulationRun> failed) {
		System.out.println(thread + " completed all simulations, terminating.");
		threads.remove(thread);

		// TODO: Combine the collated data with any existing

		// This was the final thread, so notify the main thread
		if (threads.isEmpty()) {
			finished = true;
			this.notify();
		}
	}
}

@@ -4122,24 +4122,37 @@
 tedException
+, IOException
  %7B%0A%09%09// Star
@@ -4597,136 +4597,8 @@
 ive%0A
-%09%09try %7B%0A%09%09%09// This isn't really portable - really we should use apache commons compression library to create a .tar.gz ourself%0A%09
 %09%09St
@@ -4683,17 +4683,16 @@
 me()%7D;%0A%0A
-%09
 %09%09Proces
@@ -4758,17 +4758,16 @@
 Dir);%0A%09%09
-%09
 process.
@@ -4780,205 +4780,8 @@
 r();
-%0A%09%09%7D%0A%09%09catch (IOException e) %7B%0A%09%09%09// TODO Auto-generated catch block%0A%09%09%09e.printStackTrace();%0A%09%09%7D%0A%09%09catch (InterruptedException e) %7B%0A%09%09%09// TODO Auto-generated catch block%0A%09%09%09e.printStackTrace();%0A%09%09%7D
 %0A%0A%09%09

Throw an IOException rather than catch it.