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

from winswitch.consts import GSTVIDEO_TYPE, GSTVIDEO_PORT_BASE, X11_TYPE, NX_TYPE, VNC_TYPE, WINDOWS_TYPE, OSX_TYPE
from winswitch.objects.session import Session
from winswitch.virt.server_util_base import ServerUtilBase
from winswitch.util.common import parse_screensize, format_screensize
from winswitch.util.gstreamer_util import get_hidden_wrapper_command, get_launch_wrapper_command_for_pipeline, get_queue2_max_size_time_str, get_default_gst_video_source_module
from winswitch.virt.options_common import FRAMERATE, GST_VIDEOSCALE_METHOD, GST_VIDEO_CODEC
from winswitch.util.main_loop import callLater


class	GSTVideoServerUtil(ServerUtilBase):

	PIPELINE_SIGNAL = re.compile(r"^\[II\]\s.*\sgst_launch_wrapper.onsignal_exit\(\)")
	PIPELINE_STARTING = re.compile(r"^\[II\]\s.*\sgst_launch_wrapper.start\(\) starting")
	PIPELINE_STARTED = re.compile(r"^\[II\]\s.*\sgst_launch_wrapper.start\(\) started")

	IGNORE_RES = []

	def	__init__(self, config, add_session, remove_session, update_session_status, session_failed):
		ServerUtilBase.__init__(self, GSTVIDEO_TYPE, GSTVIDEO_PORT_BASE, config, add_session, remove_session, update_session_status, session_failed)
		self.connecting_timeout = 20			#20 seconds to go from connecting to connected
		self.default_screen_scaledown = 4
		self.prelaunch_enabled = False

	def get_config_options(self):
		return	self.get_config_options_base(detect=False, log=True, start=True)+["default_screen_scaledown"]

	def get_unique_shadow(self, session, user, read_only):
		return	None		#always create a new one

	def can_client_set_status(self, session, user_id, _from, _to):
		"""
		Only the client knows if it has successfully connected:
		"""
		return	ServerUtilBase.can_client_set_status(self, session, user_id, _from, _to) \
			or (_from==Session.STATUS_AVAILABLE and _to==Session.STATUS_CONNECTING) \
			or (_from==Session.STATUS_AVAILABLE and _to==Session.STATUS_CONNECTED) \
			or (_from==Session.STATUS_CONNECTING and _to==Session.STATUS_CONNECTED) \
			or (_from==Session.STATUS_CONNECTED and _to==Session.STATUS_AVAILABLE)

	def get_test_port(self, session):
		return	session.port

	def create_shadow_session(self, session, user, read_only, screen_size, options):
		"""
		Override so we can validate the session we are supposed to shadow.
		"""
		assert session.session_type in [VNC_TYPE, NX_TYPE, X11_TYPE, WINDOWS_TYPE, OSX_TYPE]
		shadow = ServerUtilBase.create_shadow_session(self, session, user, read_only, screen_size, options)
		if screen_size:
			shadow.screen_size = screen_size
		else:
			parsed = parse_screensize(shadow.screen_size)
			if parsed and self.default_screen_scaledown!=1:
				(w, h, d) = parsed
				w = int(w/self.default_screen_scaledown)
				h = int(h/self.default_screen_scaledown)
				shadow.screen_size = format_screensize(w,h,d)
		return	shadow

	def start_display(self, session, user, is_preload):
		self.sdebug(None, session, user, is_preload)
		assert session.shadowed_display
		#stop the shadow as soon as client disconnects
		def close_on_suspend():
			self.slog()
			self.stop_display(session, user, session.display)
		session.add_status_update_callback(Session.STATUS_CONNECTED, Session.STATUS_AVAILABLE, close_on_suspend, clear_it=True, timeout=None)
		#ensure client connects to it, or we stop it:
		def check_connected():
			if session.status==Session.STATUS_CLOSED:
				self.sdebug("session already closed")
				return
			if session.status!=Session.STATUS_CONNECTED:
				self.slog("session not CONNECTED, stopping it")
				self.stop_display(session, user, session.display)
			else:
				self.sdebug("session CONNECTED ok")
		callLater(30, check_connected)

		# client can specify framerate:
		framerate = 5
		if FRAMERATE in session.options:
			framerate = int(session.options.get(FRAMERATE))
		framerate_str = "%s/1" % framerate

		video_src = self.config.gstvideo_src_plugin or get_default_gst_video_source_module()
		if not video_src:
			raise Exception("cannot start GStreamer video, no video source plugin found!")
		pipeline = [video_src,
				"video/x-raw-rgb,framerate=%s" % framerate_str,
				]
		parsed = parse_screensize(session.screen_size)
		if parsed:
			(w, h, _) = parsed
			if w and h:
				videoscale_method = -1
				if GST_VIDEOSCALE_METHOD in session.options:
					videoscale_method = int(session.options.get(GST_VIDEOSCALE_METHOD))
				#self.sdebug("videoscale_method=%s, session.options=%s" % (videoscale_method, session.options), session, user, is_preload)
				if videoscale_method<0:
					pipeline.append("videoscale")
				else:
					pipeline.append("videoscale method=%s" % videoscale_method)
				pipeline.append("video/x-raw-rgb,width=%s,height=%s" % (w,h))
		codec = session.options.get(GST_VIDEO_CODEC, None)
		if codec:
			pipeline += ["ffmpegcolorspace"]
			pipeline += ["%senc" % codec]
		pipeline += ["gdppay",
					"queue2 %s" % get_queue2_max_size_time_str(user.line_speed),
					"tcpserversink port=%s host=%s" % (session.port, session.host)]
		cmd_args_list = get_hidden_wrapper_command(False, True)
		env = session.get_env()
		args = get_launch_wrapper_command_for_pipeline(cmd_args_list, True, self.get_log_file(session), session.name, pipeline, env)
		def check_session_started(*args):
			self.sdebug("session=%s" % session, *args)
			if session.status==Session.STATUS_STARTING:
				self.early_failure(session, "Session failed to start (timeout)", Session.STATUS_CLOSED)
		callLater(30, check_session_started)
		return self.start_daemon(session, args, env, use_daemon_wrapper=False)

	def process_log_line(self, session, line):
		self.sdebug(None, session, line)
		if not line or line=='\n':
			return
		for ignore in GSTVideoServerUtil.IGNORE_RES:
			if ignore.match(line):
				return
		trimmed = line.strip()
		if GSTVideoServerUtil.PIPELINE_SIGNAL.match(trimmed):
			return	Session.STATUS_CLOSED
		elif GSTVideoServerUtil.PIPELINE_STARTING.match(trimmed):
			return	Session.STATUS_STARTING
		elif GSTVideoServerUtil.PIPELINE_STARTED.match(trimmed):
			return	Session.STATUS_AVAILABLE
		return	None
