"""
Convenience routines for performing common operations.
@since: 0.28
"""

# Copyright (C) 2007, Thomas Leonard
# See the README file for details, or visit http://0install.net.

import os, sys, logging
from zeroinstall import support

def get_selections_gui(iface_uri, gui_args, test_callback = None):
	"""Run the GUI to choose and download a set of implementations.
	If the GUI itself is due for a check, refresh it first.
	The user may ask the GUI to submit a bug report about the program. In that case,
	the GUI may ask us to test it. test_callback is called in that case with the implementations
	to be tested; the callback will typically call L{zeroinstall.injector.run.test_selections} and return the result of that.
	@param iface_uri: the required program
	@type iface_uri: str
	@param gui_args: any additional arguments for the GUI itself
	@type gui_args: [str]
	@param test_callback: function to use to try running the program
	@type test_callback: L{zeroinstall.injector.selections.Selections} -> str
	@since: 0.28
	"""
	from zeroinstall.injector import selections, autopolicy, namespaces, model, run, qdom
	from StringIO import StringIO

	gui_policy = autopolicy.AutoPolicy(namespaces.injector_gui_uri)
	if iface_uri != namespaces.injector_gui_uri and (gui_policy.need_download() or gui_policy.stale_feeds):
		# The GUI itself needs updating. Do that first.
		logging.info("The GUI could do with updating first.")
		gui_sel = get_selections_gui(namespaces.injector_gui_uri, ['--refresh'])
		if gui_sel is None:
			logging.info("Aborted at user request")
			return None		# Aborted by user
	else:
		# Try to start the GUI without using the network.
		gui_policy.freshness = 0
		gui_policy.network_use = model.network_offline
		gui_policy.recalculate()
		assert gui_policy.ready		# Should always be some version available
		gui_sel = selections.Selections(gui_policy)

	cli_from_gui, gui_to_cli = os.pipe()		# socket.socketpair() not in Python 2.3 :-(
	gui_from_cli, cli_to_gui = os.pipe()
	try:
		child = os.fork()
		if child == 0:
			# We are the child
			try:
				try:
					os.close(cli_from_gui)
					os.close(cli_to_gui)
					os.dup2(gui_to_cli, 1)
					os.dup2(gui_from_cli, 0)
					run.execute_selections(gui_sel, gui_args + ['--', iface_uri])
				except:
					import traceback
					traceback.print_exc(file = sys.stderr)
			finally:
				sys.stderr.flush()
				os._exit(1)
		os.close(gui_from_cli)
		gui_from_cli = None
		os.close(gui_to_cli)
		gui_to_cli = None

		while True:
			logging.info("Waiting for selections from GUI...")

			reply = support.read_bytes(cli_from_gui, len('Length:') + 9, null_ok = True)
			if reply:
				if not reply.startswith('Length:'):
					raise Exception("Expected Length:, but got %s" % repr(reply))
				xml = support.read_bytes(cli_from_gui, int(reply.split(':', 1)[1], 16))

				dom = qdom.parse(StringIO(xml))
				sels = selections.Selections(dom)

				if dom.getAttribute('run-test'):
					logging.info("Testing program, as requested by GUI...")
					if test_callback is None:
						output = "Can't test: no test_callback was passed to get_selections_gui()\n"
					else:
						output = test_callback(sels)
					logging.info("Sending results to GUI...")
					output = ('Length:%8x\n' % len(output)) + output
					logging.debug("Sending: %s" % `output`)
					while output:
						sent = os.write(cli_to_gui, output)
						output = output[sent:]
					continue
			else:
				sels = None

			pid, status = os.waitpid(child, 0)
			assert pid == child
			if status == 1 << 8:
				logging.info("User cancelled the GUI; aborting")
				return None		# Aborted
			if status != 0:
				raise Exception("Error from GUI: code = %d" % status)
			break
	finally:
		for fd in [cli_to_gui, cli_from_gui, gui_to_cli, gui_from_cli]:
			if fd is not None: os.close(fd)
	
	return sels

def ensure_cached(uri):
	"""Ensure that an implementation of uri is cached.
	If not, it downloads one. It uses the GUI if a display is
	available, or the console otherwise.
	@param uri: the required interface
	@type uri: str
	@return: a new policy for this program, or None if the user cancelled
	@rtype: L{zeroinstall.injector.selections.Selections}
	"""
	from zeroinstall.injector import autopolicy, selections

	p = autopolicy.AutoPolicy(uri, download_only = True)
	p.freshness = 0		# Don't check for updates

	if p.need_download() or not p.ready:
		if os.environ.get('DISPLAY', None):
			return get_selections_gui(uri, [])
		else:
			p.recalculate_with_dl()
			p.start_downloading_impls()
			p.handler.wait_for_downloads()

	return selections.Selections(p)


syntax highlighted by Code2HTML, v. 0.9.1