#!/usr/bin/env python

import gtk, GDK
import sys
sys.path.insert(0, '/home/tlau/src/fugu')
from pygale import *
import time, re, string

TOXIN_VERSION = 'Toxin/1.0'

def getinstance():
	import socket, os
	dom = pygale.gale_env.get('GALE_DOMAIN', 'unknown domain')
	host = socket.gethostname()
	user = pygale.gale_env.get('GALE_USER', 'unknown user')
	if sys.platform == 'win32':
		display = 'Windows'
	else:
		display = pygale.gale_env.get('DISPLAY', 'unknown display')
	pid = str(os.getpid())
	return '%(dom)s/%(host)s/%(user)s/%(display)s/%(pid)s' % locals()

# Gale protocol client class
class App:
	def __init__(self):
		# Use the Gtk event loop
		pygale.engine.engine = pygale.engine.GtkEngine()

		# Initialize PyGale
		pygale.init()
		# Register shutdown handler
		sys.exitfunc = pygale.shutdown

		# Create a gale client connection
		self.galeconn = pygale.GaleClient()

		self.galeconn.set_onconnect(self.makesub)
		self.galeconn.connect(self.connected)

	def makesub(self, host):
		"subscribe to my personal category"
		if host is None:
			print 'Unable to connect to Gale server'
			sys.exit(0)
		print 'Connected to', host

		# Construct my user category
		mysub = pygale.gale_env.get('GALE_SUBS', None)
		pubsubs = pygale.gale_env.get('GALE_GSUB', None)
		if mysub is not None:
			sub = mysub
		else:
			sub = pygale.id_category(pygale.gale_user(), 'user', '')
		if pubsubs is not None:
			sub = sub + ':' + pubsubs
		self.galeconn.sub_to(sub)
		print 'Subscribed to', sub

		# Set up a callback on new puffs
		self.galeconn.set_puff_callback(self.recvpuff)

	def connected(self, host):
		if host is None:
			print 'Unable to connect to a gale server'
			sys.exit()
		print 'Connected to', host

	def recvpuff(self, puff):
		PuffPop(puff, self.galeconn)

# Pop up a new puff
class PuffPop:
	def __init__(self, puff, galeconn):
		self.puff = puff
		self.galeconn = galeconn
		self._create_widgets()

	def _create_widgets(self):
		self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)

		box = gtk.GtkVBox()
		box.show()
		self.window.add(box)

		self.text = gtk.GtkText()
		self.text.set_editable(gtk.FALSE)
		self.text.set_word_wrap(gtk.TRUE)
		self.text.show()
		box.pack_start(self.text)

		header = '%s on %s\n\n' %\
			(self.puff.get('message/sender', 'Anonymous'),
			self.puff.get_cat())
		self.window.set_title(header)

		self.text.freeze()
		self.text.insert_defaults('From: %s\n' %
			self.puff.get('message/sender', 'Anonymous'))
		if self.puff.get('id/time', None) is not None:
			timestr = 'Time: %s\n' % time.strftime("%l:%M %p %a %m/%d",
                         time.localtime(self.puff.get('id/time')))
			self.text.insert_defaults(timestr)
		bodytext = re.sub('\r\n', '\n', self.puff.get('message/body',
			''))
		self.text.insert_defaults(bodytext)
		self.text.thaw()

		hbox = gtk.GtkHBox()
		hbox.show()
		box.pack_start(hbox)

		replybut = gtk.GtkButton('Reply')
		replybut.connect('clicked', self.reply)
		replybut.show()
		hbox.pack_start(replybut)

		closebut = gtk.GtkButton('Close')
		closebut.connect('clicked', self.window.destroy)
		closebut.show()
		hbox.pack_start(closebut)

		self.window.show()
	
	def reply(self, *args):
		ComposePop(self.galeconn, recp=self.puff.get_signer())

class ComposePop:
	def __init__(self, galeconn, recp=None):
		self.galeconn = galeconn
		self.puff = pygale.Puff()
		self.bad_ids = []
		self.recp_ids = []
		self.cat = []
		self.recp_names = []
		self._create_widgets(recp)

	def _create_widgets(self, recp=None):
		self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)

		box1 = gtk.GtkVBox()
		box1.show()
		self.window.add(box1)

		box2 = gtk.GtkHBox()
		box2.show()
		box1.pack_start(box2)

		label = gtk.GtkLabel('To:')
		label.show()
		box2.pack_start(label)

		self.recipient = gtk.GtkEntry()
		self.recipient.show()
		box2.pack_start(self.recipient)

		self.text = gtk.GtkText()
		self.text.set_editable(gtk.TRUE)
		self.text.set_word_wrap(gtk.TRUE)
		self.text.show()
		box1.pack_start(self.text)

		box3 = gtk.GtkHBox()
		box3.show()
		box1.pack_start(box3)

		sendbut = gtk.GtkButton('Send')
		sendbut.connect('clicked', self.send)
		sendbut.show()
		box3.pack_start(sendbut)

		closebut = gtk.GtkButton('Cancel')
		closebut.connect('clicked', self.window.destroy)
		closebut.show()
		box3.pack_start(closebut)

		if recp:
			self.recipient.set_text(recp)

		self.window.show()
	
	def send(self, *args):
		recplist = string.split(self.recipient.get_text())
		recplist = map(pygale.expand_id, recplist)
		if not recplist:
			m = tkMessageBox.showerror(
				title='No recipients',
				parent=self,
				message='At least one recipient must be specified.')
			return
		self.num_priv_recips = len(recplist)
		for p in recplist:
			pygale.lookup_id(p, lambda i, s=self, p=p:
				s.collect_ids(p, i))
	
	def collect_ids(self, req_id, id_info):
		id, fullname = id_info
		if id is None or fullname is None:
			self.bad_ids.append(req_id)
		else:
			self.cat.append(pygale.id_category(id, 'user', ''))
			self.recp_names.append(fullname)
			self.recp_ids.append(id)
		if len(self.recp_ids) + len(self.bad_ids) == self.num_priv_recips:
			# All ids collected
			self.puff.set_text('message/recipient',
							   string.join(self.recp_names, ','))
			self.send_puff()

	def send_puff(self):
		if self.bad_ids:
			if len(self.bad_ids) == 1:
				msg = 'Cannot find user ' + self.bad_ids[0]
			else:
				msg='Cannot find users:\n' + string.join(self.bad_ids, '\n')
			m = tkMessageBox.showerror(
				title='Error: recipient list',
				parent=self.galewin,
				message=msg)
			return
		self.puff.set_time('id/time', int(time.time()))
		self.puff.set_text('id/instance', getinstance())
		self.puff.set_text('message/sender', gale_env.get('GALE_FROM'))
		self.puff.set_cat(string.join(self.cat, ':'))
		self.puff.set_text('id/class', TOXIN_VERSION)
		self.puff.set_text('message/body',
			re.sub('\n', '\r\n', self.text.get_chars(0, -1) + '\n'))
		out = self.puff.sign_message(pygale.gale_user())
		if out is None:
			# can't sign
			print "Can't sign puff"
			return
		out.encrypt_message(self.recp_ids, lambda puff, s=self:
			s.finish_send(puff))

	def finish_send(self, puff):
		# TODO: check for errors
		self.galeconn.transmit_puff(puff)
		self.window.destroy()

class MainWin:
	def __init__(self, galeconn):
		self._create_widgets()
		self.galeconn = galeconn
	
	def _create_widgets(self):
		self.window = gtk.GtkWindow()
		self.window.set_title('Toxin')
		self.window.connect('destroy', gtk.mainquit)
		self.window.connect('delete_event', gtk.mainquit)

		box = gtk.GtkVBox()
		self.window.add(box)
		box.show()

		new = gtk.GtkButton('Send puff')
		new.connect('clicked', self.newpuff)
		new.show()
		box.pack_start(new)

		close = gtk.GtkButton('Quit Toxin')
		close.connect('clicked', self.confirmquit)
		close.show()
		box.pack_start(close)

		self.window.show()
	
	def confirmquit(self, *args):
		gtk.mainquit()
#		m = tkMessageBox.askyesno(title='Really quit',
#			message='Really leave Toxin?')
#		if m:
#			self.quit()
	
	def newpuff(self, *args):
		ComposePop(self.galeconn)

def main():
	a = App()
	win = MainWin(a.galeconn)
	gtk.mainloop()

if __name__ == '__main__':
	main()

