"""	The Module class """

import re

from functools import wraps

def magic (decorator):
	"""
	Decorates a decorator method so that it can be called in multiple ways
	
	If a function is given to the decorator, it should return the decorated
	function, else it should return the decorator.
	
	The decorator function will always be called with a function.
	
	Yes. This will get confusing.
	What did you expect?
	I'm decorating decorators with decorator.
	"""
	@wraps(decorator)
	def magic_decorator (self, function=None, *args, **kwargs):
		"""
		Calls the decorator if the function is provided,
		else returns the decorator 
		"""
		def call (function):
			return decorator(self, function, *args, **kwargs)
		return call(function) if (function is not None) else call
	return magic_decorator

class Module (list):
	"""
	A module is a list of (tag, listener) tuples, and optionally, a requirements
	attribute consisting of a list of names of modules that it requires.
	
	This class provides some helper functions for creating those tuples.
	"""
	
	def __init__ (self, protocol=None, requirements=None):
		"""
		Any keywords passed to __init__ will be set as module attributes
		
		:Keywords:
			- `requirements` (list): A list of required module names
			- `protocol` (str): The protocol to apply to all listeners
		"""
		super(Module, self).__init__()
		self.protocol = protocol
		self.requirements = requirements
	
	def add_listener (self, tag, function):
		"""
		Adds a listener to the list.
		
		If no tag is provided, uses the name of the function.
		Uses the modules protocol if it has one.
		"""
		tag = tag or function.__name__
		tag = (tag, self.protocol) if self.protocol else tag
		self.append((tag, function))
		return function
	
	@magic
	def listener (self, function, tag=None):
		"""
		Adds a listener to the module.
		
		Can be called multiple ways::
		
			func = Module.listener(func, "message")
			
			@magic
			def message (...):
				...
			
			@magic(tag="message")
			def message (...):
				...
		"""
		return self.add_listener(tag, function)
	
	@magic
	def command (self, function, command=None, tag=None):
		"""
		Adds a listener that checks that the event's text starts with a command.
		
		If `command_prefix` is in the config, it must appear before the command.
		The command uses the `command` parameter, or the name of the function.
		
		Like `listener`, can be called in multiple ways.
		"""
		command = command or function.__name__
		@wraps(function)
		def command_function (event):
			"""	Checks `event.message_text` for the given command """
			prefix = event.handler.config.get('command_prefix', '')
			regex  = "{}{}(?: (?P<text>.*))?".format(prefix, command)
			result = re.match(regex, event.message_text())
			if result:
				return function(event, result.group('text'))
		return self.add_listener(tag or "message", command_function)
		
	def regex (self, regex, tag=None):
		"""
		Adds a listener that checks the event's text with a regex.
		
		If it matches, the listener is called with the event and match objects.
		"""
		regex = re.compile(regex)
		def decorator (function):
			"""	Decorates the function with a check function """
			def regex_function (event):
				"""
				Searches `event.message_text` with the regex,
				calling the function if a match is made
				"""
				result = regex.search(event.message_text())
				if result:
					return function(event, result)
			return self.add_listener(tag or "message", regex_function)
		return decorator

	def __repr__ (self):
		return "Module({})".format([tag for tag, func in self])
