from twisted.plugin import IPlugin
from twisted.words.protocols import irc
from txircd.config import ConfigValidationError
from txircd.module_interface import Command, ICommand, IModuleData, ModuleData
from txircd.utils import isValidNick, timestampStringFromTime
from zope.interface import implements
from datetime import datetime

class NickCommand(ModuleData):
	implements(IPlugin, IModuleData)
	
	name = "NickCommand"
	core = True
	
	def actions(self):
		return [ ("changenickmessage", 1, self.sendNickMessage),
		         ("changenick", 1, self.broadcastNickChange),
		         ("remotechangenick", 1, self.broadcastNickChange),
		         ("buildisupport", 1, self.buildISupport) ]
	
	def userCommands(self):
		return [ ("NICK", 1, NickUserCommand(self.ircd)) ]
	
	def serverCommands(self):
		return [ ("NICK", 1, NickServerCommand(self.ircd)) ]

	def verifyConfig(self, config):
		if "nick_length" in config:
			if not isinstance(config["nick_length"], int) or config["nick_length"] < 0:
				raise ConfigValidationError("nick_length", "invalid number")
			elif config["nick_length"] > 32:
				config["nick_length"] = 32
				self.ircd.logConfigValidationWarning("nick_length", "value is too large", 32)
	
	def sendNickMessage(self, userShowList, user, oldNick):
		hostmask = "{}!{}@{}".format(oldNick, user.ident, user.host())
		conditionalTags = {}
		self.ircd.runActionStandard("sendingusertags", user, conditionalTags)
		for targetUser in userShowList:
			tags = targetUser.filterConditionalTags(conditionalTags)
			targetUser.sendMessage("NICK", to=user.nick, prefix=hostmask, tags=tags)
		del userShowList[:]
	
	def broadcastNickChange(self, user, oldNick, fromServer):
		self.ircd.broadcastToServers(fromServer, "NICK", timestampStringFromTime(user.nickSince), user.nick, prefix=user.uuid)

	def buildISupport(self, data):
		data["NICKLEN"] = self.ircd.config.get("nick_length", 32)

class NickUserCommand(Command):
	implements(ICommand)
	
	forRegistered = None
	
	def __init__(self, ircd):
		self.ircd = ircd
	
	def parseParams(self, user, params, prefix, tags):
		if not params or not params[0]:
			user.sendSingleError("NickCmd", irc.ERR_NEEDMOREPARAMS, "NICK", "Not enough parameters")
			return None
		if not isValidNick(params[0]) or len(params[0]) > self.ircd.config.get("nick_length", 32):
			user.sendSingleError("NickCmd", irc.ERR_ERRONEUSNICKNAME, params[0], "Erroneous nickname")
			return None
		if params[0] in self.ircd.userNicks:
			otherUser = self.ircd.userNicks[params[0]]
			if user != otherUser:
				user.sendSingleError("NickCmd", irc.ERR_NICKNAMEINUSE, params[0], "Nickname is already in use")
				return None
		return {
			"nick": params[0]
		}
	
	def execute(self, user, data):
		user.changeNick(data["nick"])
		if not user.isRegistered():
			user.register("NICK")
		return True

class NickServerCommand(Command):
	implements(ICommand)
	
	burstQueuePriority = 89
	
	def __init__(self, ircd):
		self.ircd = ircd
	
	def parseParams(self, server, params, prefix, tags):
		if len(params) != 2:
			return None
		if prefix not in self.ircd.users:
			if prefix in self.ircd.recentlyQuitUsers:
				return {
					"lostuser": True
				}
			return None
		user = self.ircd.users[prefix]
		try:
			time = datetime.utcfromtimestamp(float(params[0]))
		except ValueError:
			return None
		if params[1] in self.ircd.userNicks:
			localUser = self.ircd.userNicks[params[1]]
			if localUser != user:
				if localUser.localOnly:
					allowChange = self.ircd.runActionUntilValue("localnickcollision", localUser, user, server, users=[localUser, user])
					if allowChange:
						return {
							"user": user,
							"time": time,
							"nick": params[1]
						}
					if allowChange is False:
						return {
							"user": user,
							"time": time,
							"nick": None
						}
					return None
				return None
		return {
			"user": user,
			"time": time,
			"nick": params[1]
		}
	
	def execute(self, server, data):
		if "lostuser" in data:
			return True
		user = data["user"]
		newNick = data["nick"]
		if not newNick:
			return True # Handled collision by not changing the user's nick
		if newNick in self.ircd.userNicks and self.ircd.userNicks[newNick] != user:
			user.changeNick(user.uuid)
			return True
		user.changeNick(data["nick"], server)
		user.nickSince = data["time"]
		return True

cmd_nick = NickCommand()
@@ -2088,25 +2088,8 @@
 rams
- or not params%5B0%5D
 :%0A%09%09
@@ -2189,24 +2189,140 @@
 return None%0A
+%09%09if not params%5B0%5D:%0A%09%09%09user.sendSingleError(%22NickCmd%22, irc.ERR_NONICKNAMEGIVEN, %22No nickname given%22)%0A%09%09%09return None%0A
 %09%09if not isV

Send the correct numeric when no nick is given