#!/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 pygtk
pygtk.require("2.0")
import gtk

from winswitch.util.common import get_bool, parse_screensize
from winswitch.virt.options_common import CLIPBOARD, FULLSCREEN, READ_ONLY, TIMEZONE, LOCALE, SCREEN_SIZE


class	ClientOptionsBase:
	"""
	This adds UI methods for providing get_options_widgets()
	used in the *_client_util classes
	"""
	def	__init__(self, session_type, update_session_status, notify_callback):
		pass

	def setup_cb_callback(self, checkbutton, key, callback):
		""" callback(key,value) will be called whenever the checkbutton is (un)ticked """
		if callback and checkbutton:
			def checkbutton_toggled(cb, key, callback):
				value = cb.get_active()
				callback(key, value)
			checkbutton.connect("toggled", checkbutton_toggled, key, callback)

	def setup_combo_callback(self, combo, key, callback, use_index=False, cast_to_int=False, default=0):
		""" Ensures that callback(key,value) is called with the new value
			whenever the selected entry of the combo is changed.
			if use_index is True, the value is the index rather than the text of the selected item
			if cast_to_int is True does what it says and uses 'default' as value if the cast fails
		"""
		if callback:
			def combo_changed(combo, key, callback, use_index, cast_to_int, default):
				if use_index:
					value = combo.get_active()
				else:
					value = combo.get_active_text()
				if cast_to_int:
					try:
						value = int(value)
					except:
						value = default
				callback(key, value)
			combo.connect("changed", combo_changed, key, callback, use_index, cast_to_int, default)


	def get_fullscreen_widget(self, server, options, change_cb, shown=True):
		""" a simple checkbox to enable fullscreen mode """
		cw = None
		if shown:
			cw = gtk.CheckButton()
			cw.set_active(get_bool(options.get(FULLSCREEN)))
			self.setup_cb_callback(cw, FULLSCREEN, change_cb)
		return cw

	def get_clipboard_widget(self, server, options, change_cb, shown=True, enabled=True):
		""" a simple checkbox to enable clipboard sharing """
		cw = None
		if shown:
			cw = gtk.CheckButton()
			cw.set_active(enabled and (CLIPBOARD not in options or get_bool(options.get(CLIPBOARD))))
			cw.set_sensitive(enabled)
			self.setup_cb_callback(cw, CLIPBOARD, change_cb)
		return cw

	def get_readonly_widget(self, server, current_options, change_cb, shown=True):
		""" a simple checkbox make the session readonly """
		cw = None
		if shown:
			cw = gtk.CheckButton()
			cw.set_active(get_bool(current_options.get(READ_ONLY)))
			self.setup_cb_callback(cw, READ_ONLY, change_cb)
		return cw

	def get_timezone_widget(self, server, current_options, tz_options, default_tz, change_cb, shown=True):
		if not shown or len(tz_options)==0:
			return	None
		cb = gtk.combo_box_new_text()
		#find the current value:
		tz_value = current_options.get(TIMEZONE) or default_tz
		#populate drop down:
		index = 0
		active = 0
		for tz in tz_options:
			cb.append_text(tz)
			if tz_value and tz_value==tz:
				active = index
			index += 1
		if active>=0:
			cb.set_active(active)
		self.setup_combo_callback(cb, TIMEZONE, change_cb)
		return cb

	def get_locale_widget(self, server, current_options, default_locale, change_cb, shown=True):
		if not shown or len(server.locales)==0:
			return	None
		def split_locale(loc):
			"""
				"en_US" -> ["en", "US"]
				"en" -> ["en", None]
			"""
			if not loc:
				return	None, None
			p = loc.split("_", 1)
			if len(p)==2:
				return p[0], p[1]
			return	p[0], None
		#group locales, ie: {"en" : ["US", None]}
		locale_groups = {}
		for loc in server.locales:
			main, l = split_locale(loc)
			loc_l = locale_groups.get(main)
			if not loc_l:
				loc_l = []
				locale_groups[main] = loc_l
			if l:
				loc_l.append(l)
		current_locale = current_options.get(LOCALE) or default_locale

		locale_box = gtk.HBox()
		main_cb = gtk.combo_box_new_text()
		sub_cb = gtk.combo_box_new_text()

		def main_locale_changed(main_cb, sub_cb, change_cb):
			""" re-populate the sub combo with the new options """
			loc = main_cb.get_active_text()
			options = locale_groups.get(loc)
			sub_cb.get_model().clear()
			if not options:
				sub_cb.set_sensitive(False)
				sub_cb.hide()
				if change_cb:
					change_cb(LOCALE, loc)
				return
			_, def_opt = split_locale(current_locale)
			sub_cb.show()
			sub_cb.set_sensitive(False)		#prevent change firing too many times
			index = 0
			l_index = 0
			for opt in options:
				sub_cb.append_text(opt)
				if def_opt:
					dl = def_opt.lower().replace("utf-8", "utf8")
					l = opt.lower().replace("utf-8", "utf8")
					if dl==l:
						l_index = index
				index += 1
			sub_cb.set_sensitive(True)
			sub_cb.set_active(l_index)
		def sub_locale_changed(sub_cb, main_cb, change_cb):
			main_loc = main_cb.get_active_text()
			if not sub_cb.get_sensitive():
				if change_cb:
					change_cb(LOCALE, loc)
				return
			sub_loc = sub_cb.get_active_text()
			loc = "%s_%s" % (main_loc, sub_loc)
			if change_cb:
				change_cb(LOCALE, loc)

		main_cb.connect("changed", main_locale_changed, sub_cb, change_cb)
		sub_cb.connect("changed", sub_locale_changed, main_cb, change_cb)

		#populate the main cb, which will populate the secondary one too via main_locale_changed
		def_main, _ = split_locale(current_locale)
		main_cb.get_model().clear()
		main_cb.append_text("")
		index = 1
		l_index = 0
		for main in sorted(locale_groups.keys()):
			main_cb.append_text(main)
			if def_main:
				dl = def_main.lower().replace("utf-8", "utf8")
				l = main.lower().replace("utf-8", "utf8")
				if dl==l:
					l_index = index
			index += 1
		main_cb.set_active(l_index)

		locale_box.add(main_cb)
		locale_box.add(sub_cb)
		return locale_box

	def add_timezone_locale_widgets(self, widgets, settings, server, options, change_cb):
		""" convenience method for adding timezone and locale widgets to the list """
		tz = self.get_timezone_widget(server, options, settings.timezone_options, settings.default_timezone, change_cb, True)
		if tz:
			widgets.append(("Timezone", tz))
		locale = self.get_locale_widget(server, options, settings.locale or server.default_locale, change_cb, True)
		if locale:
			widgets.append(("Locale", locale))

	def get_screen_size_widget(self, server, current_options, desktop_sizes, default_size, bit_depths, default_bit_depth, change_cb, shown=True):
		if not shown or not desktop_sizes or len(desktop_sizes)==0:
			return	None
		ss_box = gtk.HBox()
		screen_size_cb = gtk.combo_box_new_text()
		ss_box.pack_start(screen_size_cb)
		screen_depth_cb = gtk.combo_box_new_text()
		ss_box.pack_start(screen_depth_cb)

		def screen_spec_changed(widget, size_cb, depth_cb):
			def get_selected_screen_size():
				#width/height:
				if not size_cb.get_sensitive():
					return	None
				active = size_cb.get_active()
				if active<=0:
					return	None		#ignore "Default" - first option
				size = parse_screensize(size_cb.get_active_text())
				if not size:
					return	None
				(w,h,_) = size
				return (w,h)
			screen_size = get_selected_screen_size()	#ie: (640,480)
			if not screen_size:
				return	None
			(w,h) = screen_size
			def get_selected_screen_depth():
				if not depth_cb.get_sensitive():
					return	None
				if len(bit_depths)==0:
					return	None
				active = depth_cb.get_active()
				if active<=0:
					return	None		#ignore "Default" - first option
				return	int(depth_cb.get_active_text().split(" ")[0])
			depth = get_selected_screen_depth()					#ie: 32
			if not change_cb:
				return
			if not depth:
				change_cb(SCREEN_SIZE, "%sx%s" % (w, h))								#ie: 640x480
			else:
				change_cb(SCREEN_SIZE, "%sx%sx%s" % (w, h, depth))					#ie: 640x480x32

		screen_size_cb.connect("changed", screen_spec_changed, screen_size_cb, screen_depth_cb)
		screen_depth_cb.connect("changed", screen_spec_changed, screen_size_cb, screen_depth_cb)

		#screen size:
		current_w, current_h, current_depth = -1, -1, -1
		current_size = current_options.get(SCREEN_SIZE) or default_size
		if current_size:
			p = parse_screensize(current_size)
			if p:
				current_w, current_h, current_depth = p

		#populate size:
		index = 0
		size_index = -1
		for (w,h) in desktop_sizes:
			w = int(w)
			h = int(h)
			size = "%s x %s" % (w,h)
			screen_size_cb.append_text(size)
			if (current_w, current_h) == (w, h):
				size_index = index
			index += 1
		if size_index>0:
			screen_size_cb.set_active(size_index)

		#populate depth:
		screen_depth_cb.append_text("Default")
		sensitive = bit_depths is not None and len(bit_depths)>0
		if sensitive:
			index = 1
			depth_index = -1
			for d in bit_depths:
				screen_depth_cb.append_text("%d bits" % d)
				if current_depth and d==current_depth:
					depth_index = index
				index += 1
			if depth_index>0:
				screen_depth_cb.set_active(depth_index)
		screen_depth_cb.set_sensitive(sensitive)
		return ss_box

	def get_desktop_sizes(self, server_command, command_type, shadowed_session):
		return	self.settings.get_desktop_sizes()

	def get_default_desktop_size(self, server_command, command_type, shadowed_session):
		return	self.settings.default_desktop_size

	def get_desktop_bit_depths(self, server_command, command_type, shadowed_session):
		return	self.desktop_bit_depths

	def add_screen_size_widget(self, widgets, server, server_command, shadowed_session, options, change_cb):
		command_type=None
		if server_command:
			command_type = server_command.type
		ss = self.get_screen_size_widget(server, options,
											self.get_desktop_sizes(server_command, command_type, shadowed_session),
											self.get_default_desktop_size(server_command, command_type, shadowed_session),
											self.get_desktop_bit_depths(server_command, command_type, shadowed_session),
											None, change_cb, True)
		if ss:
			widgets.append(("Screen Size", ss))


	def get_options_widgets(self, server, server_command, shadowed_session=None, current_options=None, default_options=None, change_cb=None):
		"""
		Can be used by subclasses to add widgets to start/edit session screens.
		Must return an array of pairs: [(label_string, widget)]
		If defined, change_cb will fire everytime any of the widgets is changed,
		it will be called with the form: change_cb("KEY", value)
		"""
		return	[]
