# -*- coding: utf-8 -*-
"""
UTc! module: login_gui

Purpose
=======
 Provides an interface for allowing users to connect to UTc! servers.
 
Legal
=====
 All code, unless otherwise indicated, is original, and subject to the
 terms of the GPLv3, which is provided in COPYING.
 
 (C) Neil Tallim, 2009
"""
import ConfigParser
import os
import re
import wx

import client
import client_gui

import src.path_utils as path_utils

import src.networking.data_box

_VERSION = "1.1.0" #: The version of UTc! with which this client is compatible.

class LoginGUI(wx.Frame):
	"""
	An interface that allows a user to connect to a UTc! server.
	"""
	def __init__(self, *args, **kwds):
		"""
		wxPython control initialisation routine.
		
		Also sets up event bindings and variables required by this GUI.
		"""
		kwds["style"] = wx.SYSTEM_MENU|wx.MINIMIZE_BOX|wx.CLOSE_BOX|wx.CAPTION
		wx.Frame.__init__(self, *args, **kwds)
		self.pnl_main = wx.Panel(self, style=wx.TAB_TRAVERSAL)
		self.lbl_host = wx.StaticText(self.pnl_main, -1, "Host: ")
		self.txt_host = wx.TextCtrl(self.pnl_main, -1, "")
		self.lbl_port = wx.StaticText(self.pnl_main, -1, "Port: ")
		self.txt_port = wx.TextCtrl(self.pnl_main, -1, "")
		self.lbl_name = wx.StaticText(self.pnl_main, -1, "Handle: ")
		self.txt_name = wx.TextCtrl(self.pnl_main, -1, "")
		self.btn_connect = wx.Button(self.pnl_main, -1, "Connect")
		
		self.__set_properties()
		self.__do_layout()
		self.__load_data()
		
		self.btn_connect.Bind(wx.EVT_BUTTON, self._clickConnectButton)
		self.txt_host.Bind(wx.EVT_TEXT, self._flipConnectButton)
		self.txt_port.Bind(wx.EVT_TEXT, self._flipConnectButton)
		self.txt_name.Bind(wx.EVT_TEXT, self._flipConnectButton)
		
	def Destroy(self):
		"""
		Saves this window's values to ~/.UTc!/login.cfg before being destroyed.
		"""
		options = ConfigParser.RawConfigParser()
		
		options.add_section("meta")
		options.set("meta", "schema-version", "1")
		
		options.add_section("login")
		options.set("login", "host", self.txt_host.GetValue())
		options.set("login", "port", self.txt_port.GetValue())
		options.set("login", "handle", self.txt_name.GetValue())
		
		options.write(open(path_utils.getUserPath() + "login.cfg", "w"))
		
		wx.Frame.Destroy(self)
		
	def _clickConnectButton(self, event):
		"""
		Attempts to use the provided information to connect to a UTc! server.
		
		If login was successful, this window will dissappear, being replaced by
		the full lobby interface. Otherwise, an error message will be displayed.
		
		@type event: wx.PyEvent
		@param event: The event that triggered this function.
		"""
		if not self._allowConnect():
			return
			
		server = None
		team_id = None
		players = []
		try:
			data_box = src.networking.data_box.DataBox()
			server = client.Socket(self.txt_host.GetValue(), int(self.txt_port.GetValue()), data_box)
			server.forceData("%s %s" % (self.txt_name.GetValue(), _VERSION))
			server.readData(True)
			(code, payload) = data_box.getLine().split(None, 1)
			if not code == "PASS":
				raise ValueError("validation failed: %s" % payload)
			team_id = int(payload.split(None, 1)[1])
			
			plyr_regexp = re.compile(r"PLYR (\w+) (\d+)")
			while True:
				server.readData(True)
				line = data_box.getLine()
				if line == "LEND":
					break
				match = plyr_regexp.match(line)
				if line:
					players.append((match.group(1), int(match.group(2))))
		except Exception, e:
			wx.MessageDialog(self, "Unable to connect to server: %s" % (e), "Connection error", wx.OK).ShowModal()
			return
			
		_client_gui = client_gui.ClientGUI(None, -1, "")
		_client_gui.setConnection(self, server, self.txt_host.GetValue(), self.txt_name.GetValue(), team_id, players)
		self.Hide()
		_client_gui.Show()
		
	def _flipConnectButton(self, event):
		"""
		Enables the connect button if the user's input values are valid.
		
		@type event: wx.PyEvent
		@param event: The event that triggered this function.
		"""
		if self._allowConnect():
			self.btn_connect.Enable(True)
		else:
			self.btn_connect.Enable(False)
			
	def _allowConnect(self):
		"""
		Determines whether the user's input values are valid before allowing
		the user to connect to a UTc! server.
		
		@rtype: bool
		@return: True if the user should be allowed to connect.
		"""
		return not re.search("[ ]", self.txt_host.GetValue()) and re.match("^\d+$", self.txt_port.GetValue()) and re.match("^\w+$", self.txt_name.GetValue())
		
	def __set_properties(self):
		"""
		wxPython control configuration routine.
		"""
		self.SetTitle("Ultra Tank-chan!")
		self.CentreOnScreen()
		self.btn_connect.SetDefault()
		self.btn_connect.Enable(False)
		
	def __do_layout(self):
		"""
		wxPython control layout routine.
		"""
		szv_main = wx.BoxSizer(wx.VERTICAL)
		szh_name = wx.BoxSizer(wx.HORIZONTAL)
		szh_address = wx.BoxSizer(wx.HORIZONTAL)
		szh_port = wx.BoxSizer(wx.HORIZONTAL)
		szh_host = wx.BoxSizer(wx.HORIZONTAL)
		szh_host.Add(self.lbl_host, 0, wx.ALIGN_CENTER_VERTICAL, 0)
		szh_host.Add(self.txt_host, 1, wx.EXPAND, 0)
		szh_address.Add(szh_host, 1, wx.EXPAND, 0)
		szh_port.Add(self.lbl_port, 0, wx.ALIGN_CENTER_VERTICAL, 0)
		szh_port.Add(self.txt_port, 0, wx.EXPAND, 0)
		szh_address.Add(szh_port, 0, wx.EXPAND, 0)
		szv_main.Add(szh_address, 1, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 2)
		szh_name.Add(self.lbl_name, 0, wx.ALIGN_CENTER_VERTICAL, 0)
		szh_name.Add(self.txt_name, 1, wx.EXPAND, 0)
		szh_name.Add((0, 0), 1, wx.EXPAND, 0)
		szv_main.Add(szh_name, 1, wx.LEFT|wx.RIGHT|wx.EXPAND, 2)
		szv_main.Add((0, 2), 0, wx.EXPAND, 0)
		szv_main.Add(self.btn_connect, 0, wx.LEFT|wx.RIGHT|wx.EXPAND, 2)
		self.pnl_main.SetSizer(szv_main)
		self.pnl_main.Layout()
		szv_main.Fit(self)
		
	def __load_data(self):
		"""
		Attempts to load the user's last-used values from ~/.UTc!/login.cfg,
		falling back to defaults if the file does not exist.
		"""
		options = ConfigParser.RawConfigParser()
		if not (options.read(path_utils.getUserPath() + "login.cfg") and options.has_section("meta") and int(options.get("meta", "schema-version")) == 1):
			self.txt_host.SetValue("localhost")
			self.txt_port.SetValue("64775")
			self.txt_name.SetValue("")
		else:
			self.txt_host.SetValue(options.get("login", "host"))
			self.txt_port.SetValue(options.get("login", "port"))
			self.txt_name.SetValue(options.get("login", "handle"))
		self._flipConnectButton(None)
		
