package jp.ac.nii.icpc2010.submission;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringTokenizer;

public class SubmissionController {

	final String uploadsPath = "/Users/florian/Documents/phd/hg/code/sws/keikaku/res/services/portal/test/uploads";
	final String deployPath = "/Users/florian/Documents/phd/hg/code/sws/keikaku/res/services/portal/test/deploy";
	final private String jarFilePath = "/Users/florian/Documents/phd/hg/projects/icpc/jtron.jar";

	HashSet<String> teams = new HashSet<String>();
	Map<String, HashSet<String>> submissions = new HashMap<String, HashSet<String>> ();
	Map<String, HashSet<String>> finalSubmissions = new HashMap<String, HashSet<String>> ();
	Map<String, String> movedFiles = new HashMap<String, String>();
	Map<String, String> teamToClassNames = new HashMap<String, String>();
	Map<String, String> finalSubmissionsReverse = new HashMap<String, String>();
	HashSet<String> javaFiles = new HashSet<String>();

	public void start() {

		getTeamNames();
		getNewestSubmissions();
		deploySubmissions();
		analyzeSubmissionLog();
		createPackageStructure();
		compileFiles();
		runTournament();
		closeLogFile();
	}

	private void analyzeSubmissionLog() {
		debug("Verfying submission log...");
		String logFile = uploadsPath + "/log.xml";

		try {
			BufferedReader input =  new BufferedReader(new FileReader(logFile));
			String line = null;

			HashMap<String, HashSet<String>> teamToIPMap = new HashMap<String, HashSet<String>>();
			while (( line = input.readLine()) != null) {
				if (!line.contains("Team"))
					continue;
				
				String teamName = line.substring(line.indexOf(">") + 1, line.lastIndexOf("<"));
				if (!teamToIPMap.containsKey(teamName)) 
					teamToIPMap.put(teamName, new HashSet<String>());
				
				while (( line = input.readLine()) != null) {
					
					if (!line.contains("IP"))
						continue;
					
					String ip = line.substring(line.indexOf(">") + 1, line.lastIndexOf("<"));
					teamToIPMap.get(teamName).add(ip);
					break;
				}
			}
			
			for (String team : teamToIPMap.keySet()) {
				
				HashSet<String> ips = teamToIPMap.get(team);
				
				if (ips.size() > 1) {
					
					error("***********************************************", false);
					error("  WARNING: Team '" + team + "' has submission from multiple IPs!!", false);
					error("             IPs:", false);
					
					for (String ip : ips) 
						error("                   " + ip, false);
					
					error("  Please check the log file and contact the participants", false);
					error(" Log file: " + logFile, false);
					error("***********************************************", false);
				}
			}
		}
		catch (Exception e) {

			error("Error while parsing log.xml: " + e.getMessage(), false);
			error("Please check submission log.xml manually! Path:" + logFile, false);
			error("Try to continue...", false);
		}
	}

	private void closeLogFile() {
		SubmissionUtils.closeLog();
	}

	private void runTournament() { 

		String cmdLine = "";
		String classPath = deployPath;
		for (String team : teams) {

			//			for (String file : finalSubmissions.get(team)) {
			cmdLine += "\"" + team.replaceAll(" ", "_") + "\" ";
			cmdLine += teamToClassNames.get(team) + " ";
			//			}
		}
		String cmd = "java -classpath " + classPath + ":" + jarFilePath  + " jp.ac.nii.icpc2010.tournament.TournamentRunner -players " + cmdLine;

		Process p;
		try {
			debug("Invoking " + cmd);
			p = Runtime.getRuntime().exec(cmd);
			String line;
			BufferedReader input =
				new BufferedReader
				(new InputStreamReader(p.getInputStream()));
			while ((line = input.readLine()) != null) 
				debug(line);

			input.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// One log file
	// Check whether class files have not unique names

	private void compileFiles() {

		debug("Compiling files...");
		for (String javaFile : javaFiles) {
			try {
				String cmd = compileFile(javaFile);
				if (!(new File(javaFile.replace(".java", ".class")).exists())) {

					error("Could not compile " + javaFile + ". Maybe a programming error in that java file? Command was: " + cmd);
				}
			} catch (IOException e) {
				error("Error while compiling " + javaFile + ":" + e.getMessage());
			}
		}
	}

	private String compileFile(String javaFile) throws IOException {

		debug("Compiling file " + javaFile);
		String cmd = "javac -classpath " + jarFilePath + " " + javaFile;

		Process p = Runtime.getRuntime().exec(cmd);
		String line;
		BufferedReader input =
			new BufferedReader
			(new InputStreamReader(p.getInputStream()));
		while ((line = input.readLine()) != null) {
			debug(line);
		}
		input.close();
		return cmd;
	}

	private void createPackageStructure() {

		debug("Creating package structure...");
		for (String file : finalSubmissionsReverse.keySet())
			createPackageStructure(file, finalSubmissionsReverse.get(file));
	}

	private void createPackageStructure(String filename, String teamname) {
		try {
			BufferedReader input =  new BufferedReader(new FileReader(filename));
			try {
				String line = null;

				boolean found = false;
				while (( line = input.readLine()) != null){
					if (!line.contains("package"))
						continue;

					//package jp.ac.nii.icpc2010.players;
					LinkedList<String> directories = new LinkedList<String>();
					found = true;
					String sub = line.substring(line.indexOf(" ") + 1).replaceAll(";", "");
					teamToClassNames.put(teamname, sub + "." + filename.substring(filename.lastIndexOf("/") + 1, filename.indexOf(".java")));
					StringTokenizer st = new StringTokenizer(sub, ".");
					while (st.hasMoreTokens())
						directories.add(st.nextToken());

					String currentPath = deployPath;
					for (String _dir : directories) {

						currentPath += File.separator + _dir;
						File dir = new File(currentPath);
						if (!dir.exists())
							dir.mkdir();
					}
					File file = new File(filename);
					file.renameTo(new File(new File(currentPath), file.getName()));
					movedFiles.put(filename, currentPath + File.separator + file.getName());
					javaFiles.add(currentPath + File.separator + file.getName());

					break;
				}
				if (!found) {
					teamToClassNames.put(teamname, filename.substring(filename.lastIndexOf("/") + 1, filename.indexOf(".java")));
					movedFiles.put(filename, filename);
					javaFiles.add(filename);
					return;
				}
			}
			finally {
				input.close();
			}
		}
		catch (IOException ex){
			error("Error in submission '" + filename + "':" + ex.getMessage());
			return;
		}
	}

	private void deploySubmissions() {

		File deploy = new File(deployPath);
		if (!deploy.exists()) {
			debug("Creating directory " + deploy);
			deploy.mkdir();
		}

		for (String team : submissions.keySet()) {

			finalSubmissions.put(team, new HashSet<String>());
			for (String file : submissions.get(team)) {

				String filename = file.substring(file.lastIndexOf("/") + 1);
				String toFileName = deployPath + File.separator + filename;
				try {
					SubmissionUtils.copy(file, toFileName);
					finalSubmissions.get(team).add(toFileName);
					finalSubmissionsReverse.put(toFileName, team);
				} catch (IOException e) {
					error(e.getMessage());
				}
			}
		}
	}

	private void getTeamNames() {

		File dir = new File(uploadsPath);
		debug("Checking submission folder...");
		String[] children = dir.list(); 
		if (children == null) { 
			error("Could not find any submission. Is the path '" + uploadsPath + "' correct?");
		} 
		else { 
			debug("Found " + children.length + " teams");
			for (int i=0; i<children.length; i++) { 
				// Get filename of file or directory 
				String filename = children[i]; 
				File f = new File(uploadsPath + "/" + children[i]);
				if (f.isDirectory()) {
					debug("Found team '" + filename + "'");
					teams.add(filename);
				}
			} 
		} 
	}

	private void error(String string) {

		error(string, true);
	}
	
	private void error(String string, boolean terminate) {
		
		SubmissionUtils.error(string, this);
		if (terminate) {
			SubmissionUtils.error("Terminating application", this);
			System.exit(1);
		}
		
	}

	private void debug(String string) {
		SubmissionUtils.debug(string, this);
	}

	private void getNewestSubmissions() {

		for (String team : teams) {

			File dir = new File(uploadsPath + File.separator + team);			
			String[] children = dir.list(); 
			if (children == null) { 
				error("Team '" + team + "' has no submissions. Ignore this entry. ", false);
				continue;
			}
			int newest = 0;
			for (int i=0; i<children.length; i++) {
				try {
					int n = Integer.parseInt(children[i]);
					if (n > newest)
						newest = n;
				}
				catch (Exception e) {
					error("Invalid entry in '" + team + "'");
				}
			}
			debug("Newest submission for team '" + team + "' seems to be " + newest);

			dir = new File(uploadsPath + File.separator + team + File.separator + newest);
			children = dir.list();
			if (children == null) { 
				error("Team '" + team + "' has no submissions. Ignore this entry. ", false);
				continue;
			}

			HashSet<String> hs = new HashSet<String>();
			submissions.put(team, hs);

			debug("Team '" + team + "' has the following submission files in the newest submission:");
			for (int i=0; i<children.length; i++) {

				if (children[i].equals("log.xml"))
					continue;

				if (children[i].endsWith("~"))
					continue;

				debug("  " + children[i]);
				hs.add(uploadsPath + File.separator + team + File.separator + newest + File.separator + children[i]);
			}
		}
	}

	public static void main(String[] args) {
		new SubmissionController().start();
	}
}
