#!/usr/bin/python
## Test server, www.fams.de   --port 8192   
"""
GPL licensing

(C) Copyright, 2007 Yosi Izaq.

This file, py_eye.py, is part of PyEye programm.

PyEye is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PyEye is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyEye.  If not, see <http://www.gnu.org/licenses/>.
"""

"""

	Description. a smallish utility that works like God's eye for dom-II - it determines the turn
	status without the player having to actually activate dom-III and connect to the server.
	Its much faster and prevents accidently erasing the player's turn when connecting from multiple computers.

	The utility is distributed as an exe file.

	Author. Yosi Izaq (aka WraithLord), izaqyos@gmail.com 
"""

"""
Two classes, first is a dominoins client, intialized with the address and port
that connects to the server and retreives the turn status.
The second class reads the configuration (list of tracked games, together with
adress, port and nation name) and, through CLI (uses CMD module),
asks the user which game he wants to query.

ToDo:

1. Parse the response. [done]
	Add four dictionaries as follows:
	early_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
	mid_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
	late_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
	eras, 0:early_nations, 1:mid_nations, 2:late_nations

	Use Edi's DB for this
	
	Add flag icons for all nations

2. Add configuration. (new class, CFG reader) [done]

3. Add CLI. (new class, CMD interface) [done]

3.1 Tie it all together. [done]

3.1.1 set debug to 1 and see that only important data is displayed. [done]

3.2 Test for windows, compile, test and release.

4. Note that:[done]
	Era is 1-3 (1=early)

	P=80, the response contains three blocks of 80
	chars for player control, status and connected

	Dom-III patch 3.14 increased P to 95

	values for nation status (0- not played, 1- yes played) and
	the possible values for connected (0- not connected, 1- yes
	connected).

5. Switch debug prints to use logging module.
	finish converting all dbg_lvl ifs to pyEyeLogger.debug

6. Make nation dictionaries configurable, to easil

7. Make a parse response function that can be used by GUI module. 
Add to GUI module icons for nations and status.

"""

import ConfigParser, os, cmd, glob, re, logging, tarfile
from struct import *
from socket import *

##user imports
#import py_eye_gui
#from py_eye_gui import *

# ToDo, replace with logging module...
dbg_lvl = 1

FORMAT = "%(asctime)-15s %(levelname)s %(message)s"

#For normal prints
logging.basicConfig(format=FORMAT)
#For debug info
#logging.basicConfig(level=logging.DEBUG, format=FORMAT)

pyEyeLogger = logging.getLogger("PyEyeLogger")

class DomIIIUtils:
	"""
	This class defines various utility functions for dominions III like verify current turn, backup turn etc
	"""
	
	m_cfg = None

	def __init__(self, cfg):
		self.m_cfg = cfg

	def save_turn_for_client(self, game):

		pyEyeLogger.debug("Commencing turns backup...\nDetermine turn # and missing or invalid turns...")

		turn, missing_set = self.verify_turn_as_client(game)

		if turn == None:
			pyEyeLogger.warning("Turn information could not be extracted, as a result saving the turn will fail!")
			return None

		backup_dir = os.path.join(self.m_cfg.m_settings['backup_path'], game, turn)
		pyEyeLogger.debug("saving turn %s for game %s to: %s"%(turn, game, backup_dir))

		if not os.path.exists(backup_dir):
		    os.makedirs(backup_dir)

		pyEyeLogger.debug("Creating bzw tarball of turns...")

		destination = os.path.join(backup_dir, game+'_'+turn+'.tar.bz2')
		foldertobackup = os.path.join(os.path.dirname(self.m_cfg.m_settings['dom_path']),"savedgames", game)

		out = tarfile.TarFile.open(destination, 'w:bz2')
		out.add(foldertobackup, arcname=os.path.basename(foldertobackup))
		out.close()

		return turn #turn can be used for diagnstic messages by potential callers

	def verify_turn_as_client(self, game): #Verify the turn as client is very different from verifying as server
		# as the server has access to the ftherlnd file which gives much information.
		"""
		Method that uses Illwinters --verify switch.
		1. get turn# from ftherlnd.chk file.
		2. Compile a list of nations that don't have the current turn or are not in OK status.
		3. Print that list and return two things, the turn number and the list of nations with missing turns.

		Perform this for the given game 
		"""

		pyEyeLogger.debug("Verifying turns and extracting current turn number.")
		
		nation_turn = None
		dom_path= os.path.dirname(self.m_cfg.m_settings['dom_path']) #Extract dom III dir from dom III exe path
		pyEyeLogger.debug("Dom III path: %s"%dom_path)
		cur_dir = os.getcwd()
		os.chdir(dom_path)

		cmd1 = ' dom3.exe'+' '+game+' --verify'
		#cmd1 = os.path.join('.', cmd1)
		##cmd1 = cmd1.replace(' ', '\ ')
		pyEyeLogger.debug("Executing verify command: %s from dir %s"%(cmd1, os.getcwd()))
		os.system(cmd1)
		
		pat1 = '\sturnnbr\s(\d+)'

#		#Following code is good only for server:
#		#get current turn
#		fthr_path = os.path.join(dom_path, "savedgames", game,'ftherlnd.chk')
#		pyEyeLogger.debug("fatherland path: %s"%(fthr_path))
#
#		fthr_file = open(fthr_path, "rb")
#		pat1 = '\sturnnbr\s(\d+)'
##		p1 = re.compile(pat1)
#		for line in fthr_file:
#			m = re.search(pat1, line)
#			if (m):
#			    turn = m.group(1)
#		fthr_file.close()
#		pyEyeLogger.info("Turn is: %s"%(turn))

		#Check for invalid turns or missing turns.
		nations_missing_turns = []
		try:
			os.chdir(os.path.join(dom_path,"savedgames", game))
		except OSError:
			return None, None
		chk_files = glob.glob('*.chk');

#		#Only relevant for server:
#		chk_files.remove('ftherlnd.chk')
		for file in chk_files:
			nation = file.split('.').pop(0)
			chk_file = open(file, "rb")
			first_line = 1
			missing = 0
			for line in chk_file:
# check that turn is valid
				if ((first_line == 1) and (not (line.startswith('2h file is OK!')))):
					missing = 1
				first_line = 0

# check that turn is current				
				m = re.search(pat1, line)
				if (m):
					nation_turn = m.group(1)
#					#Only relevant for server:
#					if nation_turn != turn:
#						missing = 1
				
				if missing:
				# so we know this nation is missing a turn 
				# but is it at all active??? only if not shall it
				# be reported as missing
					if (self.games[self.active_game].players.has_key(nation)): 
						nations_missing_turns.append(nation)
					missing = 0
						
			chk_file.close()

		#remove chk files
		chk_files = glob.glob('*.chk');
		for file in chk_files:
			os.remove(file)
		
		if nation_turn == None:
			pyEyeLogger.warning("Turn verification process failed!")

		else:
			nations_missing_turns_set = set(nations_missing_turns)
			pyEyeLogger.info("Turn is: %s, nations missing turns are: %s"%(nation_turn, nations_missing_turns_set))

		ret = [nation_turn, nations_missing_turns_set]
		os.chdir(cur_dir)
		return ret

class pyEyeSettingsCfg:
	m_dom_path		= ''
	m_dom_flags		= ''
	m_backup_path		= ''
	m_auto_save_turns	= ''

	def __init__(self, dom_path, dom_flags, backup_path, auto_save_turns='off'):
		self.m_dom_path		= dom_path
		self.m_dom_flags	= dom_flags
		self.m_backup_path	= backup_path
		self.m_auto_save_turns	= auto_save_turns
		pyEyeLogger.debug("Settings configuration: dominions III exe path: %s, runtime flags: %s, backup path: %s, auto save turns is: %s."%(self.m_dom_path, self.m_dom_flags, self.m_backup_path, self.m_auto_save_turns)) 

	def __str__(self):
		return ("Settings configuration: dominions III exe path: %s, runtime flags: %s, backup path: %s, auto save turns is: %s."%(self.m_dom_path, self.m_dom_flags, self.m_backup_path, self.m_auto_save_turns))

class pyEyeGameCfg:
	m_host		= None
	m_age		= None
	m_port		= None
	m_nation	= None

	def __init__(self, host, port, age, nation = 'unknown'):
		self.m_host	= host
		self.m_port	= port
		self.m_age	= age
		self.m_nation	= nation
		pyEyeLogger.debug("Game configuration: host,%s, port, %s, age, %s, nation %s."%(self.m_host, self.m_port, self.m_age, self.m_nation)) 

	def __str__(self):
		return ("Game configuration: host,%s, port, %s, age, %s, nation %s."%(self.m_host, self.m_port, self.m_age, self.m_nation))
		

class pyEyeNationCfg:
	m_name	= ''	#Also used as the key in nations dictionary
	m_age	= ''	# either of {early|mid|late}
	m_desc	= 0	#Thematic description of the nation, not really used currently
	m_icon	= ''	#location of icon file, used by GUI
	m_id	= 0	# ID of nation, important for parsing respnse from dom server

	m_controled_opt	= ('not playing', 'human', 'AI')
	m_status_opt	= ('not played', 'played')
	m_con_opt	= ('not connected', 'connected')

	m_controled	= ""
	m_status	= ""
	m_con		= ""

	def __init__(self, name, age, desc, icon, id):
		self.m_id	=	id
		self.m_icon	=	icon
		self.m_desc	=	desc
		self.m_age	=	age
		self.m_name	=	name
		pyEyeLogger.debug("Nation configuration: name,%s; id, %s; age, %s; desc, %s; icon, %s."%(self.m_name, self.m_id, self.m_age, self.m_desc, self.m_icon)) 

		self.m_controled	= self.m_controled_opt[0]
		self.m_status		= self.m_status_opt[0]
		self.m_con		= self.m_con_opt[0]

	def set_nation_status(self, controlled, status, con):
		pyEyeLogger.debug("Received nation %s status as: controlled %d, status %d, connected %d."%(self.m_name, controlled, status, con)) 
		self.m_controled	= self.m_controled_opt[controlled]
		self.m_status		= self.m_status_opt[status]
		self.m_con		= self.m_con_opt[con]
		pyEyeLogger.debug("Setting nations %s status to: controlled %s, status %s, connected %s."%(self.m_name, self.m_controled, self.m_status, self.m_con)) 


	def __str__(self):
		return ("Nation configuration: [name- %s, id- %s; age- %s; desc- %s; icon, %s].\nNation Status: [controled %s, status %s, connected %s]."%(self.m_name, self.m_id, self.m_age, self.m_desc, self.m_icon, self.m_controled, self.m_status, self.m_con))

class pyEyeCfg:
	m_games = dict()
	m_cfg_file = os.path.join(os.getcwd(), r'./data/.py_eye_cfg')
	m_config = ConfigParser.ConfigParser()

	m_nations_cfg_file = os.path.join(os.getcwd(), r'./data/.py_eye_nations_cfg')
	m_nations_config = ConfigParser.ConfigParser()

	m_early_nations_dict ={}
	m_mid_nations_dict ={}
	m_late_nations_dict ={}

	m_settings_cfg_file = os.path.join(os.getcwd(), r'./data/.py_eye_settings_cfg')
	m_settings_config = ConfigParser.ConfigParser()

	m_settings = {} #list

	def update_cfg(self):
		pyEyeLogger.debug("Updating configuration")
		self.m_config.readfp(open(self.m_cfg_file))
		self.m_nations_config.readfp(open(self.m_nations_cfg_file))
		self.m_settings_config.readfp(open(self.m_settings_cfg_file))

		self.parse_games_cfg()
		self.parse_nations_cfg()
		self.parse_settings_cfg()

	def update_settings_cfg(self, path='', flags='', backup_path='', auto_save_turns=''):
		"""
		This method updates the settings configuration
		"""

		if (path == ''):
			path = self.m_settings['dom_path']

		if (flags == ''):
			flags = self.m_settings['dom_flags']

		if (backup_path == ''):
			backup_path = self.m_settings['backup_path']

		if (auto_save_turns == ''):
			auto_save_turns = self.m_settings['auto_save_turns']

		pyEyeLogger.debug("Updating the settings configuration to: path-%s, flags-%s, backup_path %s, auto save turns is: %s"%(path, flags, backup_path, auto_save_turns))

		#Update settings configuration file
		filelines = file(self.m_settings_cfg_file).readlines()
		index = 0
		for line in filelines:
			pyEyeLogger.debug("Processing index %s, line: %s,"%( index, line)) 
			#Found dom_path line
			if (line.find('dom_path') != -1):
				line = r'dom_path : '+path+'\n'
				pyEyeLogger.debug("Setting dom path to: %s"%( line)) 
				filelines[index] = line

			#Found dom_flags line
			if (line.find('dom_flags') != -1):
				line = r'dom_flags : '+flags+'\n'
				pyEyeLogger.debug("Setting dom flags to: %s"%( line)) 
				filelines[index] = line

			#Found backup_path line
			if (line.find('backup_path') != -1):
				line = r'backup_path : '+backup_path+'\n'
				pyEyeLogger.debug("Setting dom backup_path to: %s"%( line)) 
				filelines[index] = line

			#Found auto_save_turns line
			if (line.find('auto_save_turns') != -1):
				line = r'auto_save_turns : '+auto_save_turns+'\n'
				pyEyeLogger.debug("Setting dom auto_save_turns to: %s"%( line)) 
				filelines[index] = line

			index = index +1

		pyEyeLogger.debug("Writing updated settings configuration file")
		file(self.m_settings_cfg_file ,"w").writelines(filelines)

		#Reload the configuration
		self.m_settings_config.readfp(open(self.m_settings_cfg_file))
		self.parse_settings_cfg()

	def parse_settings_cfg(self):
		"""
		This method loads the settings configuration
		"""

		pyEyeLogger.debug("Parsing settings configuration")
		for section in self.m_settings_config.sections():
			pyEyeLogger.info( "section found: %s"% section)
			pyEyeLogger.info( "section items: %s"% self.m_settings_config.items(section))

			dom_path	= ' '
			dom_flags	= ' '
			backup_path	= ' '
			auto_save_turns	= ' '

			for item, val in self.m_settings_config.items(section):
				if item == 'dom_path':
					dom_path = val
				elif item == 'dom_flags':
					dom_flags = val
				elif item == 'backup_path':
					backup_path = val
				elif item == 'auto_save_turns':
					auto_save_turns = val
				else:
					pyEyeLogger.error( "Illegal configuration parameter %s, value %s."%(item, val))
			
			self.m_settings['dom_path'] = dom_path
			self.m_settings['dom_flags'] = dom_flags
			self.m_settings['backup_path'] = backup_path
			self.m_settings['auto_save_turns'] = auto_save_turns
			
		pyEyeLogger.debug( "Settings dictionary: (dominions III exe path: %s, dominions III exe flags: %s, turns backup path: %s), auto save turns is: %s"%(self.m_settings['dom_path'], self.m_settings['dom_flags'], self.m_settings['backup_path'], self.m_settings['auto_save_turns']  ) )

	def parse_games_cfg(self):
		"""
		This method loads the games configuration
		"""
		for section in self.m_config.sections():
			pyEyeLogger.info( "section found: %s"% section)
			pyEyeLogger.info( "section items: %s"% self.m_config.items(section))

			host = ' '
			port = 0
			age  = ' '
			nation = 'unknown'
			for item, val in self.m_config.items(section):
				if item == 'host':
					host = val
				elif item == 'port':
					port = val
				elif item == 'age':
					age = val
				elif item == 'nation':
					nation = val
				else:
					pyEyeLogger.error( "Illegal configuration parameter %s, value %s."%(item, val))
			
			game = pyEyeGameCfg(host, port, age, nation)
			self.m_games[section] = game
			pyEyeLogger.debug( "Add new entry to games dictionary: (%s : %s, %s, %s, %s)"%(section, self.m_games[section].m_host,self.m_games[section].m_port, self.m_games[section].m_age, self.m_games[section].m_nation    ) )
			
		pyEyeLogger.debug( "Games: %s"% self.m_games)



	def parse_nations_cfg(self):
		"""
		This method loads the nations configuration
		"""

		for section in self.m_nations_config.sections():
			pyEyeLogger.info( "section found: %s"% section)
			pyEyeLogger.info( "section items: %s"% self.m_nations_config.items(section))

			name = section
			id = 0
			age  = ' '
			desc  = ' '
			icon  = ' '
			for item, val in self.m_nations_config.items(section):
				if item == 'id':
					id = val
				elif item == 'age':
					age = val
				elif item == 'description':
					desc = val
				elif item == 'icon':
					icon = val
				else:
					pyEyeLogger.error( "Illegal configuration parameter %s, value %s."%(item, val))
			
			nation = pyEyeNationCfg(name, age, desc, icon, id)

			if (age == 'early'):
				self.m_early_nations_dict[id] = nation
				pyEyeLogger.debug( "Add new entry to %s nations dictionary: %s"%(nation.m_age, self.m_early_nations_dict[id]) )
			elif (age == 'mid'):
				self.m_mid_nations_dict[id] = nation
				pyEyeLogger.debug( "Add new entry to %s nations dictionary: %s"%(nation.m_age, self.m_mid_nations_dict[id]) )
			elif (age == 'late'):
				self.m_late_nations_dict[id] = nation
				pyEyeLogger.debug( "Add new entry to %s nations dictionary: %s"%(nation.m_age, self.m_late_nations_dict[id]) )
			else:
				pyEyeLogger.info( "Nation with no age, probably reserved %s"% nation)

			#pyEyeLogger.debug( "Add new entry to %s nations dictionary: (%s : %s, %s, %s)"%(nation.m_age, self.m_early_nations_dict[name].m_id,  self.m_early_nations_dict[name].m_name, self.m_early_nations_dict[name].m_desc, self.m_early_nations_dict[name].m_icon) )
			
		pyEyeLogger.debug( "Early nations dictionary: %s"% self.m_early_nations_dict)
		pyEyeLogger.debug( "Mid nations dictionary: %s"% self.m_mid_nations_dict)
		pyEyeLogger.debug( "Late nations dictionary: %s"% self.m_late_nations_dict)

	def __init__(self):
		self.update_cfg()


	def readSection(self, section): 
		return self.m_config.items(section)

	def addSection(self, section_values):
		"""
		section_values is a list of pairs that describe the section name (game name), host, port and age

		ToDo, use ConfigParser to add new section to configuration. Also add edit and remove functions
		 Config parser supports setting values to options inside sections but it 
		 doesn't support adding new sections, so section is added manually to end of file and then
		confif parser is used to set the options
		"""
		pyEyeLogger.debug(" Adding section values: %s"%(section_values))
		file = open(self.m_cfg_file, 'r+')
		file.seek(0,2) #go to end of file
		file.write("\n") 
		for option, value in section_values: 
			if (option == "section name"):
				section = value
				pyEyeLogger.debug(" Adding section: %s"%(section))
				file.write("["+value+"]\n") 

			else:
				pyEyeLogger.debug(" Adding option %s, value %s, to section: %s"%(option, value, section))
				#self.m_config.set(section, option, value)  #doesn't seem to work well inside this loop, since there's a race condition between adding the sections and adding its values w/o raising section don't exist exception so I chose the easy way.

				if option == "nation": #check for empty nation
					if (len(value)) == 0:
						value = "No Nation"
				file.write(option+" = "+value+"\n") 
		
		file.close

	def setSectionValue(self, section, option, value): 
		return self.m_config.set(section, option, value)

	def deleteSections(self, sections):
		pyEyeLogger.info("Removing game sections: %s"%(sections)) 

		for section in sections:
			pyEyeLogger.debug("Removing game section: %s"%(section)) 
			self.m_config.remove_section(section)

		in_section = False
		section_index = 0
		section_size = 5 #Note: Change this if section size is modified!
		index = 0
		remove_range = []
		filelines = file(self.m_cfg_file).readlines()
		for line in filelines:
			#pyEyeLogger.debug("Processing index %d, line: %s,"%(index, line)) 
			try:
				#Found section to remove
				sections.index( line[line.find('[')+1:line.find(']')])
				remove_range.append((index, index+section_size))#Add to remove list
				#in_section = True
			except ValueError:
				pass

			index = index +1

#			if (in_section):
#				pyEyeLogger.debug("Found game section to remove: %s"%(line))
#				section_index = section_index +1
#				if (section_index >= section_size):
#					in_section = False
#			
#			else:
#			#Write down all the rest of the sections
#				out.write(line)

		offset = 0
		for i,j in remove_range:
			pyEyeLogger.debug("removing lines %d to %d, offset %d"%(i, j, offset)) 
			filelines = filelines[:i-offset] + filelines[j-offset:]
			offset = offset + (j-i)

		pyEyeLogger.debug("Writing updated configuration file")
		file(self.m_cfg_file,"w").writelines(filelines)

class Dom3Client:
	""" TCP client for Dominions 3 game server."""
	m_address	= ' '	
	m_port		= 0
	m_bufsiz	= 0

	#protocol fields
	m_magic1 = chr(int('1100110', 2))
	m_magic2 = chr(int('1001000', 2))
	m_stat_update = chr(int('11', 2))

	def __init__(self, address, port, bufsiz):
		pyEyeLogger.info( "initializing Dom3Client.")

		self.m_address		= address
		self.m_port			= port 
		self.m_bufsiz		= bufsiz

		pyEyeLogger.debug( "Dom3Client: address, %s, port, %s, buffer size, %s. "%( self.m_address, self.m_port, self.m_bufsiz))
	
	
	def send_data(self, data):
		"""Send buffer to dom-III server"""
		tcpCliSock = socket(AF_INET, SOCK_STREAM)
		ADDR = (self.m_address, int(self.m_port))

		pyEyeLogger.info( "Connecting to: %s"% str(ADDR))
		try:
			tcpCliSock.connect(ADDR)
		except:
			pyEyeLogger.debug( "Connection refused")
			return 0

		#Loop is the old time convention for client, though in this case we only want one attempt at sending data...
		while 1:
			if not data:
				print "no data to send."
				break

			pyEyeLogger.debug( "Sending data: %s"% data)
			tcpCliSock.send("".join(data))

			recv_data = []
			recv_data = tcpCliSock.recv(1024)
			pyEyeLogger.debug( "Receiving data, length: %s, content, %s"% (len(recv_data), list(recv_data)))
			if (not data) or (len(recv_data) < 16): #Sanity check
				recv_data = 0 #This is the convention for error response
				pyEyeLogger.debug( "Empty or invalid data received")
				#break #break is in comment since anyway the loop is for client aesthitcs only and wil run only one time anyway

			pyEyeLogger.debug( "Returning server response: %s"%(str(recv_data)))
			return recv_data


			pyEyeLogger.debug( "Closing connection")

			tcpCliSock.close()
			break;
	
	def bin(self, a):
		s=''
        	t={'0':'000','1':'001','2':'010','3':'011',
        	   '4':'100','5':'101','6':'110','7':'111'}
        	for c in oct(a)[1:]:
        	        s+=t[c]
        	return s

	def int2binary(self, a):
		s="%X"%a
		ret=''
        	t={'0':'0000','1':'0001','2':'0010','3':'0011',
        	   '4':'0100','5':'0101','6':'0110','7':'0111',
        	   '8':'1000','9':'1001','A':'1010','B':'1011',
        	   'C':'1100','D':'1101','E':'1110','F':'1111',
		   }
        	for c in s[0:]:
	#	        print" c: ",c, " adding: ", t[c]
        	        ret+=t[c]
        	return ret
		 
		 
	def Denary2Binary(self, n):
		'''convert denary integer n to binary string bStr'''
		bStr = ''
    		if n < 0:  raise ValueError, "must be a positive integer"
    		if n == 0: return '0'
    		while n > 0:
    		    bStr = str(n % 2) + bStr
    		    n = n >> 1
    		return bStr
 
	def int2bin(self, n, count=24):
	    """returns the binary of integer n, using count number of digits"""
	    return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
	

	def build_player_query(self):
		"""Build dom-III status query packet"""
		pckt = []
		data_length = 1 #1 char, update req.

		pyEyeLogger.debug( "adding magic1 to packet: %s %s"%(ord(self.m_magic1) , self.int2binary(ord(self.m_magic1))) )
		pckt.append(self.m_magic1)
		
		pyEyeLogger.debug( "adding magic2 to packet: %s %s"%(ord(self.m_magic2) , self.int2binary(ord(self.m_magic2))) )
		pckt.append(self.m_magic2)

		#data length as int
		pyEyeLogger.debug( "adding length to packet: 60 %s%s"%( self.int2binary(6), self.int2binary(0)) )
		pckt.append(chr(1))
		pckt.append(chr(0))
		pckt.append(chr(0))
		pckt.append(chr(0))
		#min. length is 6, add nation field length

		#query data
		
		#1 char, player announcement, followed by P=80 (# of nations in dom) chars (95 since dom-III patch 3.14)
		# with value = 0/1 - not playing/playing nation
		# It seems like it can be skipped for query
		# pckt.append(chr(int('1', 2)))
		
		#string, player nation
		
		# request status update
		pyEyeLogger.debug( "adding request for status update to packet: %s %s"% (ord(self.m_stat_update) , self.int2binary(ord(self.m_stat_update))) )
		pckt.append(self.m_stat_update)
	
		return pckt


	def parse_query_response_gui(self, resp, cfg):
		"""
		Parse the response from server and send the results back in a format that GUI can understand and display
		Expects a raw dominions III response buffer and pyEyeCfg instance
		"""

		if (resp == 0):#only happens when dominions server wasn't reachable
			pyEyeLogger.info( "Dominions server is down")
			return 0, 0, 0


		pyEyeLogger.debug( "Parsing response for GUI: %s"% list(resp))

		nations_dict_list = [cfg.m_early_nations_dict, cfg.m_mid_nations_dict, cfg.m_late_nations_dict]
		#ages_map = {'early':0, 'mid':1, 'late':2}
		#max_nations = 80 #Value that is good for dom-I, dom-II and dom-III versions < 3.14
		max_nations = 95

		# Trim the response prefix, until \0x2 => status waiting
		resp = resp[resp.find('\x02')+1:]
		pyEyeLogger.debug( "Trimmed response header. result: %s"% list(resp))

		game_name = resp[:resp.find('\x00')]
		resp = resp[resp.find('\x00'):]  

		#Time to host is right after magic char of 45 or '-'
		pyEyeLogger.debug( "time to host raw: %s"% list(resp[resp.find('-')+1:resp.find('-')+5]))
		time_to_host = (unpack('i', resp[resp.find('-')+1:resp.find('-')+5])[0])/60000 #Normalized to minutes.
		#Trim response so that it only contains nations status.
		resp = resp[resp.find('-')+6:]
		turn_num = unpack('i', resp[(3*max_nations+0):(3*max_nations+4)])[0]
		pyEyeLogger.debug( "Extracted game name: %s"%game_name)
		pyEyeLogger.debug("Time to next host: %4d hours, %2d minuts"%(time_to_host/60, time_to_host%60) )
		#pyEyeLogger.debug( "game age is: %s"% cfg.m_games[game_name].m_age )

		#Update info for all nations.
		for current_dict in nations_dict_list:
			for nation_id, nation_cfg in current_dict.iteritems(): 

				pyEyeLogger.debug("Nation ID %s, nation %s, played by: %s, status %s, connected %s"%(int(nation_id), nation_cfg.m_name, list(resp[int(nation_id)]),list(resp[int(nation_id)+max_nations]),list(resp[int(nation_id)+(2*max_nations)])))
				

				#Sometimes nation indes is screwed up (0xff instead of 0), so make sure its in range [0,2]
				if ord(resp[int(nation_id)]) > 2:
					controled_by = '\x00' #Set to not played
				else:
					#ToDo check whether value '0xff' signifies nation defeated.
					controled_by = resp[int(nation_id)] #Set to 0,1,2

				nation_cfg.set_nation_status( ord(controled_by), ord(resp[int(nation_id)+max_nations]), ord(resp[int(nation_id)+(2*max_nations)]))
				#current_dict[nation_id] = stat
				pyEyeLogger.debug("Updated nation status in dictionary: %s."%nation_cfg)

#		#Pretty print results
#		print "\nGame: ",game_name, ", Turn ",turn_num,", Time to next host: %4d hours, %2d minuts"%(time_to_host/60, time_to_host%60) 
#		print 75*'-'
#		print 75*'-'
#		for current_dict in nations_dict_list:
#			for nation_id, nation_cfg in current_dict.iteritems(): 
#				if nation_cfg.m_controled != 'not playing':
#					print "-> %12s, %11s controlled, %10s and is %13s <-"%(nation_cfg.m_name, nation_cfg.m_controled, nation_cfg.m_status, nation_cfg.m_con)
#					print 75*'-'
#
#		print 75*'-'

		# The list of nations dictionaries contains all the information about the status of the nations in the game
		# The return values is a list of nations that are actually playing the game
		query_result = []
		for current_dict in nations_dict_list:
			for nation_id, nation_cfg in current_dict.iteritems(): 
				if nation_cfg.m_controled != 'not playing':
					query_result.append(nation_cfg)

		pyEyeLogger.debug("Query result as list of playing nations is: %s."%query_result)
		return query_result, time_to_host, turn_num

		
	def parse_query_response(self,resp, cfg):
		"""Parse dom-III status query response"""
	
		pyEyeLogger.debug( "Parsing response: %s"% list(resp))

		controled	= ('not playing', 'human', 'AI')
		status		= ('not played', 'played')
		con		= ('not connected', 'connected')

		early_nations_dict ={ 
		0: ('Arcoscephale', controled[0], status[0], con[0]),
		1: ('Ermor', controled[0], status[0], con[0]),
		2: ('Ulm', controled[0], status[0], con[0]),
		3: ('Marverni', controled[0], status[0], con[0]),
		4: ('Sauromatia', controled[0], status[0], con[0]),
		5: ('Tien Chi', controled[0], status[0], con[0]), 
		6: ('reserved', controled[0], status[0], con[0]),
		7: ('Mictlan', controled[0], status[0], con[0]),
		8: ('Abysia', controled[0], status[0], con[0]),
		9: ('Caelum', controled[0], status[0], con[0]),
		10: ('Ctis', controled[0], status[0], con[0]),
		11: ('Pangaea', controled[0], status[0], con[0]),
		12: ('Agartha', controled[0], status[0], con[0]),
		13: ('reserved', controled[0], status[0], con[0]),
		14: ('reserved', controled[0], status[0], con[0]),
		15: ('Vanheim', controled[0], status[0], con[0]),
		16: ('Helheim', controled[0], status[0], con[0]),
		17: ('Niefelheim', controled[0], status[0], con[0]),
		18: ('Kailasa', controled[0], status[0], con[0]),
		19: ('Yomi', controled[0], status[0], con[0]),
		20: ('reserved', controled[0], status[0], con[0]),
		21: ('Atlantis', controled[0], status[0], con[0]),
		22: ('Rlyeh', controled[0], status[0], con[0]),
		26: ('Oceania', controled[0], status[0], con[0]),
		}
		

		mid_nations_dict ={ 
		27: ('Arcoscephale', controled[0], status[0], con[0]),
		28: ('Ermor', controled[0], status[0], con[0]),
		29: ('Pythium', controled[0], status[0], con[0]),
		30: ('Man', controled[0], status[0], con[0]),
		31: ('Ulm', controled[0], status[0], con[0]),
		32: ('Marignon', controled[0], status[0], con[0]), 
		33: ('Mictlan', controled[0], status[0], con[0]),
		34: ('Tien Chi', controled[0], status[0], con[0]),
		35: ('Machaka', controled[0], status[0], con[0]),
		36: ('Agartha', controled[0], status[0], con[0]),
		37: ('Abysia', controled[0], status[0], con[0]),
		38: ('Caelum', controled[0], status[0], con[0]),
		39: ('Ctis', controled[0], status[0], con[0]),
		40: ('Pangaea', controled[0], status[0], con[0]),
		41: ('Vanheim', controled[0], status[0], con[0]),
		42: ('Jotunheim', controled[0], status[0], con[0]),
		43: ('Bandar Log', controled[0], status[0], con[0]),
		44: ('Shinuyama', controled[0], status[0], con[0]),
		45: ('Gath', controled[0], status[0], con[0]),
		46: ('Atlantis', controled[0], status[0], con[0]),
		47: ('Rlyeh', controled[0], status[0], con[0]),
		48: ('Oceania', controled[0], status[0], con[0]),
		}
		late_nations_dict ={ 
		49: ('Arcoscephale', controled[0], status[0], con[0]),
		50: ('Ermor', controled[0], status[0], con[0]),
		51: ('Chelms', controled[0], status[0], con[0]),
		52: ('Ulm', controled[0], status[0], con[0]),
		53: ('Marignon', controled[0], status[0], con[0]),
		54: ('Mictlan', controled[0], status[0], con[0]), 
		55: ('Tien Chi', controled[0], status[0], con[0]),
		56: ('Jotunheim', controled[0], status[0], con[0]),
		57: ('Agartha', controled[0], status[0], con[0]),
		58: ('Abysia', controled[0], status[0], con[0]),
		59: ('Caelum', controled[0], status[0], con[0]),
		60: ('Ctis', controled[0], status[0], con[0]),
		61: ('Pangaea', controled[0], status[0], con[0]),
		62: ('Midgard', controled[0], status[0], con[0]),
		63: ('Utgard', controled[0], status[0], con[0]),
		64: ('Patala', controled[0], status[0], con[0]),
		65: ('Gath', controled[0], status[0], con[0]),
		66: ('Atlantis', controled[0], status[0], con[0]),
		67: ('Rlyeh', controled[0], status[0], con[0]),
		}

		nations_dict_list = [early_nations_dict, mid_nations_dict, late_nations_dict]
		ages_map = {'early':0, 'mid':1, 'late':2}
		#max_nations = 80 #Value that is good for dom-I, dom-II and dom-III versions < 3.14
		max_nations = 95

		# Trim the response prefix, until \0x2 => status waiting
		resp = resp[resp.find('\x02')+1:]
		pyEyeLogger.debug( "Trimmed response header. result: %s"% list(resp))

		game_name = resp[:resp.find('\x00')]
		resp = resp[resp.find('\x00'):]  

		#Time to host is right after magic char of 45 or '-'
		pyEyeLogger.debug( "time to host raw: %s"% list(resp[resp.find('-')+1:resp.find('-')+5]))
		time_to_host = (unpack('i', resp[resp.find('-')+1:resp.find('-')+5])[0])/60000 #Normalized to minutes.
		#Trim response so that it only contains nations status.
		resp = resp[resp.find('-')+6:]
		turn_num = unpack('i', resp[(3*max_nations+0):(3*max_nations+4)])[0]
		pyEyeLogger.debug( "Extracted game name: %s"%game_name)
		pyEyeLogger.debug("Time to next host: %4d hours, %2d minuts"%(time_to_host/60, time_to_host%60) )
		pyEyeLogger.debug( "game age is: %s"% cfg.m_games[game_name].m_age )

		#Update info for all nations.
		current_dict = nations_dict_list[ages_map[cfg.m_games[game_name].m_age]]
		for nation_id, stat in current_dict.iteritems(): 

			if dbg_lvl == 10:
				#print "Nation %s, played by: %c, status %c, connected %c"%(current_dict[nation_id].[0], resp[nation_id],resp[nation_id+max_nations],resp[nation_id+(2*max_nations)])
				print "Nation ID %d, nation %s, played by: %s, status %s, connected %s"%(nation_id, stat[0], list(resp[nation_id]),list(resp[nation_id+max_nations]),list(resp[nation_id+(2*max_nations)]))
			

			#Sometimes nation indes is screwed up (0xff instead of 0), so make sure its in range [0,2]
			if ord(resp[nation_id]) > 2:
				controled_by = '\x00' #Set to not played
			else:
				#ToDo check whether value '0xff' signifies nation defeated.
				controled_by = resp[nation_id] #Set to 0,1,2

			if dbg_lvl == 10:
				print controled[ord(controled_by)]
				print status[ord(resp[nation_id+max_nations])]
				print con[ord(resp[nation_id+(2*max_nations)])]

			stat= ( stat[0], controled[ord(controled_by)], status[ord(resp[nation_id+max_nations])], con[ord(resp[nation_id+(2*max_nations)])])
			current_dict[nation_id] = stat
			if dbg_lvl == 10:
				print "Updated dictionary entry, ", stat

		#Pretty print results
		print "\nGame: ",game_name, ", Turn ",turn_num,", Time to next host: %4d hours, %2d minuts"%(time_to_host/60, time_to_host%60) 
		print 75*'-'
		print 75*'-'
		for nation_id, stat in current_dict.iteritems(): 
			if stat[1] != 'not playing':
				print "-> %12s, %11s controlled, %10s and is %13s <-"%(stat[0], stat[1], stat[2], stat[3])
				print 75*'-'

		print 75*'-'
#1. Parse the response.
#	Add four dictionaries as follows:
#	early_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
#	mid_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
#	late_nations, #nationID: tupple of: 'nationName', nationStatus, isConnected
#	eras, 0:early_nations, 1:mid_nations, 2:late_nations
	


	
class pyEyeCLI(cmd.Cmd):

    m_version		= '1.32'
    m_cfg		= pyEyeCfg()

    def __init__(self):
        cmd.Cmd.__init__(self)
        self.prompt = "[PyEye] "
	intro = "Welcome to PyEye v"+ self.m_version+"!"  ## defaults to None
        self.intro  = intro

    ## Command definitions ##
    def do_hist(self, args):
        """Print a list of commands that have been entered"""
        print self._hist

    def do_exit(self, args):
        """Exits from the PyEye"""
        return -1

    ## Command definitions to support Cmd object functionality ##
    def do_EOF(self, args):
        """Exit on system end of file character"""
        return self.do_exit(args)

    def do_games(self, args):
        """Show list of configured games'!'"""
	#ToDo, put code here to query the status of game (use client and cfg instances) and present
	# the parsed results to the user
	print self.m_cfg.m_games.keys()

    def do_all(self, args):
	""" Query status of all games in configuration """
	if dbg_lvl == 10:
		print "cfg: ", self.m_cfg, " games: ",self.m_cfg.m_games 
	for game_name, game_data in self.m_cfg.m_games.iteritems():
		if dbg_lvl >= 5:
			print "Query status of game: ",game_name, "with cfg: ",game_data
		client = Dom3Client(game_data.m_host,game_data.m_port, 1024	)
		client.parse_query_response(client.send_data(client.build_player_query()), self.m_cfg)
		
    def do_query(self, args):
        """Query for status of game'!'"""
	#ToDo, put code here to query the status of game (use client and cfg instances) and present
	# the parsed results to the user
	for game in args.split(' '):
		game = game.strip()
		if dbg_lvl >= 5:
			print "Query status of game: ",game, "with cfg: ",self.m_cfg.m_games[game].m_host," ",self.m_cfg.m_games[game].m_port
		client = Dom3Client(self.m_cfg.m_games[game].m_host,self.m_cfg.m_games[game].m_port, 1024	)
		client.parse_query_response(client.send_data(client.build_player_query()), self.m_cfg)

    
    def do_about(self, args):
        """PyEye utility 
	Description. a smallish utility that works like God's eye for dom-II - it determines the turn
	status without the player having to actually activate dom-III and connect to the server.
	Its much faster and prevents accidently erasing the player's turn when connecting from multiple computers.

	Many thanks to Johan for his generous help.
	For issues/requests please contact izaqyos@gmail.com.
        """
        ## The only reason to define this method is for the help text in the doc string
        print self.do_about.__doc__


    def do_help(self, args):
        """Get help on commands
           'help' or '?' with no arguments prints a list of commands for which help is available
           'help <command>' or '? <command>' gives help on <command>
        """
        ## The only reason to define this method is for the help text in the doc string
        cmd.Cmd.do_help(self, args)


    ## Override methods in Cmd object ##
    def preloop(self):
        """Initialization before prompting user for commands.
           Despite the claims in the Cmd documentaion, Cmd.preloop() is not a stub.
        """
        cmd.Cmd.preloop(self)   ## sets up command completion
        self._hist    = []      ## No history yet
        self._locals  = {}      ## Initialize execution namespace for user
        self._globals = {}

    def postloop(self):
        """Take care of any unfinished business.
           Despite the claims in the Cmd documentaion, Cmd.postloop() is not a stub.
        """
        cmd.Cmd.postloop(self)   ## Clean up command completion
        print "Thanks for using PyEye!"

    def precmd(self, line):
        """ This method is called after the line has been input but before
            it has been interpreted. If you want to modifdy the input line
            before execution (for example, variable substitution) do it here.
        """
        self._hist += [ line.strip() ]
        return line

    def postcmd(self, stop, line):
        """If you want to stop the console, return something that evaluates to true.
           If you want to do some post command processing, do it here.
        """
        return stop

    def emptyline(self):    
        """Do nothing on empty input line"""
        pass

    def default(self, line):       
        """Called on an input line when the command prefix is not recognized.
	   User gets prompted.
        """
	print "Please enter a known command, or ? for command list"


##Test code
##client = Dom3Client('www.fams.de', 8192, 1024)
###client = Dom3Client('localhost', 21567, 1024)
##client.send_data(client.build_player_query())
#root = Tk()
#root.wm_title("PyEye v"+"0.02")
#gui = pyEyeGui(root)
#root.mainloop()

#cli = pyEyeCLI()
#cli.cmdloop()   

#cfg = pyEyeCfg()

dom_util = DomIIIUtils(pyEyeCfg())
#dom_util.verify_turn_as_client('hydrant_test')
#dom_util.save_turn_for_client('hydrant_test')
