#!/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
import pango

import time
import webbrowser

from winswitch.consts import APPLICATION_NAME, TYPE_WORKSTATION, TYPE_LAPTOP, TYPE_SERVER, WINSWITCH_VERSION, CHOOSE_SESSION_HELP_URL, SITE_URL, ABOUT_URL, SITE_DOMAIN
from winswitch.globals import KDE, LXDE, WIN32, OSX
from winswitch.util.simple_logger import Logger, msig
from winswitch.util.common import csv_list, no_newlines
from winswitch.util.file_io import load_GPL3
from winswitch.util.process_util import get_output
from winswitch.objects.global_settings import get_settings
from winswitch.objects.server_command import ServerCommand
from winswitch.util.icon_util import get_data_from_icon, get_icon_from_data
from winswitch.util.icon_cache import guess_icon_from_name
from winswitch.ui import icons

PREFER_PYNOTIFY = True



ui_util = None
def get_ui_util():
	global ui_util
	if not ui_util:
		ui_util = UI_Util()
	return	ui_util

class UI_Util:

	def __init__(self):
		Logger(self, log_colour=Logger.HIGHLIGHTED_BLUE)
		#icon/buttons/menu related stuff
		self.default_size = 16
		self.gtk_use_underline = True		#unless it fails...
		self.gpl3 = None
		self.misc_menu = None
		self.small_screen = False
		self.font = None
		self.about_dialog = None
		#figure out if we need to set a smaller font
		self.guess_appropriate_font()
		self.detect_gtk_theme()

	def guess_appropriate_font(self):
		try:
			import gtk.gdk
			w = gtk.gdk.screen_width()
			h = gtk.gdk.screen_height()
			self.small_screen = w<1024 or h<800
			if OSX:
				self.slog("using larger font for OSX")
				if self.small_screen:
					self.font = pango.FontDescription("sans 11")
				else:
					self.font = pango.FontDescription("sans 12")
			elif self.small_screen:
				self.slog("small screen detected: %sx%s, using small font" % (w,h))
				self.font = pango.FontDescription("sans 9")
		except Exception, e:
			self.serr(None, e)

	def detect_gtk_theme(self):
		self.gtk_theme = None
		#try to detect the gtk theme in use:
		if not WIN32:
			try:
				cmd = ["gconftool-2", "-g", "/desktop/gnome/interface/gtk_theme"]
				code, out, _ = get_output(cmd)
				self.sdebug("cmd=%s, out=%s, code=%s" % (cmd, out, code))
				if code==0 and out:
					self.gtk_theme = no_newlines(out)
			except:
				pass
		if self.gtk_theme is None:
			import os
			gtk_env = os.environ.get("GTK2_RC_FILES")
			self.sdebug("GTK2_RC_FILES=%s" % gtk_env)
			#ie: GTK2_RC_FILES="/usr/share/themes/QtCurve/gtk-2.0/gtkrc"
			if gtk_env:
				parts = gtk_env.split("/")
				l = len(parts)
				for i in xrange(l):
					if parts[i]=="themes" and i<l:
						self.gtk_theme = parts[i+1]
					if parts[i]=="gtk-2.0" and i>0:
						self.gtk_theme = parts[i-1]

	def close(self):
		pass

	def _icon(self, name):
		if name is None:
			return	None
		return	icons.get(name)

	def get_best_colour_scheme_icon_name(self):
		if WIN32:
			from winswitch.virt.rdp_common_util import get_WindowsEdition
			edition = get_WindowsEdition()
			if edition.lower().find("vista")>=0:
				return	"winswitch"
			return	"winswitch_grey"
		if OSX or WIN32:
			#looks better on win32 (esp XP which has blue colour scheme), smaller icons show less detail
			#macs are minimalist and don't like colour, black and white so much more "arty"..
			return "winswitch_grey"
		if self.gtk_theme:
			BRIGHT_THEMES = ["clearbooks", "radiance", "amaranth", "crux", "glider",
							"glossy", "guepe", "luxiriant", "mist", "nuvola",
							"sphere", "crystal", "unity"]
			DARK_THEMES = ["ambiance", "high contrast", "high contrast big size",
							"dust", "new wave", "darklooks", "gorilla"]
			if self.gtk_theme.lower() in BRIGHT_THEMES:
				return	"winswitch_applet"				#blue
			if self.gtk_theme.lower() in DARK_THEMES:
				return	"winswitch_indicator"			#transparent
		if LXDE:
			return	"winswitch_grey"
		if KDE:
			return "winswitch_indicator"
		return	"winswitch"


	def set_no_underline(self, menu_item):
		if self.gtk_use_underline:
			try:
				menu_item.set_property("use-underline", False)
			except Exception, e:
				self.gtk_use_underline = False
				self.serror("error: %s, disabling 'use-underline'" % e, menu_item)

	def	get_icon_size(self):
		icon_size = get_settings().menu_size
		if icon_size<0 and self.default_size>0:
			icon_size = self.default_size
		if not icon_size or icon_size<10 or icon_size>100:
			icon_size = 32
		return	icon_size


	def scale_icon(self, icon, icon_size):
		if not icon:
			return	None
		return	icon.scale_simple(icon_size, icon_size, gtk.gdk.INTERP_BILINEAR)

	def scaled_image(self, pixbuf, icon_size=None):
		if not icon_size:
			icon_size = self.get_icon_size()
		return	gtk.image_new_from_pixbuf(pixbuf.scale_simple(icon_size,icon_size,gtk.gdk.INTERP_BILINEAR))

	def make_scaled_image(self, icon_name, icon_size=None):
		return	self.scaled_image(icons.get(icon_name), icon_size)

	def set_menu_image(self, menu_item, image, icon_size=None):
		if image:
			image = self.scaled_image(image, icon_size)
			if image:
				menu_item.set_image(image)

	def make_menuitem(self, title, icon_name=None, tooltip=None, activate_callback=None, button_release=False, icon_size=None):
		return	self.menuitem(title, self._icon(icon_name), tooltip, activate_callback, button_release, icon_size)

	def menuitem(self, title, icon=None, tooltip=None, activate_callback=None, button_release=False, icon_size=None):
		if not self.gtk_use_underline:
			title = title.replace("_", "__")
		menu_item = gtk.ImageMenuItem(title)
		self.set_no_underline(menu_item)
		if not get_settings().icons_in_menus_workaround_applied:
			self.apply_menu_workaround(menu_item.get_settings())
		self.set_menu_image(menu_item, icon, icon_size)
		if get_settings().icons_in_menus and hasattr(menu_item, "set_always_show_image"):
			menu_item.set_always_show_image(True)
		if tooltip:
			menu_item.set_tooltip_text(tooltip)
		if activate_callback:
			if button_release:
				menu_item.connect('button-release-event', activate_callback)
			else:
				menu_item.connect('activate', activate_callback)
		return	menu_item

	def apply_menu_workaround(self, settings):
		try:
			self.sdebug("gtk-menu-images=%s" % settings.get_property("gtk-menu-images"), settings)
		except Exception, e:
			self.serr(None, e, settings)
		get_settings().icons_in_menus_workaround_applied = True
		settings.set_property('gtk-menu-images', get_settings().icons_in_menus)


	def session_type_info_help_button(self):
		return self.make_imagebutton(None, "information", "Help page about session types", self.session_type_help, 24)

	def session_type_help(self, *args):
		webbrowser.open_new_tab(CHOOSE_SESSION_HELP_URL)


	def make_imagebutton(self, title, icon_name, tooltip=None, clicked_callback=None, icon_size=32, default=False, min_size=None):
		icon = self._icon(icon_name)
		return	self.imagebutton(title, icon, tooltip, clicked_callback, icon_size, default, min_size)

	def imagebutton(self, title, icon, tooltip=None, clicked_callback=None, icon_size=32, default=False, min_size=None):
		button = gtk.Button(title)
		if not get_settings().icons_in_buttons_workaround_applied:
			self.apply_imagebutton_workaround(button.get_settings())
		if icon:
			button.set_image(self.scaled_image(icon, icon_size))
		if tooltip:
			button.set_tooltip_text(tooltip)
		if min_size:
			button.set_size_request(min_size, min_size)
		if clicked_callback:
			button.connect("clicked", clicked_callback)
		if default:
			button.set_flags(gtk.CAN_DEFAULT)
		return	button

	def apply_imagebutton_workaround(self, settings):
		try:
			self.sdebug("gtk-button-images=%s" % settings.get_property("gtk-button-images"), settings)
		except Exception, e:
			self.serr(None, e, settings)
		get_settings().icons_in_buttons_workaround_applied = True
		settings.set_property('gtk-button-images', get_settings().icons_in_buttons)

	def make_label(self, title, font=None, selectable=False, line_wrap=False):
		label = gtk.Label(title)
		if font:
			label.modify_font(pango.FontDescription(font))
		else:
			self.setfont(label)
		label.set_selectable(selectable)
		if selectable:
			label.select_region(0,0)
		label.set_line_wrap(line_wrap)
		return	label

	def make_checkbox(self):
		return self.setfont(gtk.CheckButton())

	def text_combo(self):
		return self.setfont(gtk.combo_box_new_text())

	def setfont(self, widget):
		if self.font:
			widget.modify_font(self.font)
		return widget

	def fixfont(self, obj):
		if self.font and issubclass(gtk.Widget, type(obj)):
			self.setfont(obj)

	def make_title_box(self, title):
		box = gtk.VBox(False, 0)
		title_box = gtk.VBox(False, 0)
		title_box.pack_start(self.make_label(title, "sans 16"))
		title_box.set_border_width(10)
		self.add_top_widget(box, title_box, False)
		return box

	def	add_top_widget(self, widget, child, use_vertical_space=True):
		v = 0.0
		if use_vertical_space:
			v = 1.0
		align = gtk.Alignment(0.5, 0.0, 1.0, v)
		align.add(child)
		widget.add(align)


	def add_close_accel(self, window, callback):
		# key accelerators
		accel_group = gtk.AccelGroup()
		accel_group.connect_group(ord('w'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_LOCKED, callback)
		window.add_accel_group(accel_group)
		accel_group = gtk.AccelGroup()
		escape_key, modifier = gtk.accelerator_parse('Escape')
		accel_group.connect_group(escape_key, modifier, gtk.ACCEL_LOCKED |  gtk.ACCEL_VISIBLE, callback)
		window.add_accel_group(accel_group)


	#****************************************************************
	#
	def get_session_window_icon(self, server, session):
		icon = session.get_window_icon()
		if not icon:
			icon = session.get_default_icon()
			if not icon:
				self.sdebug("assigned temporary default_icon using command_uuid=%s or command=%s" % (session.command_uuid, session.command), server, session)
				icon_data = None
				#try from command if we find it
				if len(session.command_uuid):
					cmd = server.get_command_by_uuid(session.command_uuid)
					if cmd:
						icon_data = cmd.get_icon_data()
				if not icon_data:
					icon_data = guess_icon_from_name(session.command)
				if not icon_data:
					icon_data = get_data_from_icon(icons.get("empty"))
				if icon_data:
					session.set_default_icon_data(icon_data)
					icon = get_icon_from_data(icon_data)
		return icon

	def get_user_image(self, user):
		icon = user.get_avatar_icon()
		if not icon:
			icon = icons.get("user")
		return icon

	def get_server_type_icon(self, server_type, default_empty=True):
		if server_type == TYPE_LAPTOP:
			return	icons.get("laptop")
		if server_type == TYPE_WORKSTATION:
			return	icons.get("workstation")
		if server_type == TYPE_SERVER:
			return	icons.get("server")
		self.sdebug("unknown server type!", server_type)
		if default_empty:
			return	icons.get("empty")
		else:
			return	None


	def populate_type_menu(self, option_menu, session_types, selected_type):
		index = 0
		type_menu = gtk.Menu()
		self.setfont(type_menu)
		selected_index = -1
		for session_type in session_types:
			type_menu.append(self.make_menuitem(session_type, session_type, None, None, icon_size=24))
			if session_type == selected_type:
				selected_index = index
			index += 1
		option_menu.set_menu(type_menu)
		if selected_index>=0:
			option_menu.set_history(selected_index)


	def get_command_menu_entry(self, server, command):
		icon = command.get_icon()
		if not icon:
			if not command.missing_data_warning and (time.time()-server.last_sync_sent)>60:
				command.missing_data_warning = True
				self.slog("missing icon!", server, command)
			icon = icons.get("empty")
		tooltip = command.comment
		if get_settings().debug_mode:
			tooltip = "%s\n%s" % (command.comment, command.command)
		entry = self.menuitem(command.name, icon, tooltip, None)
		return	entry

	def get_misc_menu(self):
		if not self.misc_menu:
			self.misc_menu = ServerCommand("MISC", "Misc", None, "Programs that do not fit in the menus above", None)
			self.misc_menu.icon = icons.get("empty")
		return	self.misc_menu

	def	group_commands_in_categories(self, server, commands):
		"""
		Returns the commands sorted in categories, in the form:
		[(Menu-ServerCommand), [Command-ServerCommands])]
		"""
		sig = msig(server, csv_list(commands))
		category_commands = []

		menus = sorted(server.menu_directories)
		for menu_group in menus:
			# find commands for this group
			filtered_commands = []
			for command in commands:
				if command.menu_category == menu_group.name:
					filtered_commands.append(command)
			if len(filtered_commands)==0:
				if not command.missing_data_warning and (time.time()-server.last_sync_sent)>60:
					self.log(sig+" no commands found for menu group: %s" % menu_group)
					command.missing_data_warning = True
				continue
			# remove them from the main list
			for command in filtered_commands:
				commands.remove(command)

			category_commands.append((menu_group, filtered_commands))

		# left overs?
		if len(commands)>0:
			category_commands.append((self.get_misc_menu(), commands))
		return	category_commands









	def get_user_visible_name(self, user, self_suffix=" (yourself)"):
		v = user.name
		if not v:
			v = "unknown"
		is_self = user.uuid==get_settings().uuid
		if get_settings().show_user_host and not is_self and user.host:
			v += " on " + user.host
		if is_self:
			return	v + self_suffix
		return	v




	def true_false_icon(self, boolean):
		size = 16
		if boolean is True:
			icon = icons.get("ticked_small")
		elif boolean is False:
			icon = icons.get("unticked_small")
		else:
			icon = icons.get("unknown")
		return icon.scale_simple(size, size, gtk.gdk.INTERP_HYPER)







	def about(self, *args):
		if self.about_dialog:
			self.about_dialog.show()
			self.about_dialog.present()
			return
		if not self.gpl3:
			self.gpl3 = load_GPL3()
		comments = ''
		try:
			from winswitch.build_info import BUILT_BY, BUILT_ON, BUILD_DATE, REVISION, LOCAL_MODIFICATIONS, RELEASE_BUILD
			if not RELEASE_BUILD:
				comments = 'This is a beta version, use at your own risk.\n'
			comments += "Built on %s by %s. %s" % (BUILT_ON, BUILT_BY, BUILD_DATE)
			if RELEASE_BUILD:
				comments += " (release build)"
			else:
				comments += " (beta build)"
			if int(LOCAL_MODIFICATIONS)==0:
				comments += "\n(svn revision %s)" % REVISION
			else:
				comments += "\n(svn revision %s with %s local changes)" % (REVISION, LOCAL_MODIFICATIONS)
		except Exception, e:
			self.serr("no build information", e, args)

		dialog = gtk.AboutDialog()
		def on_website_hook(dialog, web, *args):
			''' called when the website item is selected '''
			webbrowser.open(ABOUT_URL)
		def on_email_hook(dialog, mail, *args):
			webbrowser.open("mailto://"+mail)
		gtk.about_dialog_set_url_hook(on_website_hook)
		gtk.about_dialog_set_email_hook(on_email_hook)
		dialog.set_name(APPLICATION_NAME)
		dialog.set_version(WINSWITCH_VERSION)
		dialog.set_copyright('Copyright (c) 2009-2012, Nagafix Ltd')
		dialog.set_authors(('Antoine Martin <antoine@nagafix.co.uk>',''))
		dialog.set_artists(('Guillaume Nicolle <guillaume@vcube.fr>',''))
		#dialog.set_artists ([""])
		dialog.set_comments(comments)
		dialog.set_license(str(self.gpl3))
		dialog.set_website(SITE_URL)
		dialog.set_website_label(SITE_DOMAIN)
		dialog.set_logo(icons.get("winswitch_about_logo"))
		dialog.set_program_name(APPLICATION_NAME)
		def response(*args):
			dialog.destroy()
			self.about_dialog = None
		dialog.connect("response", response)
		self.about_dialog = dialog
		dialog.show()





