#!/usr/bin/env python

from Tkinter import *		# These are used frequently

import tkMessageBox
import asynchat
import asyncore
import socket
import string
import random
import math
import types
import sys

TERMINATOR 		= '\r\n'	# Line terminator in TCP protocol

COLOURS = [		"red",		# Default colours
			"blue",		# (more made up when necessary)
			"green",
			"yellow",
			"brown"	  ]

INDEPENDENT_COLOUR	= "black"

DATA_PATH		= "images/"	# default: current dir
ASHIP_BITMAP		= "aship.xbm"	# Attacking ship (facing right)
DSHIP_BITMAP		= "dship.xbm"	# Defending ship (facing left)
SHOT_BITMAP		= "shot.xbm"	# Bullet
PLANET_BITMAP		= "planet.xbm"	# Planet

CANVAS_WIDTH		= 340		# Default values for canvas size
CANVAS_HEIGHT		= 340		# Overridden when playfield is drawn
CANVAS_BORDER		= 10		# How much extra borders to leave for canvas
CANVAS_BACKGROUND	= "white"
SIDEBAR_WIDTH		= 200		# Minimum width of the right sidebar
BATTLE_OFFSET_X		= 50		# How to place battle windows
BATTLE_OFFSET_Y		= 50		# relative to the planets
NUMBER_WIDTH		= 40		# Space to reserve for numbers
					# (left of Move button)
FIELD_FONT		= "Helvetica 9"
FIELD_COLOUR		= "black"
UNCERTAIN_COLOUR	= "#7f7f7f"

HOST			= "localhost"	# Default server host
PORT			= 42529		# and TCP port
DEFAULT_NAME		= "Player"	# Default player name

SOCKET_PERIOD		= 100		# Wait time between polls (in ms)
FRAMES			= 20		# No. of frames in battle animation

PROTOCOL_VERSION	= "0.23"
CLIENT_REVISION		= "1.34"
WINDOW_TITLE		= "Galax Client"

class Connection(asynchat.async_chat):
	"Handle all the TCP socket operations."

	def __init__(self, host, port, queue, protocol):
		asynchat.async_chat.__init__(self)
		self.queue = queue
		self.protocol = protocol
		self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
		self.buffer = ''
		self.set_terminator(TERMINATOR)
		self.connect((host, port))

	def handle_connect(self):
		self.protocol.got_connect()

	def collect_incoming_data(self, data):
		self.buffer = self.buffer + data

	def found_terminator(self):
		data = self.buffer
		self.buffer = ''
		self.queue.recv(data)

	def handle_close(self):
		self.close()
		self.protocol.got_close()

class CommandQueue:
	"Store commands from the server until they can be taken care of."

	def __init__(self, receiver):
		self.queue = []
		self.receiver = receiver
		self.stop()

	def recv(self, data):
		"Add a command to the queue"

		self.queue.append(data)
		print "<--", data
		self.flush()

	def flush(self):
		"""Flush the queue by handing away ash many commands ash possible.
	
		   Stop when the queue is empty or no more
		   commands can be received."""
	
		while self.gate == 1:
			try:
				c = self.queue[0]	# First item
				del self.queue[0]
				self.receiver.recv(c)
			except IndexError:
				return			# No more data

	def start(self):
		"Allow data through."

		self.gate = 1
		self.flush()

	def stop(self):
		"Don't let data through."

		self.gate = 0

class Protocol:
	"Handle the communication protocol."

	def __init__(self, client):
		self.client = client
		self.handlers = {}
		self.queue = CommandQueue(self)
		self.connected = 0			# Not connected
		self.register_handler('GSRV', self.handshake, 2)

	def socket_check(self):
		"Periodically check the TCP sockets."
		
		asyncore.poll(timeout=0.0)
		# FIXME this uses Tk events which is part of interface
		self.checkevent = self.client.interface.root.after(SOCKET_PERIOD, self.socket_check)

	def connect(self, host, port):
		self.conn = Connection(host, port, self.queue, self)
		self.socket_check()

	def register_handler(self, command, func, minargs):
		"""Register a handler function for a command.
		
		   command	4-byte string
		   func		the function to call
		   minargs	the minimum number of arguments
		   
		   The function should accept 3 arguments:
		   args		list of arguments
		   argstr	the argument string unparsed
		   cmd		the actual command given"""

		c = string.upper(command)		# Fold case
		self.handlers[c] = (minargs, func)

	def parse(self, argstr):
		"""Parse the argument string.
		
		   Guess the format of each argument and return a list of
		   arguments of the right type (int, float or string)."""

		parsedargs = []
		args = string.split(argstr)
		for a in args:
			try:				# Integer argument?
				i = string.atoi(a)
				parsedargs.append(i)
			except ValueError:
				try:			# Floating point arg?
					f = string.atof(a)
					parsedargs.append(f)
				except ValueError:	# Raw string
					parsedargs.append(a)
		return parsedargs				

	def recv(self, data):
		"Receive a command from the command queue."

		try:
			cmd = string.split(data, None, 1)
			c = string.upper(cmd[0])	# Fold case
		except IndexError:			# No valid command
			return

		try:
			argstr = cmd[1]
		except IndexError:
			argstr = ''			# No arguments

		args = self.parse(argstr)
		if c in self.handlers.keys():
			(minargs, func) = self.handlers[c]
			if len(args) >= minargs:
				func(args, argstr, c)	# Call the handler
			else:
				self.client.interface.popup.warn("Not enough arguments (%d needed) for command:\n%s %s" %
					(minargs, c, argstr))
		else:
			self.client.interface.popup.warn("Unknown command:\n%s %s" % (c, argstr))
			# FIXME ugly?

	def send(self, data):
		if self.connected:
			print "-->", data
			self.conn.send(data + TERMINATOR)

	def got_close(self):
		print "closed"
		self.connected = 0
							# FIXME what else?

	def got_connect(self):
		print "connected"
		self.connected = 1
		self.queue.start()			# Accept commands

	def handshake(self, arg, argstr, cmd):
		rev = CLIENT_REVISION
		s = "GCLT %s client.py %s" % (PROTOCOL_VERSION, rev)
		self.send(s)

	def disconnect(self):
		if self.connected:
			self.conn.close()
		self.got_close()

class Player:
	"Hold information about a single player."
	def __init__(self):
		self.done = 0

	def update(self, client, id, name, clientid):
		self.id = id
		self.name = name
		self.clientid = clientid
		if id != 0:			# FIXME this is ugly
			client.interface.notify(self, id)

	def set_done_status(self, done):
		self.done = done
		if self.id != 0:		# FIXME
			client.interface.notify(self, self.id)

class Players:
	"Hold information about the players in the game."

	def __init__(self, client):
		self.client = client
		independent = Player()
		independent.update(client, 0, "Independent", "")
		self.ids = { 0: independent }	# Player-by-id hash
		client.protocol.register_handler('GPLR', self.add_player, 3)
		client.protocol.register_handler('KILL', self.kill_player, 1)
		client.protocol.register_handler('WINN', self.winner, 1)
		client.protocol.register_handler('PDON', self.pdon, 1)
	
	def add_player(self, args, argstr, cmd):
		"Add a player to the game according to the GPLR command."

		id = args[0]
		[idstr, name, clientid] = string.split(argstr, None, 2)
		p = Player()				# Create new player
		self.ids[id] = p			# Add to hash
		self.client.interface.notify(self)	# Update player list
		p.update(self.client, id, name, clientid)

	def kill_player(self, args, argstr, cmd):
		"Kill a player, according to KILL"
		
		id = args[0]
		try:
			p = self.ids[id]
		except KeyError:
			print "Invalid KILL received!"
			return
		self.client.interface.popup.killed(id)
		del self.ids[id]
		self.client.interface.notify(self, what=cmd, subelement=p)

	def winner(self, args, argstr, cmd):
		"Declare the winner"
		
		id = args[0]
		self.client.interface.popup.winner(id)

	def pdon(self, args, argstr, cmd):
		try:
			p = self.ids[args[0]]
		except KeyError:
			print "Invalid PDON received!"
			return
		p.set_done_status(1)

	def clear_done_status(self):
		for p in self.ids.values():
			p.set_done_status(0)
		

class Planet:
	"Hold information about a single planet."
	
	def __init__(self, client, id, x, y, indust):
		self.id = id
		self.x = x
		self.y = y
		self.indust = indust
		self.client = client
		self.seenyear = 0
		self.seenships = 0
		self.ships = 0

	def setvalues(self, owner, ships, indust, uncertain=0):
		if owner != None:
			self.owner = owner
		if uncertain:
			self.seenyear = self.client.year.get()
			self.seenships = ships
		self.ships = ships
		if indust != None:
			self.indust = indust
		self.client.interface.notify(self, self.id)

class Playfield:
	"Hold information about the playfield, planets etc."
	
	def __init__(self, client):
		self.client = client
		self.ids = {}			# id-to-planet hash
		client.protocol.register_handler('GFLD', self.define_field, 5)
		client.protocol.register_handler('PLNT', self.define_planet, 6)

	def define_field(self, args, argstr, cmd):
		"Set the playfield according to the GFLD command"

		[self.xsize, self.ysize, self.nplanets] = args[0:3]
		self.client.interface.notify(self, None, "size")
	
	def define_planet(self, args, argstr, cmd):
		"Create or update a planet according to the PLNT command"
		  
		[id, x, y, owner, ships, indust] = args[0:6]
		try:
			p = self.ids[id]
			p.setvalues(owner, ships, indust)
		except KeyError:
			p = Planet(client, id, x, y, indust)
			self.ids[id] = p
			p.setvalues(owner, ships, indust)
			if owner == self.client.players.myself:
				self.client.planetinfo.set(id)

	def update_ships(self, id, ships, owner=None, uncertain=0):
		"Update the number of ships on a planet."
		
		p = self.ids[id]
		p.setvalues(owner, ships, None, uncertain)
		self.client.planetinfo.update()

class Battle:
	def __init__(self, client):
		self.client = client
		self.client.interface.popup.battle()

	def update(self, cmd, args):
		if cmd == 'BATT':
			self.client.year.set(args[0])		# Set year
			self.planet = self.client.planetinfo.set(args[1])	# Set planet 
			self.attacker = args[2]
			self.defender = args[3]
			self.surprise = args[4]
			self.aships = args[5]
			self.dships = args[6]
		elif cmd == 'ATTK':
			self.who = args[0]
			self.aships = args[2]
			self.dships = args[3]
		elif cmd == 'BWIN':
			self.winner = args[0]
			self.winnerid = args[1]
			self.loserid = args[2]
			left = args[3]
			planet = args[4]
			self.client.field.update_ships(planet, left, self.winnerid, uncertain=1)
		elif cmd == 'FORT':
			self.client.year.set(args[0])
			self.planet = self.client.planetinfo.set(args[1])
			self.owner = args[2]
			self.totships = args[3]
			self.fortships = args[4]
		self.client.interface.notify(self, None, cmd)

class Battles:
	def __init__(self, client):
		self.client = client
		self.queue = []
		client.protocol.register_handler('FORT', self.add_event, 5)
		client.protocol.register_handler('BATT', self.add_event, 7)
		client.protocol.register_handler('ATTK', self.add_event, 4)
		client.protocol.register_handler('BWIN', self.add_event, 5)
	
	def add_event(self, args, argstr, cmd):
		if cmd == 'BATT' or cmd == 'FORT':
			self.battle = Battle(self.client)
		self.battle.update(cmd, args)

class Game:
	def update(self, client, id, nplayers, list):
		self.id = id
		self.nplayers = nplayers
		self.list = list
		client.interface.notify(self, id)

class Games:
	def __init__(self, client):
		self.client = client
		self.games = {}
		client.protocol.register_handler('GAME', self.game, 3)
		client.protocol.register_handler('SLCT', self.slct, 0)
	
	def game(self, args, argstr, cmd):
		id = args[0]
		nplayers = args[1]
		list = args[2:]		
		try:
			g = self.games[id]
		except KeyError:
			g = Game()
			self.games[id] = g
			self.client.interface.notify(self)
		g.update(self.client, id, nplayers, list)		
	
	def slct(self, args, argstr, cmd):
		pass


class StatusBar:
	def __init__(self, client):
		self.client = client
		self.text = ""
		self.client.protocol.register_handler('CLOK', self.clok, 0)
		self.client.protocol.register_handler('JNOK', self.jnok, 3)
		self.client.protocol.register_handler('GOWN', self.gown, 2)

	def set(self, text):
		self.text = text
		self.client.interface.notify(self)

	def clear(self):
		self.set("")

	def clok(self, args, argstr, cmd):
		self.set("Client OK.")
		self.client.interface.popup.selectgame()

	def jnok(self, args, argstr, cmd):	# FIXME part of interface?
		self.set("Joined OK.")
		id = args[1]
		self.client.players.myself = id

	def gown(self, args, argstr, cmd):
		owner = args[1]
		if self.client.players.myself == owner:
			self.set("You are the owner of the game.")
			self.client.interface.popup.startgame()
		else:
			self.set("Waiting for the owner to start the game.")
		

class Year:
	def __init__(self, client):
		self.client = client
		client.protocol.register_handler('YEAR', self.year_cmd, 1)
		
	def year_cmd(self, args, argstr, cmd):
		self.year = args[0]
		self.client.interface.notify(self, what=cmd)
		self.client.players.clear_done_status()
		self.client.planetinfo.update()

	def set(self, year):
		self.year = year
		self.client.interface.notify(self)

	def get(self):
		return self.year

class PlanetInfo:
	def __init__(self, client):
		self.client = client
		self.planet = None
	
	def set(self, planetid):
		self.planet = self.client.field.ids[planetid]
		self.update()
		return self.planet

	def update(self):
		self.client.interface.notify(self)

class Transfer:
	def __init__(self, client):
		self.client = client
		self.dist = ""
		self.eta = ""
		self.clear()
		self.client.protocol.register_handler('MVOK', self.mvok, 3)
	
	def set_from(self, planet):
		self.from_planet = planet
		self.calculate()
		self.client.interface.notify(self)
	
	def set_to(self, planet):
		self.to_planet = planet
		self.calculate()
		self.client.interface.notify(self)

	def clear(self):
		self.from_planet = None
		self.to_planet = None
		self.calculate()
		self.client.interface.notify(self)

	def distance(self, p1, p2):
		"Count the distance between two planets"
		
		dx = p1.x - p2.x
		dy = p1.y - p2.y
		return math.sqrt(dx * dx + dy * dy)

	def calculate(self):
		if self.from_planet and self.to_planet:
			dist = self.distance(self.from_planet, self.to_planet)
			eta = self.client.year.year + dist
			self.dist = "%.02f" % dist
			self.eta = "%.02f" % eta
		else:
			self.dist = ""
			self.eta = ""

	def move(self, amount):
		"Make the transfer: move the ships"

		if self.from_planet and self.to_planet:
			f = self.from_planet.id
			t = self.to_planet.id
			s = "MOVE %d %d %d" % (f, t, amount)
			self.client.protocol.send(s)

	def mvok(self, args, argstr, cmd):
		id = args[0]
		left = args[1]
		eta = args[2]
		self.client.status.set("Move OK, arrival in %.02f, %d ships left" % (eta, left))
		self.client.field.update_ships(id, left)
		self.clear()

class Errors:
	def __init__(self, client):
		self.client = client
		client.protocol.register_handler('ERRP', self.err, 1)
		client.protocol.register_handler('ERRM', self.err, 1)
		client.protocol.register_handler('ERRC', self.err, 1)
		client.protocol.register_handler('ERRJ', self.err, 1)
	
	def err(self, args, argstr, cmd):
		if cmd == 'ERRP':
			err = "Permission error"
		elif cmd == 'ERRM':
			err = "Error during move"
		elif cmd == 'ERRC':
			err = "Command error"
		elif cmd == 'ERRJ':
			err = "Error during join"
		self.client.interface.popup.error(err, argstr)
		
# The interface classes start here

class Interface:
	"The class that owns all the interface elements."
	def __init__(self, client):
		self.client = client
		self.handlers = {}		# Class-to-handler hash
		self.root = Tk()		# The root window
 		self.actions = Actions(client, self)	# Action methods
		self.data = InterfaceData(client)
		self.popup = PopUp(client, self)	# Pop-up window creation
		self.mainwindow = MainWindow(self.root, self, self)	# Main window
		
		self.root.columnconfigure(1, minsize=SIDEBAR_WIDTH)

	def start(self):
		self.root.mainloop()
	
	def register_display(self, elementclass, id, handler):
		if not elementclass == None:
			key = (elementclass, id)
			if key in self.handlers.keys():
				self.handlers[key].append(handler)
			else:
				self.handlers[key] = [handler]
		else:
			print "Attempt to register display handler for None from", handler
	
	def notify(self, element, id=None, what=None, subelement=None):
		key = (element.__class__, id)
		try:
			handlers = self.handlers[key]
		except KeyError:
			print "No handler for", key
			return
		for h in handlers:
			h(element, what, subelement)


	def unregister_display(self, handler):
		for h in self.handlers.values():
			if handler in h:
				h.remove(handler)

class Actions:
	"Handle all the actions the user can do, i.e. mouse clicks."
		
	def start_queue(self):
		self.client.protocol.queue.start()
	
	def stop_queue(self):
		self.client.protocol.queue.stop()

	def __init__(self, client, interface):
		self.client = client
		self.interface = interface

	def move(self, amount):
		"Press of the Move button."

		self.client.trans.move(amount)

	def done(self):
		"Press of the Turn Done button."

		self.client.trans.clear()
		self.client.protocol.send("DONE")

	def selectplanet(self, planetid):
		"Show information about a planet when the mouse enters it."

		self.client.planetinfo.set(planetid)

	def selecttrans(self, planet, button):
		if button == 1:
			self.client.trans.set_from(planet)
		else:
			self.client.trans.set_to(planet)
	
	def connect(self):
		"The menu item Connect"
		
		self.interface.popup.connect()
	
	def disconnect(self):
		"The menu item Disconnect"
		
		pass				# FIXME
	
	def quit(self):
		"The menu item Quit"
		
		self.interface.popup.confirmquit()

	def do_connect(self, host, port):
		"Handle the Connect button in the Connect dialog box."
		
		self.client.protocol.connect(host, port)

	def do_quit(self):
		"The user has confirmed that s/he wants to quit."
		
		self.client.protocol.send("QUIT")
		self.client.protocol.disconnect()
		self.interface.root.destroy()

class Dialog(Toplevel):
	"Base class for dialog boxes. Based on tkSimpleDialog.Dialog."

	def __init__(self, client, title=None, ok="OK", cancel="Cancel", wait=1, grab=1, place=1):
		self.client = client
		self.interface = client.interface
		self.parent = client.interface.root
		
		Toplevel.__init__(self, self.parent)
		self.transient(self.parent)
		
		self.text_ok = ok
		self.text_cancel = cancel

		if title:
			self.title(title)
		
		body = Frame(self)
		self.initial_focus = self.body(body)
		body.pack(padx=5, pady=5)
		
		self.buttonbox()
		
		if grab:
			try:
				self.grab_set()
			except TclError:
				pass
		
		if not self.initial_focus:
			self.initial_focus = self
		
		self.protocol("WM_DELETE_WINDOW", self.cancel)
		
		if place:
			self.geometry("+%d+%d" % (self.parent.winfo_rootx() + 50,
						  self.parent.winfo_rooty() + 50))
		
		self.initial_focus.focus_set()
		
		if wait:
			self.wait_window(self)
		
	def body(self, master):
		"Create the dialog body. Should be overridden."
		
		pass
	
	def buttonbox(self):
		"Create the button box."
		
		box = Frame(self)
		
		b1 = Button(box, text=self.text_ok, width=10, command=self.ok, default=ACTIVE)
		b1.pack(side=LEFT, padx=5, pady=5)
		if self.text_cancel:
			b2 = Button(box, text=self.text_cancel, width=10, command=self.cancel)
			b2.pack(side=LEFT, padx=5, pady=5)
		
		self.bind("<Return>", self.ok)
		if self.text_cancel:
			self.bind("<Escape>", self.cancel)
		
		box.pack()
	
	def ok(self, event=None):
		"The OK button has been pressed."

		if not self.validate():
			self.initial_focus.focus_set()
			return
		
		self.withdraw()
		self.update()
		
		self.apply()
		
		self.cancel()
	
	def cancel(self):
		"The Cancel button has been pressed."

		self.parent.focus_set()
		self.destroy()
		
	def validate(self):
		"Should probably be overridden. Check that the data is OK."

		return 1
	
	def apply(self):
		"Override this."

		pass
		
class ConnectDialog(Dialog):
	"Create the Connect... dialog box."

	def __init__(self, client):
		Dialog.__init__(self, client, "Connect to server", ok="Connect")
	
	def body(self, master):
		self.host = StringVar()
		self.port = StringVar()
		self.host.set(HOST)
		self.port.set(PORT)
		Label(master, text="Connect to server").grid(columnspan=2)
		Label(master, text="Host:").grid(row=1, column=0)
		Label(master, text="Port:").grid(row=2, column=0)
		hostent = Entry(master, textvariable=self.host).grid(row=1, column=1)
		Entry(master, textvariable=self.port).grid(row=2, column=1)
		return hostent			# Default focus

	def apply(self):
		host = self.host.get()
		port = string.atoi(self.port.get())
		self.interface.actions.do_connect(host, port)

	def validate(self):
		try:
			port = string.atoi(self.port.get())
			return 1
		except ValueError:
			return 0		

class GameSelectDialog(Dialog):
	"Create the game selection dialog box."
	def __init__(self, client):
		self.games = {}				# Game by id hash
		self.name = StringVar()
		self.name.set(DEFAULT_NAME)
		Dialog.__init__(self, client, "Select game to join", ok="Join", wait=0)
	
	def body(self, master):
		Label(master, text="Select the game to join").pack()
		frame1 = Frame(master)
		self.display = GameList(frame1, self, self.client.interface)
		frame1.pack()
		frame2 = Frame(master)
		Label(frame2, text="Player Name:").pack(side=LEFT)
		Entry(frame2, textvariable=self.name).pack(side=LEFT)
		frame2.pack(padx=5, pady=5)
		
	def apply(self):
		choice = self.display.choice.get()
		name = self.name.get()
		s = "JOIN %d %s" % (choice, name)
		self.client.protocol.send(s)
		self.display.delete()

class StartGameDialog(Dialog):
	"Create the Start Game dialog box."
	def __init__(self, client):
		Dialog.__init__(self, client, "Start the game?", ok="Start", cancel=None, wait=0)

	def body(self, master):
		Label(master, text="Start the game?").pack()
	
	def apply(self):
		self.client.protocol.send("PLAY")

class BattleWindow(Dialog):
	"Display the Battle and Fortify windows."
	
	def __init__(self, client):
		Dialog.__init__(self, client, wait=0, place=0, grab=1)
	
	def body(self, master):
		BattleScreen(master, self, self.client.interface)
	
	def buttonbox(self):
		pass
	

class InterfaceElement:
	"An abstract base class for use by all interface elements."

	elementclass = None		# The logical element class this 
					# display element represents
					# Must be overridden!
	
	def __init__(self, master, parent, interface, id=None):
		"""Initialize
		
		   master is the widget in which this element is placed
		   parent is the graphical parent object"""
	
		self.master = master	# The master widget in which to draw
		self.parent = parent	# The (graphical) parent object
		self.actions = interface.actions # Actions instance, for binding actions
		self.interface = interface
		self.data = interface.data
		self.id = id

		self.init_vars()	# Initialize variables
		self.clear()		# Clear state
		self.draw(master)	# Draw this element
		self.register()		# Register ash a display

	def register(self):
		ec = self.elementclass
		if type(ec) == types.ListType:
			for c in ec:
				self.interface.register_display(c, self.id, self.update)
		else:
			self.interface.register_display(ec, self.id, self.update)

	def unregister(self):
		self.interface.unregister_display(self.update)

	def init_vars(self):
		"Create necessary Tk and Python variables."
		
		pass
  
	def clear(self):
		"Clear the state of this element."
		
		pass

	def draw(self, master):
		"Draw this element. Should be overridden."
		  
		pass
	
	def update(self, element, what=None, subelement=None):
		"""Should usually be overridden. Update display.
		
		   what can be used to pass additional information
		   subelement can be used to pass another element"""
	
		pass

	
class BattleScreen(InterfaceElement):
	elementclass = Battle
	
	def init_vars(self):
		self.abmp = "@" + DATA_PATH + ASHIP_BITMAP
		self.dbmp = "@" + DATA_PATH + DSHIP_BITMAP
		self.sbmp = "@" + DATA_PATH + SHOT_BITMAP
		self.ash = []
		self.dsh = []
		self.width = 300
		self.height = 100
		self.shnumscale = 1
		self.ac = StringVar()
		self.dc = StringVar()
		self.st = StringVar()
		self.ac.set(0)
		self.dc.set(0)
		self.qb = self.data.quickbattles()
	
	def draw(self, master):
		if not self.qb:
			self.canvas = Canvas(master, width=self.width, height=self.height, bg="white")
			self.canvas.grid(columnspan=2)
		Label(master, textvariable=self.ac).grid(row=1, column=0, sticky=W)
		Label(master, textvariable=self.dc).grid(row=1, column=1, sticky=E)
		Label(master, textvariable=self.st).grid(row=2, columnspan=2)
		master.columnconfigure(0, minsize=self.width/2)
		master.columnconfigure(1, minsize=self.width/2)
	
	def scale(self, n):
		return int(math.ceil(float(n)/self.shnumscale))

	def adjust_scale(self, a, b=0):
		while self.scale(a) > 100 or self.scale(b) > 100:
			self.shnumscale = self.shnumscale + 1

	def draw_battle(self, element):
		acolour = self.data.get_colour(element.attacker)
		dcolour = self.data.get_colour(element.defender)
		a = element.aships
		d = element.dships
		self.adjust_scale(a, d)
		self.draw_ships(self.ash, a, 1, acolour)
		self.draw_ships(self.dsh, d, 0, dcolour)
		if element.surprise:
			s = "Surprise Attack!"
			self.set_status(s, 1000)
		s = "%d of %s's ships arrive at (%d, %d)" % 	\
			(element.aships, self.data.get_name(element.attacker),
			 element.planet.x, element.planet.y)
		self.set_status(s, 1000)
		
	def draw_fortify(self, element):
		colour = self.data.get_colour(element.owner)
		total = element.totships
		orig = total - element.fortships
		self.adjust_scale(total)
		self.draw_ships(self.ash, orig, 1, colour)
		s = "%d ships fortify (%d, %d)" %	\
			(element.fortships, element.planet.x, element.planet.y)
		self.set_status(s, 1000)
		self.draw_ships(self.ash, total, 1, colour, orig)
		self.delay(1000)
		self.set_status("", 0)

	def draw_ships(self, list, ships, side, colour, start=0):
		if self.qb:
			if side == 1:
				self.ac.set(ships)
			else:
				self.dc.set(ships)
			return

		s = self.scale(start)
		n = self.scale(ships)
		for i in range(s, n):
			if side == 1:			# Attacker's ships
				x = 8 * (i / 10) + 14
				y = 8 * (i % 10) + 14
				bmp = self.abmp
				self.ac.set(ships)
			else:				# Defender's ships
				x = self.width - 14 - 8 * (i / 10)
				y = self.height - 14 - 8 * (i % 10)
				bmp = self.dbmp
				self.dc.set(ships)
			ship = self.canvas.create_bitmap(x, y, bitmap=bmp, foreground=colour)
			list.insert(0, ship)
		
	def choose(self, n):
		if n > 9:
			return random.randint(0, 9)
		elif n <= 1:
			return 0
		else:
			return random.randint(0, n-1)

	def shoot(self, a, d):
		[ax, ay] = self.canvas.coords(a)
		[dx, dy] = self.canvas.coords(d)
		vx = (dx - ax) / float(FRAMES)
		vy = (dy - ay) / float(FRAMES)
		s = self.canvas.create_bitmap(ax, ay, bitmap=self.sbmp)
		for i in range(FRAMES):
			self.canvas.move(s, vx, vy)
			self.delay(2)
		self.canvas.delete(s)

	def place_popup(self, element, title):
		canvas = self.data.get_field_canvas()
		w = self.data.get_pwidth()
		h = self.data.get_pheight()
		px = element.planet.x
		py = element.planet.y
		x = canvas.winfo_rootx() + w * px + BATTLE_OFFSET_X
		y = canvas.winfo_rooty() + h * py + BATTLE_OFFSET_Y
		self.parent.geometry("+%d+%d" % (x, y))
		self.parent.title(title)

	def update(self, element, what, subelement):
		if what == 'BATT':
			self.place_popup(element, "Battle")
			self.draw_battle(element)
		elif what == 'FORT':
			self.place_popup(element, "Fortify")
			self.draw_fortify(element)
			self.unregister()
			self.parent.cancel()
		elif what == 'ATTK':
			if element.who:		# attacker charging
				a = self.ash
				d = self.dsh
				new = element.dships
				c = self.dc
			else:			# defender charging
				a = self.dsh
				d = self.ash
				new = element.aships
				c = self.ac
			if not self.qb:
				dc = string.atoi(c.get())
				n = self.scale(new)	# New number of defender's ships
				h = len(d) - n		# How many virtual ships to destroy
				while h > 0:
					i = self.choose(len(a))
					j = self.choose(len(d))
					self.shoot(a[i], d[j])
					self.canvas.delete(d[j])
					del d[j]
					dc = dc - self.shnumscale
					h = h - 1
					c.set(dc)
			c.set(new)
		if what == 'BWIN':
			if element.winner:
				s = "The attackers are victorious"
			else:
				s = "The defenders beat off the attacker"
			self.set_status(s, 1000)
			self.unregister()
			self.parent.cancel()
		self.actions.start_queue()

	def delay(self, time):
		self.actions.stop_queue()
		self.parent.update()
		self.parent.after(time)

	def set_status(self, text, time):
		self.st.set(text)
		self.delay(time)	

class PlayerEntry(InterfaceElement):
	"Display a single player's stats in the player list."

	elementclass = Player
	  
	def init_vars(self):
	  	self.name = StringVar()
	  	self.clientid = StringVar()
		self.done = StringVar()
		
	def draw(self, master):
		self.nlabel = Label(master, textvariable=self.name)
	  	self.clabel = Label(master, textvariable=self.clientid)
		self.dlabel = Label(master, textvariable=self.done)
		row = self.id - 1
	  	self.nlabel.grid(row=row, column=0, sticky=W)
	  	self.clabel.grid(row=row, column=1, sticky=W)
		self.dlabel.grid(row=row, column=2, sticky=W)
		self.master.columnconfigure(1, weight=1)

	def update(self, element, what, subelement):
	  	self.name.set(element.name)
	  	self.clientid.set(element.clientid)
		if element.done:
			self.done.set("Done")
		else:
			self.done.set("")
		colour = self.data.get_colour(self.id)
		self.nlabel.configure(foreground=colour)

	def clear(self):
		self.name.set("")
		self.clientid.set("")
	
	def delete(self):
#		self.clear()
		self.nlabel.destroy()
		self.clabel.destroy()
		self.dlabel.destroy()

class PlayerList(InterfaceElement):
	"Display a list of players in the game."

	elementclass = Players
	
	def init_vars(self):
		self.entries = {}		# Entries in the list (by id)

	def draw(self, master):
		Label(master, text="Players").pack()
		self.frame = Frame(master)
		self.frame.pack(anchor=W)

	def update(self, element, what, subelement):
		if what == 'KILL':
			id = subelement.id
			pe = self.entries[id]
			pe.delete()
			return
		ids = element.ids.keys()
		ids.remove(0)			# Don't show Independent
		ids.sort()			# Sort by id
		for id in ids:
			if id not in self.entries.keys():
				pe = PlayerEntry(self.frame, self, self.interface, id)
				self.entries[id] = pe
		

class PlayfieldDisplay(InterfaceElement):
	"The graphical playfield display."

	elementclass = [Playfield, Transfer, PlanetInfo]

	def init_vars(self):
		file = DATA_PATH + PLANET_BITMAP
		bmp = BitmapImage(file=file)	# Open planet bitmap
		self.pwidth = bmp.width()	# Get width
		self.pheight = bmp.height()	# and height
		self.planetbmp = "@" + file
		self.box = None
		self.arrow = None
		self.planetdisplays = {}	# id-to-display hash
		self.data.set_pwidth(self.pwidth)
		self.data.set_pheight(self.pheight)

	def draw(self, master):
		self.canvas = Canvas(master, width=CANVAS_WIDTH, height=CANVAS_HEIGHT, bg=CANVAS_BACKGROUND)
		self.canvas.bind("<ButtonPress-1>", self.click)
		self.canvas.bind("<B1-Motion>", self.click)
		self.canvas.bind("<ButtonPress-3>", self.click)
		self.canvas.pack()
		self.data.set_field_canvas(self.canvas)

	def draw_arrow(self, from_planet, to_planet):
		if self.arrow:
			self.canvas.delete(self.arrow)
			self.arrow = None
		if from_planet and to_planet:
			sx = (from_planet.x + 0.5) * self.pwidth + CANVAS_BORDER
			sy = (from_planet.y + 0.5) * self.pheight + CANVAS_BORDER
			dx = (to_planet.x + 0.5) * self.pwidth + CANVAS_BORDER
			dy = (to_planet.y + 0.5) * self.pheight + CANVAS_BORDER
			self.arrow = self.canvas.create_line(sx, sy, dx, dy, arrow="last")

	def click(self, event):
		if event.num == 1:		# Left mouse button
			own = 1			# Need to be own
		else:
			own = 0
		planet = self.find_closest(event.x, event.y, own)
		self.actions.selecttrans(planet, event.num)

	def dist2(self, x, y, planet):
		"Returns the square of the distance between planet and (x, y)."
		
		dx = x - planet.x
		dy = y - planet.y
		return (dx * dx + dy * dy)

	def find_closest(self, x, y, own):
		x = (float(x - CANVAS_BORDER) / self.pwidth) - 0.5
		y = (float(y - CANVAS_BORDER) / self.pheight) - 0.5
		min = None
		planet = None
		
		for p in self.field.ids.values():	# All planets
			if own and p.owner != self.data.get_myself():
				continue
			d = self.dist2(x, y, p)
			if d < min or min == None:
				min = d
				planet = p
		return planet

	def mark(self, planet):
		self.unmark()
		x1 = planet.x * self.pwidth + CANVAS_BORDER
		y1 = planet.y * self.pheight + CANVAS_BORDER
		x2 = x1 + self.pwidth
		y2 = y1 + self.pheight
		self.box = self.canvas.create_rectangle(x1, y1, x2, y2)

	def unmark(self):
		if self.box:
			self.canvas.delete(self.box)
			self.box = None

	def update(self, element, what=None, subelement=None):
		ec = element.__class__
		if ec == Playfield:
			w = self.pwidth * element.xsize + 2 * CANVAS_BORDER
			h = self.pheight * element.ysize + 2 * CANVAS_BORDER
			self.canvas.configure(width=w, height=h)
			for id in range(element.nplanets):
				PlanetDisplay(self.canvas, self, self.interface, id)
			self.field = element
		elif ec == Transfer:
			self.draw_arrow(element.from_planet, element.to_planet)
		elif ec == PlanetInfo:
			self.mark(element.planet)


class PlanetDisplay(InterfaceElement):
	"Display a planet icon."

	elementclass = Planet

	def init_vars(self):
		self.bmp = None

	def draw(self, master):
		self.bmp = master.create_bitmap(0, 0, bitmap=self.parent.planetbmp, anchor=NW)
		self.text = master.create_text(0, 0, font=FIELD_FONT, text="")

	def update(self, element, what, subelement):
		x = element.x * self.parent.pwidth + CANVAS_BORDER + 1	# Planet coordinates
		y = element.y * self.parent.pheight + CANVAS_BORDER + 1	# on the canvas
		colour = self.data.get_colour(element.owner)
		ships = element.ships
		ncolour = FIELD_COLOUR
		if ships == -1:
			if element.seenyear != 0:
				ships = element.seenships
				ncolour = UNCERTAIN_COLOUR
			else:
				ships = ""
		self.master.coords(self.bmp, x, y)
		self.master.coords(self.text, x, y)
		self.master.itemconfigure(self.bmp, foreground=colour)
		self.master.itemconfigure(self.text, text=ships, fill=ncolour)
		self.master.tag_bind(self.bmp, "<Enter>", self.showinfo)

	def showinfo(self, event):
		self.actions.selectplanet(self.id)

#	def clear(self):
#		self.master.delete(self.bmp)

class GameListEntry(InterfaceElement):
	"Hold the information on one game in the game list."

	elementclass = Game

	def init_vars(self):
		self.choice = self.parent.choice
		self.text = StringVar()
	
	def draw(self, master):
		if master:
			self.button = Radiobutton(master, textvariable=self.text, 
				variable=self.choice, value=self.id)
			row = self.id
			self.button.grid(row=row, column=0, sticky=W)

	def update(self, element, what, subelement):
		nplayers = element.nplayers
		list = string.join(element.list, ', ')
		s = "Game %d, %d players: %s" % (self.id, nplayers, list)
		self.text.set(s)
	
class GameList(InterfaceElement):
	"Show a list of ongoing games."

	elementclass = Games

	def init_vars(self):
		self.choice = IntVar()
		self.entries = {}		# Entry by id hash

	def draw(self, master):
		if master:
			Radiobutton(master, text="Create a new game", 		\
				variable=self.choice, value=0).grid(row=0, sticky=W)

	def update(self, element, what, subelement):
		for id in element.games.keys():
			try:
				e = self.entries[id]
			except KeyError:	# Create new entry
				e = GameListEntry(self.master, self, self.interface, id)
				self.entries[id] = e

	def delete(self):
		self.master.destroy()
		self.master = None
		
	
class StatusBarDisplay(InterfaceElement):
	"Show a status bar at the bottom of the window."

	elementclass = StatusBar
	
	def init_vars(self):
		self.text = StringVar()

	def draw(self, master):
		self.label = Label(master, textvariable=self.text)
		self.label.pack(side=LEFT)

	def update(self, element, what, subelement):
		self.text.set(element.text)
	
	def clear(self):
		self.text.set("")
	
class YearDisplay(InterfaceElement):

	elementclass = Year

	def init_vars(self):
		self.year = StringVar()
	
	def draw(self, master):
		Label(master, text="Year:").pack(side=LEFT)
		Label(master, textvariable=self.year).pack(side=LEFT)
	
	def update(self, element, what, subelement):
		self.year.set(element.year)

	def clear(self):
		self.year.set("")

class PlanetInfoDisplay(InterfaceElement):

	elementclass = PlanetInfo

	def init_vars(self):
		self.coords = StringVar()
		self.owner = StringVar()
		self.ships = StringVar()
		self.indust = StringVar()
	
	def draw(self, master):
		Label(master, text="Planet Information").pack()

		frame = Frame(master)

		Label(frame, text="Location:").grid(row=1, column=0, sticky=W)
		Label(frame, text="Owner:").grid(row=2, column=0, sticky=W)
		Label(frame, text="Ships:").grid(row=3, column=0, sticky=W)
		Label(frame, text="Indust:").grid(row=4, column=0, sticky=W)
		
		Label(frame, textvariable=self.coords).grid(row=1, column=1, sticky=W)
		l = Label(frame, textvariable=self.owner)
		l.grid(row=2, column=1, sticky=W)
		Label(frame, textvariable=self.ships).grid(row=3, column=1, sticky=W)
		Label(frame, textvariable=self.indust).grid(row=4, column=1, sticky=W)
		
		self.ownerlabel = l
		
		frame.columnconfigure(1, weight=1)
		frame.pack(anchor=W)

	def update(self, element, what, subelement):
		loc = "(%d, %d)" % (element.planet.x, element.planet.y)
		owner = element.planet.owner
		name = self.data.get_name(owner)
		colour = self.data.get_colour(owner)
		ships = element.planet.ships
		indust = element.planet.indust
		
		if ships == -1:
			if element.planet.seenyear != 0:
				ships = "%d? (visited %.2f)" % 	\
					(element.planet.seenships, element.planet.seenyear)
			else:		
				ships = "Unknown"
		if indust == -1:
			indust = "Unknown"
		
		self.coords.set(loc)
		self.owner.set(name)
		self.ships.set(ships)
		self.indust.set(indust)

		self.ownerlabel.configure(foreground=colour)

	def clear(self):
		self.coords.set("")
		self.owner.set("")
		self.ships.set("")
		self.indust.set("")


class TransferDisplay(InterfaceElement):
	"Draw the Transfer window"
	
	elementclass = Transfer
	
	def init_vars(self):
		self.fromvar = StringVar()
		self.tovar = StringVar()
		self.dist = StringVar()
		self.eta = StringVar()

	def draw(self, master):
		Label(master, text="Transfer").pack()
		
		frame = Frame(master)
		Label(frame, text="From:").grid(row=0, column=0, sticky=W)
		Label(frame, text="To:").grid(row=1, column=0, sticky=W)
		Label(frame, text="Dist:").grid(row=2, column=0, sticky=W)
		Label(frame, text="ETA:").grid(row=3, column=0, sticky=W)
		
		Label(frame, textvariable=self.fromvar).grid(row=0, column=1, sticky=W)
		Label(frame, textvariable=self.tovar).grid(row=1, column=1, sticky=W)
		Label(frame, textvariable=self.dist).grid(row=2, column=1, sticky=W)
		Label(frame, textvariable=self.eta).grid(row=3, column=1, sticky=W)
		frame.pack(anchor=W)

	def update(self, element, what=None, subelement=None):
		fp = element.from_planet
		if fp:
			f = "(%d, %d)" % (fp.x, fp.y)
		else:
			f = ""

		tp = element.to_planet
		if tp:
			t = "(%d, %d)" % (tp.x, tp.y)
		else:
			t = ""

		dist = element.dist
		eta = element.eta
		
		if dist == None:
			dist = ""
		if eta == None:
			eta = ""
		
		self.fromvar.set(f)
		self.tovar.set(t)
		self.dist.set(dist)
		self.eta.set(eta)

	def clear(self):
		self.fromvar.set("")
		self.tovar.set("")
		self.dist.set("")
		self.eta.set("")


class Controls(InterfaceElement):
	"The scale, buttons etc."
	
	elementclass = [Transfer, Year]

	def init_vars(self):
		self.amount = IntVar()
		self.max = IntVar()
		self.movephase = 0
		self.f = None
		self.t = None
	
	def draw(self, master):
		self.scale = Scale(master, orient=HORIZONTAL, showvalue=0,
			variable=self.amount, command=self.en_dis_move, to_=0)
		
		self.frame = Frame(master)
		Label(self.frame, textvariable=self.amount).grid(row=0, column=0)
		Label(self.frame, text="of").grid(row=0, column=1)
		Label(self.frame, textvariable=self.max).grid(row=0, column=2)
		self.frame.columnconfigure(0, minsize=NUMBER_WIDTH)
		self.frame.columnconfigure(2, minsize=NUMBER_WIDTH)

		self.bmove = Button(master, text="Move", command=self.move)
		self.bdone = Button(master, text="Turn Done", command=self.done)

		self.scale.pack(side=LEFT)
		self.frame.pack(side=LEFT)
		self.bmove.pack(side=LEFT)
		self.bdone.pack(side=RIGHT)

	def move(self):
		self.bmove.configure(state=DISABLED)
		self.actions.move(self.amount.get())

	def done(self):
		self.bdone.configure(state=DISABLED)
		self.movephase = 0
		self.actions.done()

	def update(self, element, what, subelement):
		ec = element.__class__
		if ec == Transfer:
			if element.from_planet:
				max = element.from_planet.ships
			else:
				max = 0
			self.scale.configure(to_=max)
			self.max.set(max)
			self.f = element.from_planet
			self.t = element.to_planet
			self.en_dis_move()
		elif ec == Year:
			if what == 'YEAR':
				self.movephase = 1
				self.bdone.configure(state=NORMAL)

	def en_dis_move(self, value=None):
		"Enable or disable the Move button, according to the situation."

		value = self.amount.get()
		if value > 0 and self.movephase:
			if self.f and self.t and self.f == self.t:
				state = DISABLED	# same src & dst
			else:
				state = NORMAL
		else:
			state = DISABLED
		
		self.bmove.configure(state=state)


class MainWindow(InterfaceElement):
	"Draw the main window of the program"

	elementclass = Interface

	def init_vars(self):
		self.qb = IntVar()

	def draw(self, master):
		frame1 = Frame(master, borderwidth=1, relief=RIDGE)
		frame2 = Frame(master, borderwidth=1, relief=RIDGE)
		frame3 = Frame(master, borderwidth=1, relief=RIDGE)
		frame4 = Frame(master, borderwidth=1, relief=RIDGE)
		frame5 = Frame(master, borderwidth=1, relief=RIDGE)
		frame6 = Frame(master, borderwidth=1, relief=RIDGE)
		frame7 = Frame(master, borderwidth=1, relief=RIDGE)
		frame8 = Frame(master, borderwidth=1, relief=RIDGE)
		
		PlayfieldDisplay(frame1, self, self.interface)
		PlayerList(frame2, self, self.interface)
		TransferDisplay(frame4, self, self.interface)
		PlanetInfoDisplay(frame5, self, self.interface)
		YearDisplay(frame6, self, self.interface)
		Controls(frame7, self, self.interface)
		StatusBarDisplay(frame8, self, self.interface)

		frame1.grid(row=0, column=0, rowspan=5, sticky=N+S+E+W)
		frame2.grid(row=0, column=1, sticky=N+S+E+W)
		frame3.grid(row=1, column=1, sticky=N+S+E+W)
		frame4.grid(row=2, column=1, sticky=N+S+E+W)
		frame5.grid(row=3, column=1, sticky=N+S+E+W)
		frame6.grid(row=4, column=1, sticky=N+S+E+W)
		frame7.grid(row=5, column=0, columnspan=2, sticky=N+S+E+W)
		frame8.grid(row=6, column=0, columnspan=2, sticky=N+S+E+W)

		menu = Menu(master)
		menu_game = Menu(menu, tearoff=0)
		menu_game.add_command(label="Connect...", command=self.interface.actions.connect)
		menu_game.add_command(label="Disconnect", command=self.interface.actions.disconnect)
		menu_game.add_separator()
		menu_game.add_command(label="Quit", command=self.interface.actions.quit)

		menu_options = Menu(menu, tearoff=0)
		menu_options.add_checkbutton(label="QuickBattles", variable=self.qb)

		menu.add_cascade(label="Game", menu=menu_game)
		menu.add_cascade(label="Options", menu=menu_options)

#		master.rowconfigure(0, weight=1)
		master.configure(menu=menu)
		master.title(WINDOW_TITLE)

class InterfaceData:
	"Holds some data that the interface elements need."
	def __init__(self, client):
		self.field_canvas = None		# Playfield canvas
		self.client = client
		self.pheight = 0
		self.pwidth = 0
		self.colours = { 0: INDEPENDENT_COLOUR }
	
	def get_colour(self, id):
		"""Return the colour of a player by id.
		  
		   The first few colours are defined, more colours are
		   made up when necessary."""
		
		global COLOURS
		try:
			return self.colours[id]
		except KeyError:		# Colour not yet defined
			try:
				colour = COLOURS[0]	# get predefined colour
				del COLOURS[0]
			except IndexError:		# make up a new colour
				r = random.randint(0, 12) * 16
				g = random.randint(0, 12) * 16
				b = random.randint(0, 12) * 16
				colour = "#%02x%02x%02x" % (r, g, b)
			self.colours[id] = colour
			return colour
		
	def get_name(self, id):
		try:
			player = self.client.players.ids[id]
			return player.name
		except KeyError:
			return "Nobody"

	def get_myself(self):
		return self.client.players.myself
	
	def get_field_canvas(self):
		return self.field_canvas

	def set_field_canvas(self, canvas):
		self.field_canvas = canvas

	def set_pwidth(self, pwidth):
		self.pwidth = pwidth
	
	def set_pheight(self, pheight):
		self.pheight = pheight
	
	def get_pwidth(self):
		return self.pwidth
	
	def get_pheight(self):
		return self.pheight
		
	def quickbattles(self):
		return self.client.interface.mainwindow.qb.get()

class PopUp:
	"Create pop-up windows for all needs."

	def __init__(self, client, interface):
		self.interface = interface
		self.client = client

	def warn(self, msg):
		"Warn about something."

		tkMessageBox.showerror("Warning", msg)
	
	def connect(self):
		"Draw the Connect dialog box."
		
		ConnectDialog(self.client)

	def selectgame(self):
		GameSelectDialog(self.client)

	def startgame(self):
		StartGameDialog(self.client)

	def battle(self):
		BattleWindow(self.client)

	def confirmquit(self):
		quit = tkMessageBox.askokcancel("Confirm exit", "Are you sure you want to quit?")
		if quit:
			self.interface.actions.do_quit()

	def killed(self, id):
		name = self.interface.data.get_name(id)
		msg = "%s is dead!" % name
		tkMessageBox.showinfo("Player killed", msg)

	def winner(self, id):
		name = self.interface.data.get_name(id)
		msg = "%s is the winner!" % name
		tkMessageBox.showinfo("Winner", msg)

	def error(self, headline, message):
		tkMessageBox.showerror(headline, message)

# End of interface classes

class GalaxClient:
	"The master class for this program."
	
	def __init__(self):
		self.protocol = Protocol(self)		# Comm protocol
		self.interface = Interface(self)	# The GUI
		self.field = Playfield(self)		# Playfield
		self.players = Players(self)		# Players
		self.status = StatusBar(self)		# Status display
		self.year = Year(self)			# Year counter
		self.planetinfo = PlanetInfo(self)	# Planet information
		self.trans = Transfer(self)		# Current transfer
		self.games = Games(self)		# Running games
		self.battles = Battles(self)		# Battles
		self.errors = Errors(self)		# Error messages

		self.status.set("Done loading")		# Just for testing

# And now, what you all have been waiting for: The main program! :)

if __name__ == '__main__':
	client = GalaxClient()			# Create the client
	client.interface.start()		# Start the interface
