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

from winswitch.util.common import is_valid_file
from winswitch.util.paths import ICON_DIR
from winswitch.ui.tray_util import BaseTray
from winswitch.ui.win32_NotifyIcon import win32NotifyIcon

DETECT_SESSION_EVENTS = True

class Win32Tray(BaseTray):

	def __init__(self, popup_menu, activate_menu, exit_cb, ask_quit, handle_session_event, default_icon_name):
		BaseTray.__init__(self, popup_menu, activate_menu, exit_cb, ask_quit, default_icon_name)
		#now let's try to hook the session notification
		if DETECT_SESSION_EVENTS:
			self.detect_win32_session_events(self.getHWND(), handle_session_event)
		self.balloon_click_callback = None

	def setup_widget(self):
		icon_path = self.get_icon_path(self.default_icon_name)
		self.tray_widget = win32NotifyIcon(self.activate_menu, self.exit, None, icon_path)

	def getHWND(self):
		return	self.tray_widget.hwnd

	def close(self):
		self.tray_widget.close()

	def get_icon_path(self, icon_name):
		icon_path = os.path.join(ICON_DIR, "%s.ico" % icon_name)
		if not is_valid_file(icon_path):
			self.serr("icon not found at %s" % icon_path, icon_name)
		return	icon_path

	def set_icon(self, icon_name):
		icon_path = self.get_icon_path(icon_name)
		self.tray_widget.set_icon(icon_path)

	def position_menu(self, menu, tray_widget):
		try:
			position = self.get_tray_position()
			return	BaseTray.validate_position(self, menu, position)
		except Exception, e:
			self.serr(None, e, menu)
		return	None

	def get_size(self):
		geom = self.get_tray_geometry()
		if not geom:
			return	None
		self.slog("geom=%s" % str(geom))
		(left, top, right, bottom) = geom
		# this is the size of the whole thing (containing many icons), not just our icon
		# so we want the smallest dimension width or height (depending on where the bar is: top/bottom edge or right/left edge)
		# substract 10 as there are borders around it
		return	max(16, min(bottom-top, right-left, 48) - 10)

	def get_tray_geometry(self):
		tray_hwnd = self.get_shell_tray_window()
		if not tray_hwnd:
			return	None
		from win32gui import GetWindowRect		#@UnresolvedImport
		geom = GetWindowRect(tray_hwnd)
		return	geom

	def get_shell_tray_window(self):
		import win32gui			                #@UnresolvedImport
		tray_window = win32gui.FindWindow("Shell_TrayWnd", None)
		if not tray_window:
			return	None
		tray_notify_window = win32gui.FindWindowEx(tray_window, 0, "TrayNotifyWnd", None)
		return	tray_notify_window

	def get_tray_position(self):
		geom = self.get_tray_geometry()
		self.sdebug("tray_hwnd geometry=%s" % str(geom))
		x = 100
		y = 100
		if geom:
			(left, top, right, bottom) = geom
			x = (left+right)/2
			if top>100 or bottom>100:
				""" not near the top of the screen, so attach to the top of the tray """
				y = top
			else:
				""" attach to the bottom of the widget """
				y = bottom
		else:
			geom = self.tray_widget.get_geometry()
			self.sdebug("tray_widget.get_geometry()=%s" % str(geom))
			if geom:
				x = right
				y = bottom
		self.sdebug("=%dx%d,True" % (x,y))
		return (x, y, True)


	#****************************************************************
	# Events detection (screensaver / login / logout)
	def detect_win32_session_events(self, app_hwnd, handle_session_event):
		"""
		Use pywin32 to receive session notification events.
		"""
		self.slog(None, app_hwnd, handle_session_event)
		try:
			import win32ts, win32con, win32api, win32gui		#@UnresolvedImport
			WM_TRAYICON = win32con.WM_USER + 20
			NIN_BALLOONSHOW = win32con.WM_USER + 2
			NIN_BALLOONHIDE = win32con.WM_USER + 3
			NIN_BALLOONTIMEOUT = win32con.WM_USER + 4
			NIN_BALLOONUSERCLICK = win32con.WM_USER + 5
			#register our interest in those events:
			#http://timgolden.me.uk/python/win32_how_do_i/track-session-events.html#isenslogon
			#http://stackoverflow.com/questions/365058/detect-windows-logout-in-python
			#http://msdn.microsoft.com/en-us/library/aa383841.aspx
			#http://msdn.microsoft.com/en-us/library/aa383828.aspx
			win32ts.WTSRegisterSessionNotification(app_hwnd, win32ts.NOTIFY_FOR_THIS_SESSION)
			#catch all events: http://wiki.wxpython.org/HookingTheWndProc
			def MyWndProc(hWnd, msg, wParam, lParam):
				#from the web!: WM_WTSSESSION_CHANGE is 0x02b1.
				if msg==0x02b1:
					self.slog("Session state change!", hWnd, msg, wParam, lParam)
					if handle_session_event:
						try:
							handle_session_event(wParam)
						except Exception, e:
							self.serr("error calling %s" % handle_session_event, e, hWnd, wParam, lParam)
				elif msg==win32con.WM_DESTROY:
					# Restore the old WndProc
					self.slog("WM_DESTROY, restoring call handler", hWnd, msg, wParam, lParam)
					win32api.SetWindowLong(app_hwnd, win32con.GWL_WNDPROC, self.oldWndProc)
				elif msg==win32con.WM_COMMAND:
					self.slog("WM_COMMAND", hWnd, msg, wParam, lParam)
				elif msg==WM_TRAYICON:
					self.sdebug("WM_TRAYICON", hWnd, msg, wParam, lParam)
					if lParam==NIN_BALLOONSHOW:
						self.slog("NIN_BALLOONSHOW", hWnd, msg, wParam, lParam)
					if lParam==NIN_BALLOONHIDE:
						self.slog("NIN_BALLOONHIDE", hWnd, msg, wParam, lParam)
						self.balloon_click_callback = None
					elif lParam==NIN_BALLOONTIMEOUT:
						self.slog("NIN_BALLOONTIMEOUT", hWnd, msg, wParam, lParam)
					elif lParam==NIN_BALLOONUSERCLICK:
						self.slog("NIN_BALLOONUSERCLICK, balloon_click_callback=%s" % self.balloon_click_callback, hWnd, msg, wParam, lParam)
						if self.balloon_click_callback:
							self.balloon_click_callback()
							self.balloon_click_callback = None
				else:
					self.sdebug(None, hWnd, msg, wParam, lParam)
				# Pass all messages to the original WndProc
				try:
					return win32gui.CallWindowProc(self.old_win32_proc, hWnd, msg, wParam, lParam)
				except Exception, e:
					self.serr(None, e, hWnd, msg, wParam, lParam)
			self.old_win32_proc = win32gui.SetWindowLong(app_hwnd, win32con.GWL_WNDPROC, MyWndProc)
		except Exception, e:
			self.serr("failed to hook session notifications..", e, app_hwnd, handle_session_event)
