import gtk, os, gobject, sys

from zeroinstall.injector.iface_cache import iface_cache
from zeroinstall.injector.policy import Policy
from zeroinstall.injector import download, handler
from zeroinstall.injector.model import SafeException
from zeroinstall.injector.reader import InvalidInterface
import dialog
from checking import CheckingBox

version = '0.29'

# Singleton Policy
policy = None

class GUIHandler(object):
	monitored_downloads = None
	dl_callbacks = None		# Download -> [ callback ]
	pulse = None
	policy = None

	def __init__(self, policy):
		self.policy = policy
		self.monitored_downloads = []
		self.dl_callbacks = {}

	def get_download(self, url, force = False):
		dl = None
		for dl in self.monitored_downloads:
			if dl.url == url:
				if force:
					dl.abort()
					dl = None
				break
		else:
			dl = None
		if dl is None:
			dl = download.Download(url)
			self.monitor_download(dl)
		return dl
	
	def add_dl_callback(self, url, callback):
		"""If url is being downloaded now, call callback() when it completes.
		Otherwise, call callback() now."""
		for dl in self.monitored_downloads:
			if dl.url == url:
				callbacks = self.dl_callbacks.get(dl, [])
				callbacks.append(callback)
				self.dl_callbacks[dl] = callbacks
				return
		callback()

	def monitor_download(self, dl):
		from zeroinstall.injector import download
		name = os.path.basename(dl.url)

		self.monitored_downloads.append(dl)

		error_stream = dl.start()
		def error_ready(src, cond):
			got = os.read(src.fileno(), 100)
			if not got:
				error_stream.close()
				self.monitored_downloads.remove(dl)
				if len(self.monitored_downloads) == 0:
					gobject.source_remove(self.pulse)
					self.policy.window.progress.hide()
					self.pulse = None
				try:
					dl.error_stream_closed()
					if hasattr(download, 'IconDownload') and \
					     isinstance(dl, download.IconDownload):
						if self.policy.window:
							self.policy.window.browser.build_tree()
				except download.DownloadError, ex:
					dialog.alert(self.policy.window,
						"Error downloading '%s':\n\n%s" %
						(name, ex))
				except InvalidInterface, ex:
					dialog.alert(self.policy.window,
						"Syntax error in downloaded interface '%s':\n\n%s" %
						(name, ex))
				except SafeException, ex:
					dialog.alert(self.policy.window,
						"Error fetching '%s':\n\n%s" %
						(name, ex))

				for cb in self.dl_callbacks.get(dl, []):
					cb()

				if len(self.monitored_downloads) == 0 and self.policy.checking:
					self.policy.checking.updates_done(self.policy.versions_changed())
				return False
			dl.error_stream_data(got)
			return True

		gobject.io_add_watch(error_stream,
				     gobject.IO_IN | gobject.IO_HUP,
				     error_ready)

		if self.pulse is None:
			def pulse():
				if self.policy.checking:
					self.policy.checking.progress.pulse()
				else:
					self.policy.window.progress.pulse()
				return True
			self.pulse = gobject.timeout_add(50, pulse)
			self.policy.window.progress.show()

	def confirm_trust_keys(self, interface, sigs, iface_xml):
		import trust_box
		trust_box.confirm_trust(interface, sigs, iface_xml)
	
	def report_error(self, ex):
		dialog.alert(None, str(ex))

class GUIPolicy(Policy):
	window = None
	checking = None		# GtkDialog ("Checking for updates...")
	original_implementation = None
	download_only = None

	def __init__(self, interface, download_only, refresh, src = False, restrictions = None):
		Policy.__init__(self, interface, GUIHandler(self), src = src)
		global policy
		assert policy is None
		policy = self

		if restrictions:
			for r in restrictions:
				self.root_restrictions.append(r)

		self.download_only = download_only

		import mainwindow
		self.window = mainwindow.MainWindow(download_only)
		root = iface_cache.get_interface(self.root)
		self.window.browser.set_root(root)

		if refresh:
			# If we have feeds then treat this as a refresh,
			# even if we've never seen the main interface before.
			# Used the first time the GUI is used, for example.
			if root.name is not None or root.feeds:
				self.checking = CheckingBox(root)

			self.refresh_all(force = False)
	
	def show_details(self):
		"""The checking box has disappeared. Should we show the details window, or
		just run the program right now?"""
		if self.checking.show_details:
			return True		# User clicked on the Details button
		if not self.ready:
			return True		# Not ready to start (can't find an implementation)
		if self.versions_changed():
			return True		# Confirm that the new version should be used
		if self.get_uncached_implementations():
			return True		# Need to download something; check first
		return False

	def store_icon(self, interface, stream):
		Policy.store_icon(self, interface, stream)
		if self.window:
			self.window.browser.build_tree()
	
	def recalculate(self, fetch_stale_interfaces = True):
		Policy.recalculate(self, fetch_stale_interfaces)
		self.window.set_response_sensitive(gtk.RESPONSE_OK, self.ready)

	def main(self):
		if self.checking:
			self.checking.show()
			if not self.handler.monitored_downloads:
				self.checking.updates_done(self.versions_changed())
			dialog.wait_for_no_windows()
			show_details = self.show_details()
			self.checking = None
			if show_details:
				self.window.show()
				gtk.main()
			else:
				import download_box
				download_box.download_with_gui(self.window)
		else:
			self.window.show()
			gtk.main()
	
	def abort_all_downloads(self):
		for x in self.handler.monitored_downloads[:]:
			x.abort()
	
	def set_original_implementations(self):
		assert self.original_implementation is None
		self.original_implementation = policy.implementation.copy()

	def versions_changed(self):
		"""Return whether we have now chosen any different implementations.
		If so, we want to show the dialog to the user to confirm the new ones."""
		if not self.ready:
			return True
		if not self.original_implementation:
			return True		# Shouldn't happen?
		if len(self.original_implementation) != len(self.implementation):
			return True
		for iface in self.original_implementation:
			old = self.original_implementation[iface]
			if old is None:
				return True
			new = self.implementation.get(iface, None)
			if new is None:
				return True
			if old.id != new.id:
				return True
		return False


syntax highlighted by Code2HTML, v. 0.9.1