package jp.ac.nii.icpc2010.tournament;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import jp.ac.nii.icpc2010.manager.OptionsManager;


public class TournamentMaster extends Thread {
	private Tournament tournament; Round currentRound;
	private Queue<QueuedMatch> queuedMatches = new ConcurrentLinkedQueue<QueuedMatch>();
	private int runningMatches;
	
	public TournamentMaster(List<Player> players, int slaves) {
		super();
		
		Logger.log("TOURNAMENT for "+players.size()+" teams");
		
		// setup round 0 representing the initial state
		tournament = new Tournament();
		currentRound = new Round(0); tournament.addRound(currentRound);
		Group g0 = new Group();
		for (int p = 0; p < players.size(); p++) {
			g0.addPlayer(players.get(p));
		}
		currentRound.addGroup(g0);
		
		queueRound();
		
		start();

		int x = 0; while (x++ < slaves) {
			new TournamentSlave(this);
		}
	}
	
	public QueuedMatch pollQueuedMatch() {
		return queuedMatches.poll();
	}
	
	public synchronized void reportFinishedMatch() {
		runningMatches--;
	}
	
	@Override
	public void run() {
		while (true) {
			while (runningMatches != 0) {
				try {
					sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			for (Group g : currentRound.getGroups()) {
				g.computeTotalScores();
				g.flagComputed();
			}
			
			if (currentRound.getGroups().size() > 1) {
				queueRound();
			} else {
				List<Player> finalProceedingPlayers = currentRound.collectProceedingPlayers();
				Collections.sort(finalProceedingPlayers);
				tournament.setWinner(finalProceedingPlayers.get(finalProceedingPlayers.size()-1));
				Logger.log("WINNER = "+tournament.getWinner());
				break;
			}
		}
	}
	
	private void queueRound() {
		runningMatches = 0;
		// which players proceed from last round?
		List<Player> proceedingPlayers = currentRound.collectProceedingPlayers();
		Logger.log("# of proceeding players = "+proceedingPlayers.size());
		// queue round!
		List<Group> currentGroups = splitIntoGroups(proceedingPlayers);
		currentRound = new Round(currentRound.getNumber()+1); tournament.addRound(currentRound);
		for (Group g : currentGroups) {
			currentRound.addGroup(g);
		}
		Logger.log("LEVEL " + currentRound.getNumber() + " (" + currentGroups.size() + " groups)");
		int x = 0;
		for (Group g : currentRound.getGroups()) {
			Logger.log("GROUP "+(++x)+" = "+g);
			queueGroup(g, currentRound);
		}
	}
	
	private void queueGroup(Group group, Round round) {
		List<Player> players = group.getPlayers();
		for (int p = 0; p < players.size(); p++) {
			for (int q = p+1; q < players.size(); q++) {
				Player one = players.get(p), two = players.get(q);
				queuedMatches.add(new QueuedMatch(one, two, round, group));
				runningMatches++;
			}
		}
	}
	
	private static List<Group> splitIntoGroups(List<Player> players) {
		List<Group> groups = new ArrayList<Group>();
		
		int groupCount = (int) Math.ceil(players.size() / 4.0);
		for (int x = 0; x < groupCount; x++) {
			groups.add(new Group());
		}
		
		int curGroup = 0;
		for (int p = 0; p < players.size(); p++) {
			groups.get(curGroup).addPlayer(players.get(p));
			curGroup = (curGroup + 1) % groupCount;
		}

		return groups;
	}
}
