# TODO: make sane before committing

from twisted.words.protocols import irc
from txircd.modbase import Mode
from txircd.utils import irc_lower, epoch, now, CaseInsensitiveDictionary
from fnmatch import fnmatch

class BanMode(Mode):
	def __init__(self):
		self.banMetadata = CaseInsensitiveDictionary()
	
	def checkSet(self, user, target, param):
		if "b" in target.mode and len(target.mode["b"]) >= self.ircd.channel_ban_list_size:
			return False
		if "!" not in param and "@" not in param:
			param = "{}!*@*".format(param)
		elif "@" not in param:
			param = "{}@*".format(param)
		elif "!" not in param:
			param = "*!{}".format(param)
		if target.name not in self.banMetadata:
			self.banMetadata[target.name] = {}
		self.banMetadata[target.name][param] = [user.nickname, epoch(now())]
		return True
	
	def checkUnset(self, user, target, param):
		if "!" not in param and "@" not in param:
			param = "{}!*@*".format(param)
		elif "@" not in param:
			param = "{}@*".format(param)
		elif "!" not in param:
			param = "*!{}".format(param)
		for banmask in target.mode["b"]:
			if param == banmask[0]:
				param = banmask
				del self.banMetadata[target.name][param] if param in self.banMetadata[target.name]
				return True
		return False
	
	def commandPermission(self, user, cmd, data):
		if cmd != "JOIN":
			return data
		channels = data["targetchan"]
		if "ban_evaluating" not in user.cache:
			user.cache["ban_evaluating"] = channels
			return "again"
		keys = data["keys"]
		remove = []
		hostmask = irc_lower(user.prefix())
		for chan in user.cache["ban_evaluating"]:
			if "b" in chan.mode:
				for mask in chan.mode["b"]:
					if fnmatch(hostmask, irc_lower(mask)):
						remove.append(chan)
						user.sendMessage(irc.ERR_BANNEDFROMCHAN, chan.name, ":Cannot join channel (You're banned)")
						break
		for chan in remove:
			index = channels.index(chan)
			channels.pop(index)
			keys.pop(index)
		data["targetchan"] = channels
		data["keys"] = keys
		del user.cache["ban_evaluating"]
		return data
	
	def showParam(self, user, target):
		if "b" in target.mode:
			for entry in target.mode["b"]:
				metadata = self.banMetadata[target.name][entry] if target.name in self.banMetadata and entry in self.banMetadata[target.name] else [ self.ircd.server_name, epoch(now()) ]
				user.sendMessage(irc.RPL_BANLIST, target.name, entry, metadata[0], metadata[1])
			if target.name in self.banMetadata:
				removeMask = []
				for mask in self.banMetadata[target.name]:
					if mask not in target.mode["b"]:
						removeMask.append(mask)
				for mask in removeMask:
					del self.banMetadata[target.name][mask]
		elif target.name in self.banMetadata:
			del self.banMetadata[target.name] # clear all saved ban data if no bans are set on channel
		user.sendMessage(irc.RPL_ENDOFBANLIST, target.name, ":End of channel ban list")

def Spawner(object):
	def __init__(self, ircd):
		self.ircd = ircd
	
	def spawn(self):
		return {
			"modes": {
				"clb": BanMode()
			}
		}
	
	def cleanup(self):
		self.ircd.removeMode("clb")
Implement channel mode +b