'''
Created on 17.04.2010
Main-Script of the Warsow-Query-Bot
@author: Orbital
'''
#--------Imports-------------
import threading
import socket
import time
import warsow
#--------Main----------------
mine = False
#This is the main socket for communication with the irc server:
s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
def send( 
		func, #Contains the function e.g. "getplayer" 
		name  #Contains the name of the user that uses the function
		):
	'''
	Handles the functions getname, getplayer, getinfo, getip, decolor, color and help
	and returns their output to the user ( name )
	'''
	
	if "!query" in func:
		#for in-channel compatibility (users will need to set !query in front of the command)
		func = func[7:]
	if func.startswith( "getname" ):
		try: output = "The requested ip (%s) leads to the server: %s" % ( func.split()[1], warsow.irc_color( query.getname( func.split()[1] ) ) )
		except: output = "Error: could not request the name from %s" % ( func.split()[1] )
	elif func.startswith( "top3" ):
		try:
			if func.split()[1].startswith( "i" ):
				result = query.top3( func.split()[1][1:], True )
			else:
				result = query.top3( func.split()[1], False )
			output = "The top 3 %s servers are: %s (%s), %s (%s) and %s(%s)" % ( func.split()[1], result[0][0],
																				result[0][1], result[1][0],
																				result[1][1], result[2][0],
																				result[2][1] )
		except:
			output = "Could not find (enough) %s servers!" % ( func.split()[1] )
	elif func.startswith( "getinfo" ):
		try: 
			result = query.getinfo( func.split()[1], func.split()[2] )
			if result != None:
				output = "The info you requested from %s (%s) is: %s" % ( func.split()[1], func.split()[2], query.getinfo( func.split()[1], func.split()[2] ) )
			else:
				raise ValueError
		except: 
			try: output = "Sorry but that information (%s) is not available!" % func.split()[2]
			except: output = "You have to set an information type!"
	elif func.startswith( "getip" ):
		output = "The ip of the server you requested is: %s" % query.getip( " ".join( func.split()[1:] ) )
	elif func.startswith( "getplayers" ):
		try: 
			output = "These players are currently on %s: %s" % ( func.split()[1], ", ".join( query.getplayers( func.split()[1], True ) ) )
		except: output = "There is noone online on %s!" % ( func.split()[1] )
	elif func.startswith( "getplayer" ):
		try: output = "%s is currently online and playing at %s (%s)" % query.getplayer( " ".join( func.split()[1:] ), True )
		except: output = "Sorry, %s is currently not online." % ( " ".join( func.split()[1:] ) )
	elif func.startswith( "decolor" ):
		output = warsow.decolor( " ".join( func.split()[1:] ) )
	elif func.startswith( "color" ):
		output = warsow.irc_color( " ".join( func.split()[1:] ) )
	elif func.startswith( "help" ):
		output = warsow.irc_color( "^0Use one of the following commands to interact with the bot: " + \
								"^1getname <ip>^0;^1 getip <name>^0;^1 getplayers <ip>^0;^1 getplayer <name>^0;^1 getinfo " + \
								"<server> <what>^0;^1 top3 <gametype>^0;^1 color <code>^0;^1 decolor <code>^0;^1 goto <channel> <pass>" + \
								"^0;^1 leave <channel> <pass>" )
	elif func.startswith( "goto" ):
		if func.split()[2] == passwd:
			s.send( "JOIN " + func.split()[1] + "\n\r" )
			output = "Joining " + func.split()[1]
		else:
			output = "Wrong password! (%s)" % func.split()[2]
	elif func.startswith( "leave" ):
		if func.split()[2] == passwd:
			s.send( "JOIN " + func.split()[1] + "\n\r" )
			output = "Leaving " + func.split()[1]
		else:
			output = "Wrong password! (%s)" % func.split()[2]
	else:
		output = "\x0301Invalid Command (%s), use help to get a list of all commands!" % ( func )
	if not len( str( output ) ) < 499: #the irc-protocol does not allow messages > 512 chars (including the send command and nickname)
		output = "\x0301Output was to long! (%d chars)" % len( str( output ) )
	s.send( "NOTICE %s :%s\n\r" % ( name, output ) )

def ping( 
		name #name of the user that requested the ping 
		):
	
	global mine #when set: main thread does not receive the messages
	a = time.time() #initial time
	s.send( "PRIVMSG %s :\001PING %d\001\n\r" % ( name, a ) ) #requests a CTCP ping
	mine, timeout = True, False
	s.settimeout( 2 ) #because pings can get lost =)
	try:
		while "PING %d" % a not in s.recv( 1024 ): pass
	except:
		timeout = True
	s.settimeout( None ) #important so other requests don't time out
	mine = False
	if timeout: s.send( "NOTICE %s :your ping timed out!\n\r" % ( name ) )
	else: s.send( "NOTICE %s :\x034you have a ping time of %0.3f seconds\n\r" % ( name , time.time() - a ) )

def parse_data( 
			data,
			buffer 
			):
	
	'''
	This is not a standalone function and should not be used from outside!
	It buffers the data the irc server sends and returns the newest event and the buffer
	'''
	
	data = buffer + data
	events = data.split( '\n' )
	buffer = events.pop()
	return( events, buffer )

def pingpong( 
			data 
			):
	
	'''
	This function handles the PING and PONG which the irc protocol uses
	It also handles printing the raw data and filtering important data
	'''
	
	global nick, channel, registered, join
	data = data.rstrip( '\r' )
	if "PING :" in data: #PING :Random_Stuff has to be replied with PONG :Same_Stuff
		s.send( "PONG :" + data.split( ":" )[1] + "\n\r" )
	else:
		print data #Makes it easier for debugging
		if "PRIVMSG" in data and ( nick in data and "ping" in data ) or "!ping" in data: #ping
			data = data.split( "!" )[0][1:]
			ping( data )
		elif "PRIVMSG" in data and ( nick in data or "!query" in data ): #others
			data = data.split( "!" )[0][1:], ":".join( data.lstrip( ":" ).split( ":" )[1:] )
			send( data[1], data[0] )
			
class RecvAll( threading.Thread ):
	
	'''
	This is the main thread that recieves all the messages from the irc except the pongs from other clients
	'''
	
	def run( 
		self 
		):
		
		global query
		query = warsow.Query() #imported from my in_the_work warsow-module =)
		query.start()
		s.connect( ( server, port ) ) #6667 is standard port
		connected = True
		registered = False
		buffer = ''
		while connected:
			if not mine: #this thread should not receive messages while a ping is running
				data = s.recv( 4096 ) #fix me: could be lower
				if data == '': connected = False
				else:
					if not registered:
						s.send( 'NICK %s\n\r' % ( nick ) )
						s.send( 'USER %s %s %s :%s\n\r' % ( user, host, host, real ) )
						registered = True
					events, buffer = parse_data( data, buffer ) #buffers the data and gives me the events
					for e in events: pingpong( e ) #handles the data

def connect():
	
	'''
	Starts the program
	'''
	
	global server, nick, user, host, real, port, passwd
	#----------Variables---------
	server = "irc.quakenet.org"
	port = 6667
	nick = "QueryBot"
	user = "QBot"
	host = "Orbital-PC"
	real = "Orbitals Warsow-Server Query Bot"
	passwd = "mypw"
	#----------------------------
	RecvAll().start()


if __name__ == "__main__":
	connect()

