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

from winswitch.consts import WINDOWS_TYPE, MAGIC_SERVER_PASSWORD
from winswitch.globals import USERNAME
from winswitch.objects.session import Session
from winswitch.virt.server_util_base import ServerUtilBase
from winswitch.ui.icons import getraw



class	LocalWindowsServerUtil(ServerUtilBase):
	"""
	Detects the local display and allows us to export it using RDP.
	"""

	def	__init__(self, config, add_session, remove_session, update_session_status, session_failed):
		ServerUtilBase.__init__(self, WINDOWS_TYPE, 0, config, add_session, remove_session, update_session_status, session_failed)
		self.prelaunch_enabled = False
		self.connecting_timeout = 30
		self.screenshots_enabled = True
		self.default_session_name = "Main Windows Display"

	def get_config_options(self):
		return	self.get_config_options_base()+[
					"# enable screenshots of the desktop",
					"screenshots_enabled",
					"# name of the session shown for the Windows desktop",
					"default_session_name"]

	def can_capture_now(self, session):
		"""
		We override to avoid trying to capture when session is AVAILABLE,
		as this means that the Windows session is locked and this would fail.
		"""
		return session.status in [Session.STATUS_CONNECTED, Session.STATUS_IDLE]

	def detect_existing_sessions(self, reloaded_sessions):
		#delete existing files:
		ServerUtilBase.zap_existing_sessions(self, reloaded_sessions)
		#detect local display if enabled:
		if self.config.export_local_displays:
			self.detect_local_displays()

	def detect_local_displays(self):
		self.sdebug()
		local_sessions = self.config.get_sessions_by_type(WINDOWS_TYPE)
		if len(local_sessions)>0:
			return
		display_size = self.get_gdk_root_size()
		session = self.initialize_new_session(USERNAME, None, False, display_size, "Main", tcp_port=self.config.rdp_port)
		session.start_time = self.get_local_display_start_time()
		session.command = "Windows Login"
		session.set_window_icon_data(getraw("windows"))
		session.name = self.default_session_name
		session.owner = self.get_local_display_owner()
		session.actor = session.owner
		session.user = USERNAME
		session.password = MAGIC_SERVER_PASSWORD
		session.full_desktop = True
		session.shared_desktop = False
		session.session_type = WINDOWS_TYPE
		session.status = Session.STATUS_CONNECTED
		session.can_clone_sound = False			#I don't think it can be done with gst plugins...
		session.can_export_sound = True			#with auto plugin
		session.can_import_sound = True			#with auto plugin
		session.gst_export_plugin = "autoaudiosrc"
		session.gst_export_plugin_options = {}
		session.gst_import_plugin = "autoaudiosink"
		session.gst_import_plugin_options = {}
		session.gst_clone_plugin = ""
		session.gst_clone_plugin_options = {}
		session.restore_actor = ""
		self.save_session(session)
		self.config.add_session(session)
		self.add_session(session)			#send the update session info to all clients
		self.sdebug("added Windows Display session %s" % (session.ID))
		return	True

	def get_local_display_start_time(self):
		try:
			import win32security		#@UnresolvedImport
			sessions = win32security.LsaEnumerateLogonSessions()[:-5]
			for sn in sessions:
				sn_info = win32security.LsaGetLogonSessionData(sn)
				if sn_info['UserName']==USERNAME:
					self.slog("found local session: %s" % sn_info)
					pytime = sn_info.get("LoginTime")
					if pytime:
						return int(pytime)
			self.slog("local session not found, using current time")
		except Exception, e:
			self.serr(None, e)
		return int(time.time())


	def get_local_display_owner(self):
		return	self.get_local_client_ID()

	def get_display_size(self):
		try:
			#http://timgolden.me.uk/python/win32_how_do_i/find-the-screen-resolution.html
			from win32api import GetSystemMetrics		#@UnresolvedImport
			return "%sx%s" % (GetSystemMetrics(0), GetSystemMetrics(1))
		except Exception, e:
			self.serror("failed to get screen size from win32api: %s, trying gtk.gdk" % e)
			try:
				import gtk.gdk
				return "%sx%s" % (gtk.gdk.screen_width(), gtk.gdk.screen_height())
			except Exception, e2:
				self.serror("failed to get screen size using gtk: %s" % e2)
		return	None



	def start_display(self, session, user, is_preload):
		self.serror("not supported!", session, user, is_preload)
		return False
	def stop_display(self, session, user, display):
		self.serror("not supported!", session, user, display)
	def start_session(self, user, server_command, screen_size, opts, filenames, success_callback=None, error_callback=None):
		self.serror("cannot start session!", user, server_command, screen_size, opts, filenames, success_callback, error_callback)

	def can_client_set_status(self, session, user_id, _from, _to):
		"""
		Let the clients tell us about some specific state transitions.
		The windows client tells the server about session events (login/logout/lock/unlock) so we can set status to AVAILABLE when session is locked,
		and back to connected when the session is unlocked.
		However, when sending a session via RDP, the events may fire after the new client has received the session (and told us about it),
		so we need to check the actor (not owner) first if present.
		"""
		self.sdebug("actor=%s, owner=%s" % (session.actor, session.owner), session, user_id, _from, _to)
		if not user_id or (user_id!=session.actor and user_id!=session.owner):		#double check: we shouldn't be here if not actor or owner
			self.slog("now owner or actor!", session, user_id, _from, _to)
			return	False
		if session.actor and session.actor!=user_id:								#actor set and user_id is not the actor: dont allow
			if session.restore_actor==user_id:
				pass		#ignore - it got disconnected
			else:
				self.slog("actor set and does not match!", session, user_id, _from, _to)
			return	False
		changeables = [Session.STATUS_CONNECTING, Session.STATUS_CONNECTED, Session.STATUS_AVAILABLE, Session.STATUS_IDLE]
		return _from in changeables and _to in changeables


	def get_test_port(self, session):
		return	self.config.rdp_port

	def	do_prepare_session_for_attach(self, session, user, disconnect, call_when_done):
		if session.actor==session.owner and session.status in [Session.STATUS_CONNECTED, Session.STATUS_IDLE]:
			"""
			Save actor so we can restore it in attach_failed()
			We only do this when the RDP session is owned by the display owner (local) and we start an RDP
			connection to it which fails. In that case, we want to reset the owner and status=CONNECTED
			"""
			def attach_failed():
				self.sdebug("restoring actor=%s" % session.restore_actor)
				session.actor = session.restore_actor
				self.update_session_status(session, Session.STATUS_CONNECTED)
			session.restore_actor = session.actor
			self.sdebug("saving restore_actor=%s" % (session.restore_actor), session, user, disconnect, call_when_done)
			session.add_status_update_callback(Session.STATUS_CONNECTING, Session.STATUS_AVAILABLE, attach_failed, timeout=None)
		# always assume ready
		call_when_done()








def main():
	from winswitch.objects.server_settings import ServerSettings
	from winswitch.util.config import get_local_server_config
	from winswitch.util.simple_logger import Logger
	logger = Logger("localWindows_server_util")
	ss = get_local_server_config() or ServerSettings()
	def add_session(session):
		logger.sdebug("start_time=%s (%s seconds ago), command=%s" % (session.start_time, time.time()-session.start_time, session.command), session)
	lwin = LocalWindowsServerUtil(ss, add_session, None, None, None)
	lwin.detect_local_displays()

if __name__ == "__main__":
	main()
