from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore, time, sys
from login import *
from admin import *

##
# GLOBAL PARAMETERS - Change them to what you need.
##
PORT = 4000
LOGFILE = "mainlog" # Change to where the logfile should go.

##
# Nothing to edit below this, unless you know what you're doing.
##

# ANSI is turned off by default.
# I am not using the other ANSI possibilities on purpose. If you want to add
# backgrounds and blinking, you're on your own.
BLACK = ''
RED = ''
GREEN = ''
YELLOW = ''
BLUE = ''
MAGENTA = ''
CYAN = ''
WHITE = ''
RESET = ''
BOLD = ''

# Global dictionaries holding the player's ID, socket and IP address.
sessions = {}
ipsessions = {}

logger = open(LOGFILE, 'a')
def Log(line):
	"Just call Log('text') to log to a file."
	timer = ''
	timer = time.asctime()
	logger.write(line + " " + timer + "\n")
	logger.flush()

class Handler:
	"Command parser, where all player commands go."

	def unknown(self, session, cmd):
		session.push('Unknown command: %s\r\n' % cmd)

	def handle(self, session, line):
		self.checkbit = 0

		#Time of last command executed. Will be used for external timeout cleanup.
		cu.execute("update pnames set last_command = %s where p_id = %s", time.time(), session.p_id)

		if not line.strip(): session.push("> ")
		parts = line.split(' ', 1)
		cmd = parts[0]
		if cmd == '': 
			pass
		else:
			try: line = parts[1].strip()
			except IndexError: line = ''
			admincmd = AdminCmds(sessions, ipsessions)
			meth = getattr(self, 'do_'+cmd, None) # Normal commands
			methadm = getattr(admincmd, 'do_'+cmd, None) # Admin commands, admin.py

			for i in self.coders: # SECURITY CHECK
				if i == session.pname:
					self.checkbit = 1
				else: pass

			if (self.checkbit == 1) & (callable(methadm)): # Is a coder
				methadm(session, line)
				session.push("> ")
			elif callable(meth):
				# Is a player, do not let them use AdminCmds()
				meth(session, line)
				session.push("> ")
			else:
				self.unknown(session, cmd)
				session.push("> ")

	def RoomBroadcast(self, session, inroom, line):
		"By calling this method, 'line' is sent to all players in 'inroom'."
		cu.execute("select p_id from pnames where is_in = %s", inroom)
		self.local = cu.fetchall()
		self.pnamer = BOLD+WHITE + session.pname + RESET
		if line == '':
			pass
		else:
			for i in self.local:
				self.tmpses = sessions.get(i[0]) # sessions dict
				if self.tmpses == '': # Player is not logged in
					pass
				elif self.tmpses.p_id == session.p_id: # Is the player
					pass
				else:
					self.tmpses.push(self.pnamer + line+"\r\n> ")

	def do_look(self, session, line):
		#Check if looked-at player is there
		cu.execute("select p_id,description,is_in,names from pnames where names = %s and is_in = %s", line.lower(), session.is_in)
		self.peeps = cu.fetchall()
		#Get list of items possessed by player.
		cu.execute("select instances.parent_id,instances.is_owned,instances.is_in,objects.obj_id,objects.name,objects.description\
					from instances,objects where instances.parent_id = objects.obj_id\
					and instances.is_owned = %s and objects.name = %s", session.p_id, line.lower())
		self.foo = cu.fetchall()
		#Get list of mobs in the room.
		cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
					npcs.npc_id,npcs.name,npcs.description,instances.id\
					from instances,npcs where instances.sub_id = 2 and instances.is_owned = 0 and instances.parent_id = npcs.npc_id\
					and instances.is_in = %s and npcs.name = %s", session.is_in, line.lower())
		self.mobonfloor = cu.fetchall()
		#Get list of objects in the room.
		cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
					objects.obj_id,objects.name,objects.description,objects.alias\
					from instances,objects where instances.sub_id = 1 and instances.is_owned = 0 and instances.parent_id = objects.obj_id\
					and instances.is_in = %s", session.is_in)
		self.obj = cu.fetchall()

		if line != '': # Not looking at the room
			try:
				if self.peeps != []: #Looking at a player
					self.descri = self.peeps[0][1].split('\\n')
					for i in self.descri:
						session.push(str(i) + "\r\n")

					session.push("Inventory:\r\n")
					cu.execute("select parent_id,is_owned from instances where is_owned = %s", self.peeps[0][0])
					self.invent = cu.fetchall()
					if self.invent != []:
						for i in self.invent:
							cu.execute("select obj_id,name from objects where obj_id = %s", i[0])
							self.has = cu.fetchall()
							session.push(str(self.has[0][1]) + "\r\n")
					else: session.push("Empty.\r\n")
					self.RoomBroadcast(session, session.is_in, " looks at " + line)
				elif self.foo != []: #An object in your inventory
					self.descri = self.foo[0][5].split('\\n')
					for i in self.descri:
						session.push(str(i) + "\r\n")

				elif self.mobonfloor != []: #A mob/NPC
					cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
								objects.obj_id,objects.name,instances.npc_own\
								from instances,objects where instances.sub_id = 1 and instances.is_owned = 0 and instances.parent_id = objects.obj_id\
								and instances.is_in = 0 and instances.npc_own = %s", self.mobonfloor[0][7])
					self.mobinv = cu.fetchall()

					self.descri = self.mobonfloor[0][6].split('\\n') # Print the description
					for i in self.descri:
						session.push(str(i) + "\r\n")

					if self.mobinv != []: # Print the mob's inventory
						self.stuff = []
						self.stufa = {}
						session.push(BOLD+WHITE+"Inventory:\r\n"+RESET)

						for i in self.mobinv:
							self.stuff.append(i[5])
							self.stufa[i[5]] = self.stuff.count(i[5])
						for i in self.stufa:
							if self.stufa[i] > 1:
								session.push(str(i) + " ("+str(self.stufa[i])+")" + "\r\n")
							else:
								session.push(str(i) + "\r\n")
					else: pass

				elif self.obj != []: #An item on the floor
					for i in self.obj:
						if i[5] == line.lower():
							self.descri = i[6].split('\\n')
							for y in self.descri: session.push(str(y) + "\r\n")
						else: # Check if an alias is defined.
							try:
								self.objalias = i[7].split(':')
								self.ind = self.objalias.count(line.lower())
								if self.ind > 0:
									self.descri = i[6].split('\\n')
									for y in self.descri: session.push(str(y) + "\r\n")
									self.action = 1
								else: session.push("You do not see that here.\r\n")
							except: pass

				else: session.push("You do not see that here.\r\n")

			except: session.push("ERROR - You do not see that here.\r\n")

		else: #No argument, look at room
			cu.execute("select short_desc,long_desc from rooms where r_id = %s", session.is_in)
			self.descr = cu.fetchone()
			session.push(CYAN+str(self.descr[0]) + RESET + "\r\n")
			self.descri = self.descr[1].split('\\n')
			for i in self.descri:
				session.push(str(i) + "\r\n")

			cu.execute("select names from pnames where is_in = %s", session.is_in)
			self.look_com = cu.fetchall()
			session.push("\r\n" + BOLD + RED)
			for i in self.look_com:
				session.push(str(i[0].capitalize()) + "\r\n")
			session.push(RESET + "\r\n")

			#Get list of mobs in the room.
			cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
						npcs.npc_id,npcs.name\
						from instances,npcs where instances.sub_id = 2 and instances.is_owned = 0 and instances.parent_id = npcs.npc_id\
						and instances.is_in = %s", session.is_in)
			self.mobonfloor = cu.fetchall()
			#Get list of objects in the room.
			cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
						objects.obj_id,objects.name\
						from instances,objects where instances.sub_id = 1 and instances.is_owned = 0 and instances.parent_id = objects.obj_id\
						and instances.is_in = %s", session.is_in)
			self.objonfloor = cu.fetchall()

			if self.mobonfloor == []: pass #If there's nobody don't print a newline
			else:
				for i in self.mobonfloor:
					session.push(BOLD+MAGENTA + str(i[5]) + " " + RESET)
				session.push("\r\n")

			if self.objonfloor == []: pass #If there's nothing don't print a newline
			else:
				self.flo = []
				self.fla = {}

				for i in self.objonfloor:
					self.flo.append(i[5])
					self.fla[i[5]] = self.flo.count(i[5])
				for i in self.fla:
					if self.fla[i] > 1:
						session.push(BOLD+GREEN+str(i)+RESET+" ("+str(self.fla[i])+")"+" ")
					else:
						session.push(BOLD+GREEN+str(i)+RESET+" ")
				session.push("\r\n")

			# List exits
			session.push(BOLD+CYAN + "Exits: ")
			cu.execute("select direction from links where room_id = %s", session.is_in)
			self.tmpgoto = cu.fetchall()
			for i in self.tmpgoto:
				session.push(i[0] + " ")
			session.push(RESET + "\r\n")

	def do_drop(self, session, line):
		try:
			cu.execute("select instances.id,instances.is_owned,objects.obj_id,objects.name\
						from instances,objects where instances.parent_id = objects.obj_id\
						and instances.is_owned = %s and objects.name = %s", session.p_id, line.lower())
			self.drop = cu.fetchone()
			cu.execute("update instances set is_owned = 0 where id = %s", self.drop[0])
			cu.execute("update instances set is_in = %s where id = %s", session.is_in, self.drop[0])
			session.push(str(self.drop[3])+" dropped.\r\n")
		except: session.push("You cannot drop this.\r\n")

	def do_go(self, session, line):
		if not line: session.push("You don't go anywhere.\r\n")
		else:
			try:
				cu.execute("select direction, exit from links where room_id = %s and direction = %s", session.is_in, line)
				self.isexit = cu.fetchone()
			except:
				session.push("No exit this way.\r\n")

			try:
				cu.execute("select tor from exits where exit_id = %s", self.isexit[1])
				self.goto = cu.fetchone()

				# Actually move
				self.movemsg = " leaves " + str(self.isexit[0]) + "."
				self.RoomBroadcast(session, session.is_in, self.movemsg)
				cu.execute("update pnames set is_in = %s where p_id = %s", self.goto[0], session.p_id)
				session.is_in = self.goto[0]
				self.RoomBroadcast(session, session.is_in, " enters the room.")
				self.do_look(session, '') # Force a look, to be replaced by something nicer.

			except:
				session.push("You can't move that way.\r\n")

	def do_say(self, session, line):
		self.says = " says: " + line
		session.push("You say: " + line + "\r\n")
		self.RoomBroadcast(session, session.is_in, self.says)

	def do_chat(self, session, line):
		cu.execute("select p_id from pnames where is_in > 0")
		self.everybody = cu.fetchall()
		self.talker = BOLD+WHITE + session.pname + RESET
		for i in self.everybody:
			self.listen = sessions.get(i[0])
			if self.listen == None:
				pass
			else:
				self.listen.push(BOLD+GREEN+"["+RESET+ self.talker +BOLD+GREEN+"] "+RESET + line + "\r\n")

	def do_logout(self, session, line):
		raise EndSession

	def do_who(self, session, line):
		session.push("The following players are logged on:\r\n")
		cu.execute("select names from pnames where is_in > 0")
		self.whoin = cu.fetchall()
		for i in self.whoin:
			session.push(BOLD + RED + str(i[0].capitalize() + RESET) + "\r\n")
		session.push("\r\n")

	def do_help(self, session, line):
		if line == '': # help command alone
			cu.execute("select command from helps")
			self.allh = cu.fetchall()
			self.allhelp = []
			for i in self.allh:
				self.allhelp.append(i[0])

			self.allhelp.sort()
			self.counter = 0
			for i in self.allhelp:
				self.counter += 1

			#Make 2 columns of help files
			self.counter = self.counter / 2
			self.counter = int(self.counter)
			self.first = self.allhelp[:self.counter]
			self.second = self.allhelp[self.counter:]
			session.push("The following help topics exist:\r\n")
			for x, y in map(None, self.first, self.second):
				if x == None:
					x = ' '
					session.push(str(x).ljust(15) + str(y).ljust(10) + "\r\n")
				else: session.push(str(x).ljust(15) + str(y).ljust(10) + "\r\n")
				

		else: #If help <foo>
			try:
				cu.execute("select command, doc from helps where command = %s", line)
				self.helper = cu.fetchone()
				session.push("Help for " + line + ":\r\n")
				self.docu = self.helper[1].split('\\n')
				for i in self.docu:
					session.push(i + "\r\n")
				session.push("\r\n")
			except:
				session.push("Help is not available on this topic.\r\n")

	def do_description(self, session, line):
		cu.execute("update pnames set description = %s where p_id = %s", line, session.p_id)
		session.push("Description set.\r\n")

	def do_inv(self, session, line):
		cu.execute("select instances.id,instances.is_owned,objects.obj_id,objects.name\
					from instances,objects where instances.parent_id = objects.obj_id\
					and instances.is_owned = %s", session.p_id)
		self.owned = cu.fetchall()
		self.stuff = []
		self.stufa = {}
		session.push(BOLD + WHITE + "Inventory:\r\n" + RESET)

		for i in self.owned:
			self.stuff.append(i[3])
			self.stufa[i[3]] = self.stuff.count(i[3])

		for i in self.stufa:
			if self.stufa[i] > 1:
				session.push(BOLD+GREEN+str(i)+RESET+" ("+str(self.stufa[i])+")"+"\r\n")
			else:
				session.push(BOLD+GREEN+str(i)+RESET+"\r\n")

	def do_get(self, session, line):
		cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,instances.id,\
					objects.obj_id,objects.name,objects.description\
					from instances,objects where instances.sub_id = 1 and instances.is_owned = 0 and instances.parent_id = objects.obj_id\
					and instances.is_in = %s", session.is_in)
		self.objonfloor = cu.fetchall()
		self.iterfloor = iter(self.objonfloor)
		self.action = 0
		for i in self.iterfloor:
			if (i[6] == line.lower()) & (self.action == 0):
				session.push("You get "+line+".\r\n")
				cu.execute("update instances set is_in = 0 where sub_id = 1 and id = %s", i[4])
				cu.execute("update instances set is_owned = %s where sub_id = 1 and id = %s", session.p_id, i[4])
				self.action = 1
			elif self.action == 1:
				pass

		if self.action == 0:
			session.push("This is not here.\r\n")

	def do_emote(self, session, line):
		self.emote = " " + line
		self.RoomBroadcast(session, session.is_in, self.emote)
		session.push("Emote: " + session.pname + self.emote + "\r\n")

	def do_give(self, session, line):
		if line == '' : session.push("Correct syntax is:\r\ngive <item> to <player>\r\n")
		else:
			self.argu = line.lower()
			if not self.argu.strip(): session.push("> ")
			self.parts = line.split(' ', 3)

			if self.parts[1] == "to":
				try:
					cu.execute("select instances.id,instances.is_owned,objects.obj_id,objects.name\
								from instances,objects where instances.parent_id = objects.obj_id\
								and instances.is_owned = %s and objects.name = %s", session.p_id, self.parts[0].lower())
					self.itom = cu.fetchall() # Given item info
					cu.execute("select p_id, names, is_in from pnames where names = %s and is_in = %s", self.parts[2].lower(), session.is_in)
					self.transf = cu.fetchall() # Receiver's info
					# Check if given to a mob.
					cu.execute("select instances.parent_id,instances.sub_id,instances.is_owned,instances.is_in,\
								npcs.npc_id,npcs.name,instances.id\
								from instances,npcs where instances.sub_id = 2 and instances.is_owned = 0 and instances.parent_id = npcs.npc_id\
								and instances.is_in = %s and npcs.name = %s", session.is_in, self.parts[2])
					self.mob = cu.fetchall()

					if self.transf != []:
						cu.execute("update instances set is_owned = %s where id = %s", self.transf[0][0], self.itom[0][0])
						session.push("You give "+self.parts[0]+" to "+self.parts[2]+".\r\n")
						self.give = " gives " + self.parts[0] + " to " + self.parts[2]
						self.RoomBroadcast(session, session.is_in, self.give)

					elif self.mob != []:
						cu.execute("update instances set npc_own = %s where id = %s", self.mob[0][6], self.itom[0][0])
						cu.execute("update instances set is_owned = 0 where id = %s", self.itom[0][0])
						session.push("You give "+self.parts[0]+" to "+self.parts[2]+".\r\n")
						self.give = " gives " + self.parts[0] + " to " + self.parts[2]
						self.RoomBroadcast(session, session.is_in, self.give)
					else: session.push("This somehow fails.\r\n")


				except: session.push("Correct syntax is:\r\ngive <item> to <player>\r\n")
	
			else: session.push("Correct syntax is:\r\ngive <item> to <player>\r\n")

	def do_stats(self, session, line):
		cu.execute("select names,str,dex,wis,p_id from pnames where p_id = %s", session.p_id)
		self.stats = cu.fetchone()
		session.push(BOLD+WHITE+"Stats for " + str(self.stats[0].capitalize()) +RESET+"\r\n")
		session.push("STR: " + str(self.stats[1]) + "\r\n")
		session.push("DEX: " + str(self.stats[2]) + "\r\n")
		session.push("WIS: " + str(self.stats[3]) + "\r\n")

	def do_setansi(self, session, arg):
		global BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET, BOLD
		if arg == "on":
			BLACK, RED, GREEN = '\033[30m', '\033[31m', '\033[32m'
			YELLOW, BLUE, MAGENTA = '\033[33m', '\033[34m', '\033[35m'
			CYAN, WHITE = '\033[36m', '\033[37m'
			RESET, BOLD = '\033[0;0m', '\033[1m'
			cu.execute("update pnames set colors = %s where p_id = %s", arg, session.p_id)
		elif arg == "off":
			BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET, BOLD = '','','','','','','','','',''
			cu.execute("update pnames set colors = %s where p_id = %s", arg, session.p_id)
		else: session.push("Syntax:\r\nsetansi [off|on]\r\n")

	# Shortcuts
	def do_n(self, session, line): self.do_go(session, 'north')
	def do_s(self, session, line): self.do_go(session, 'south')
	def do_w(self, session, line): self.do_go(session, 'west')
	def do_e(self, session, line): self.do_go(session, 'east')
	def do_u(self, session, line): self.do_go(session, 'up')
	def do_d(self, session, line): self.do_go(session, 'down')
	def do_se(self, session, line): self.do_go(session, 'southeast')
	def do_sw(self, session, line): self.do_go(session, 'southwest')
	def do_nw(self, session, line): self.do_go(session, 'northwest')
	def do_ne(self, session, line): self.do_go(session, 'northeast')

	do_north = do_n
	do_south = do_s
	do_west = do_w
	do_east = do_e
	do_up = do_u
	do_down = do_d
	do_quit = do_logout
	do_exit = do_logout
	do_i = do_inv
	do_l = do_look


class EndSession(Exception): pass

class EnterGame(Handler):
	def __init__(self, session):
		self.session = session
		self.admin = []
		self.coders = []
		
		# Initialize the permissions from the "arch" and "coders" files
		# Arch file, uncomment if needed. For the sysadmin.
		#self.arch = open('arch', 'r')
		#self.iarch = iter(self.arch)
		#for line in self.iarch:
		#	self.admin.append(line.strip('\n'))
		#self.arch.close()

		#Coders file, for in-game creators/wizards/builders
		self.coder = open('coders', 'r')
		self.icoder = iter(self.coder)
		for line in self.icoder:
			self.coders.append(line.strip('\n'))
		self.coder.close()

	def enter(self, room):
		self.room = room
		room.add(self, self.session)

	def found_terminator(self):
		line = (''.join(self.data))
		self.data = []
		if line == '': session.push("> ")
		else:
			try: self.handle(self, line)
			except: raise EndSession

	def add(self, session):
		self.ival = ipsessions.items()
		for i in self.ival:
			if session.ipaddr == "127.0.0.1":
				pass
			elif session.ipaddr == i[1]:
				session.push("You are already connected!\r\n\r\n")
				raise EndSession
			else: pass

		cu.execute("update pnames set is_in = 1 where p_id = %s", session.p_id)
		sessions[session.p_id] = session
		ipsessions[session.p_id] = session.ipaddr
		cu.execute("update pnames set ip_addr = %s where p_id = %s", session.ipaddr, session.p_id)
		#Store the player's starting location in the socket object.
		session.is_in = 1

		cu.execute("select p_id from pnames where is_in > 0")
		self.locala = cu.fetchall()
		cu.execute("select names from pnames where p_id = %s", session.p_id)
		self.pnamera = cu.fetchall()

		# Initialize ANSI colors
		cu.execute("select colors from pnames where p_id = %s", session.p_id)
		self.colorize = cu.fetchone()
		self.colorize = self.colorize[0].strip()
		if self.colorize == "on":
			self.do_setansi(session, "on")
		else: self.do_setansi(session, "off")

		self.pnamera = BOLD+WHITE + self.pnamera[0][0].capitalize() + RESET
		for i in self.locala:
			self.tmpsess = sessions.get(i[0])
			if self.tmpsess == None:
				pass
			else:
				self.tmpsess.push(self.pnamera + " enters the game.\r\n")
				self.tmpsess.push("\r\n> ")
		print self.pnamera + " logged in."
		Log(session.pname + " logged in from " + session.ipaddr)
		self.do_look(session, '') # To be replaced with something nicer.
		session.push("> ")

class SecondServSock(async_chat):
	#The chat server
	def __init__(self, server, sock, addr):
		async_chat.__init__(self, sock)
		self.server = server
		self.ipaddr = addr
		self.set_terminator("\n")
		self.name = None
		self.data = []
		self.sock = sock
		self.enter(FirstLogin(server, self.ipaddr)) #Call the login procedure before anything.
	def collect_incoming_data(self, data):
		self.data.append(data)
	def found_terminator(self):
		line = (''.join(self.data))
		line = line.strip('\r')
		self.data = []
		try: self.room.handle(self, line)
		except EndSession:
			self.handle_close()

	def handle_close(self):
		self.ival = ipsessions.items()
		for i in self.ival:
			self.sockobj = sessions[i[0]]
			self.test = str(self.sockobj)
			self.tesb = str(self.sock)
			try:
				if (self.ipaddr == i[1]) & (self.test == self.tesb):
					cu.execute("select names from pnames where p_id = %s", i[0])
					self.leaver = cu.fetchone()
					#In any case, if the player exits, remove him.
					cu.execute("update pnames set is_in = 0 where p_id = %s", i[0])
					cu.execute("update pnames set ip_addr = NULL where p_id = %s", i[0])
					Log("Disconnected from " + self.ipaddr)

					sessions[i[0]] = ''
					ipsessions[i[0]] = ''
	
					cu.execute("select p_id from pnames where is_in > 0")
					self.locali = cu.fetchall()
					self.leaver = BOLD+WHITE + self.leaver[0].capitalize() + RESET
					print self.leaver + " logged out."
					for i in self.locali:
						self.tmpsec = sessions[i[0]]
						if self.tmpsec == None:
							pass
						else:
							self.tmpsec.push(self.leaver + " leaves the game.\r\n")
							self.tmpsec.push("\r\n> ")
	
					async_chat.handle_close(self)
				else: pass
			except: pass

	def handle_error(self):
		self.handle_close()

	def enter(self, room):
		self.room = room
		room.add(self)


class MainServSock(dispatcher):
	# The Server
	def __init__(self, port):
		dispatcher.__init__(self)
		self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
		self.set_reuse_addr()
		self.bind(('', port))
		self.listen(10)
		self.enterg = EnterGame(self)
	def handle_accept(self):
		conn, addr = self.accept()
		SecondServSock(self, conn, addr)

if __name__ == '__main__':
	s = MainServSock(PORT)
	# Everybody at room #0 when we start, obvious.
	cu.execute("update pnames set is_in = 0")
	try: asyncore.loop()
	except KeyboardInterrupt: print