#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import os, time, re, sys

from winswitch.consts import XPRA_TYPE, DEFAULT_LAN_SPEED, MODEM_56K_SPEED, DSL_256K_SPEED
from winswitch.globals import USERNAME, OSX, WIN32
from winswitch.util.common import save_binary_file, get_bool
from winswitch.util.file_io import get_client_session_password_file
from winswitch.virt.client_util_base import ClientUtilBase
from winswitch.virt.xpra_common import xpra_cmd
from winswitch.virt.options_common import CLIPBOARD, ENCODING, KEYBOARD_SYNC, READ_ONLY, MMAP
from winswitch.objects.server_command import ServerCommand
from winswitch.util.main_loop import callLater

XPRA_DEBUG_OPTION="--debug-xpra" in sys.argv
# enable the auto-refresh when idle code:
AUTO_REFRESH_DELAY = True


class	XpraClientBase(ClientUtilBase):

	CONNECTED_RE = 			re.compile(r"Attached.*\(press Control-C to detach\)")
	CONNECTION_TIMEOUT_RE =	re.compile(r"Failed to connect to socket: timed out")
	CONNECTION_ENDED_RE =	re.compile(r"Connection lost")

	def	__init__(self, update_session_status, notify_callback):
		ClientUtilBase.__init__(self, XPRA_TYPE, update_session_status, notify_callback)
		#client output parsing:
		self.client_log_actions[XpraClientBase.CONNECTED_RE] 			= self.handle_line_log
		self.client_log_actions[XpraClientBase.CONNECTION_TIMEOUT_RE]	= self.handle_line_connect_timeout
		self.client_log_actions[XpraClientBase.CONNECTION_ENDED_RE]		= self.handle_line_log

	def client_detach_session(self, server, client, session):
		""" override so we can ask the server to disconnect cleanly,
			this is needed on win32 where signalProcess("SIGINT") ends up
			calling win32process.TerminateProcess which does not give the
			client time to exit cleanly (and tidy up the tray)
			We only do this if the remote end supports the disconnect call
			otherwise we just kill_client_processes as before.
		"""
		self.slog(None, server, client, session)
		try:
			if client.handler.remote_app_version>=[0,12,10]:
				session.kill_client = True		#prevent error in client process exit path
				processes = session.processes
				client_pid = session.client_pid
				client.handler.send_disconnect_session(session.ID)
				def kill_anyway():
					self.do_kill_client_processes(session, processes, client_pid)
				callLater(2, kill_anyway)
				return
		except:
			pass
		self.kill_client_processes(session)


	def get_default_compression(self, server, server_command):
		""" guess what the default compression should be: use line speed for remote video commands, otherwise lossless """
		c = 0
		if server_command and server.supports_xpra_encodings and (server_command.uses_video or server_command.type==ServerCommand.DESKTOP):
			self.sdebug("non local server and command uses_video", server, server_command)
			for x in ["x264", "vpx", "jpeg"]:
				if x in server.supports_xpra_encodings:
					return x
		self.sdebug("=%s" % c, server, server_command)
		return	c


	def do_real_attach(self, server, session, host, port):
		self.sdebug("options=%s" % session.options, server, session, host, port)
		xpra_args_list = ["--title=@title@  (on %s)" % server.get_display_name()]
		if XPRA_DEBUG_OPTION:
			xpra_args_list.append('-d all')
		if session.name:
			xpra_args_list.append('--session-name=%s' % session.name)
		if session.password:
			password_file = get_client_session_password_file(session.ID)
			save_binary_file(password_file, session.password)
			xpra_args_list.append("--password-file=%s" % password_file)
		clipboard = session.options.get(CLIPBOARD)
		if clipboard is not None and not get_bool(clipboard):
			xpra_args_list.append("--no-clipboard")
		if server.line_speed<=MODEM_56K_SPEED:
			xpra_args_list.append("-z7")
		if server.line_speed<=DSL_256K_SPEED:
			xpra_args_list.append("-z5")
		if server.line_speed>=DEFAULT_LAN_SPEED:
			xpra_args_list.append("-z1")

		if not get_bool(session.options.get(MMAP, True)):
			xpra_args_list.append("--no-mmap")

		encoding_and_options = session.options.get(ENCODING, "")
		encoding = encoding_and_options.split(":")[0]
		if encoding and encoding in server.supports_xpra_encodings:
			xpra_args_list.append("--encoding=%s" % encoding)
			if encoding=="jpeg":
				try:
					quality = encoding_and_options.split(":")[1]
					q = int(quality)
					if q!=0:
						if q>0:
							xpra_args_list.append("--quality=%s" % q)
						if AUTO_REFRESH_DELAY:
							xpra_args_list += ["--auto-refresh-delay=%s" % self.get_auto_refresh_delay(q, server.line_speed)]
				except:
					pass
		if not get_bool(session.options.get(KEYBOARD_SYNC, True)):
			xpra_args_list.append("--no-keyboard-sync")
		if get_bool(session.options.get(READ_ONLY, False)):
			xpra_args_list.append("--readonly")
		window_icon = self.get_window_icon(session)
		if window_icon:
			xpra_args_list.append("--window-icon=%s" % window_icon)
			if OSX:
				xpra_args_list.append("--tray-icon=%s" % window_icon)
		#hide the tray if preloading (only supported on posix with 0.6 onwards):
		self.slog("pre_connected=%s, preload=%s, has min=%s" % (session.pre_connected, session.preload, self.has_min_xpra_version([0, 6])))
		if os.name=="posix" and (session.pre_connected or session.preload) and self.has_min_xpra_version([0, 6]):
			xpra_args_list.append("--delay-tray")

		xpra_args_list += ["attach", "tcp:%s:%s" % (host, port)]
		args = xpra_cmd(USERNAME, self.settings.xpra_command, xpra_args_list)
		retry = (time.time() - session.start_time) < 30		#retry to connect if the session is new
		self.exec_client(server, session, args, connect_fail_retry=retry)

	def get_auto_refresh_delay(self, quality, line_speed):
		ls = int(line_speed)/1000
		if quality<0:
			""" quality was given for max-bandwidth in KB/s, so use that! """
			ls = -quality
		if ls<100:			#modem
			return	5
		if ls<1000:			#isdn
			return	2
		ls = int(ls/1000)
		if ls<10:			#wan
			return	1
		if ls<1000:			#lan
			return	0.5
		return	0.2

	def get_options_defaults(self):
		opts = {
				READ_ONLY : False,
				ENCODING : "",
				}
		if not OSX and not WIN32:
			opts[MMAP] = True
			opts[CLIPBOARD] = True
			opts[KEYBOARD_SYNC] = True
		return opts

	def has_min_xpra_version(self, min_version):
		if not self.settings.xpra_version:
			return False
		try:
			numver = [int(x) for x in self.settings.xpra_version.split(".")]
			return numver>=min_version
		except:
			pass
		return False
