
from girc import replycodes
from girc.message import Kick

# TODO case insensitive users

# TODO track a user's presence in lesser modes so that a sequence like:
#	+v foo
#	+o foo
#	-o foo
# resolves to foo being in v, instead of '' like currently.

# XXX track more info about a user, maybe over multiple channels

class UserList(object):
	"""Tracks users and their privilige levels.

	Since privilige levels differ between servers, we canonically refer to them only by
	the mode letter (eg. 'v') given in server_properties.
	However, we also allow lookup by prefix char (eg. '+') or a friendly name if available
	(eg. "voiced").
	The base unpriviliged level is referred to as simply "users",
	and its mode letter and prefix char are the empty string.
	Note that ('voiced', 'v', '+') and ('op', 'o', '@') are mandated by RFC.

	A set of usernames for a given level can be looked up by getitem (eg. userlist['o'] or userlist['@']).
	You can also look up a level by friendly name as an attribute, eg. userlist.ops
	This usage will return all users with this prefix OR ABOVE.
	For example, userlist['o'] will ALWAYS be a subset of userlist['v'],
	ie. all ops are also considered voiced.

	If you wish to be more specific, you can do operations like userlist.voiced - userlist.ops
	to get all voiced non-ops.
	However, several helper methods exist for your convenience, such as only() and below()
	(see individual docstrings).
	"""

	KNOWN_NAMES = dict(
		owners = 'q',
		admins = 'a',
		ops = 'o',
		halfops = 'h',
		voiced = 'v',
		users = '',
	)

	def __init__(self, client, channel):
		"""Takes a client object and string channel name. Begins watching for relevant messages immediately."""
		self.client = client
		self.channel = channel
		self.parse_prefixes()
		self.client.handler(self.recv_user_list, command=replycodes.replies.NAMREPLY,
		                    params=lambda params: len(params) > 2 and params[2] == channel, sync=True)
		self.client.handler(self.user_join, command='JOIN',
		                    channels=lambda channels: channel in channels, sync=True)
		self.client.handler(self.user_leave, command='PART',
		                    channels=lambda channels: channel in channels, sync=True)
		self.client.handler(self.user_leave, command='KICK', channel=channel, sync=True)
		self.client.handler(self.user_leave, command='QUIT', sync=True)
		self.client.handler(self.user_mode_change, command='MODE', target=channel, sync=True)
		self.client.handler(self.user_nick_change, command='NICK', sync=True)

	def parse_prefixes(self):
		mode_pairs = self.client.server_properties.prefixes

		# special "users" (nothing) mode
		mode_pairs.append(('', ''))

		self.modes = [mode for mode, prefix in mode_pairs]
		self.prefix_map = {prefix: mode for mode, prefix in mode_pairs} # prefix_map maps prefix chars to modes
		self._user_map = {mode: set() for mode in self.modes} # user_map maps modes to users

	def _resolve_name(self, name):
		"""Returns mode given mode, prefix or friendly name, or None"""
		if name in self.modes:
			return name