class TournamentOrganizer(object):
	def __init__(self):
		self.players = {}
		self.pairings = {}
		self.round_num = 0
		self.rounds = 0
		self.timer = Timer()

	def add_player(self, name, user):
		if user.id in [player.user.id for player in self.players.values()]:
			raise TournamentException(user.name + '#' + str(user.id) + ' has already been added')
		elif user.name in self.players:
			existing_player = self.players[user.name]
			self.players.pop(user.name)

			new_tag = existing_player.name + '#' + str(existing_player.user.id)
			existing_player.name = new_tag
			self.players[new_tag] = existing_player

			tag = name + '#' + str(user.id)
			self.players[tag] = Player(tag, user)
		else:
			self.players[user.name] = Player(user.name, user)

	def remove_player(self, name):
		if name in self.players:
			um.save_user(self.players[name].user)
			del self.players[name]
		else:
			raise TournamentException(name + ' has not been added yet')

	def record_win(self, winner, record):
		if winner in self.pairings:
			if winner in self.players:
				self.players[winner].record_win(record)
			opponent = self.pairings[winner]
			if not opponent == None and opponent in self.pairings:
				if opponent in self.players:
					self.players[opponent].record_loss(record)
				del self.pairings[opponent]
			del self.pairings[winner]
		else:
			raise TournamentException('Win not recorded, pairing for player ' + winner + ' does not exist')

	def record_loss(self, loser, record):
		if loser in self.pairings.keys():
			if loser in self.players:
				self.players[loser].record_loss(record)
			opponent = self.pairings[loser]
			if not opponent == None and opponent in self.pairings:
				if opponent in self.players:
					self.players[opponent].record_win(record)
				del self.pairings[opponent]
			del self.pairings[loser]
		else:
			raise TournamentException('Win not recorded, pairing for player ' + winner + ' does not exist')

	def record_draw(self, player, record):
		if player in self.pairings:
			if not self.pairings[player] == None and self.pairings[player] in self.pairings:
				player_wins, opp_wins, draws = record
				if player in self.players:
					self.players[player].record_draw(record)
				opponent = self.pairings[player]
				if opponent in self.players:
					self.players[opponent].record_draw((opp_wins, player_wins, draws))
				del self.pairings[player]
				del self.pairings[opponent]
			else:
				raise TournamentException('Draw not recorded, player ' + player + ' has a bye and cannot draw')
		else:
			raise TournamentException('Draw not recorded, pairing for player ' + player + ' does not exist')

	def sorted_players(self, method='by_rank'):
		if method == 'by_rank':
			players = self.players.values()
			return [player.name for player in sorted(players)]
		elif method == 'by_name':
			return [player for player in sorted(self.players.keys())]
		elif method == 'random':
			players = self.players.keys()
			random.shuffle(players)
			return players

	def make_pair(self, p1, p2):
		self.pairings[p1] = p2
		if p2 != None:
			self.pairings[p2] = p1

	def make_restrictions(self):
		restrictions = []
		for p1 in self.players.keys():
			for p2 in self.players[p1].opponents:
				restrictions.append((p1, p2.name))
		return restrictions

	def make_pairings(self):
		if to.round_num == 0 and len(to.players) < 4:
			raise TournamentException('Can\'t start tournament, not enough players')
		if len(self.pairings) > 0:
			raise TournamentException('Can\'t make pairings, round still in progress')

		restrictions = self.make_restrictions()
		restrictions.append(None)
		unpaired_count = 2

		MAX_BYES = len(to.players) % 2
		while unpaired_count > MAX_BYES:

			self.pairings = {}

			restrictions = restrictions[:-1]

			unpaired = self.sorted_players() + [ None ]
			unpaired_count = len(unpaired) - 1

			while len(unpaired) > 1 and not unpaired[0] == None:
				p1 = unpaired[0]
				unpaired = unpaired[1:]

				restricted = []

				paired = False

				while not paired and len(unpaired) > 0:
					p2 = unpaired[0]
					unpaired = unpaired[1:]
					if (p1, p2) in restrictions:
						restricted.append(p2)
					else:
						self.make_pair(p1, p2)
						paired = True
						if p2 == None:
							unpaired_count -= 1
						else:
							unpaired_count -= 2

				unpaired = restricted + unpaired

	def lock_pairings(self):
		if self.round_num == 0:
			self.rounds = int(math.ceil(math.log(len(self.players), 2)))

		self.round_num += 1
		self.timer.set(3000)
		self.timer.start()

		for p1, p2 in self.pairings.items():
			if not p2 == None:
				self.players[p1].opponents.add(self.players[p2])
				self.players[p2].opponents.add(self.players[p1])

	def game_end(self):
		for name, player in self.players.items():
			um.save_user(player.user)

	def reset(self):
		self.players = {}
		self.pairings = {}
		self.round_num = 0
		self.rounds = 0
		self.timer.reset()