#! /usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os.path

import pygst
pygst.require ("0.10")
import gst

import pygtk
pygtk.require ("2.0")
import gobject

mainloop = gobject.MainLoop ()

class Looper (gobject.GObject):
	__gsignals__ = {
		"stopped": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
		"position-updated": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
		"error": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
	}
	
	def __init__ (self, location = None):
		gobject.GObject.__init__ (self)
		self.__playbin = gst.element_factory_make ("playbin")
		
		self.__timeout_id = 0
		self.loop = False
		
		self.__start_position = 0
		self.__stop_position = 0
		
		bus = self.__playbin.get_bus ()
		bus.add_watch (self.__on_bus_message)
		
		if location:
			self.load (location)
	
	def load (self, location):
		self.__playbin.set_property ("uri", location)
		self.__start_position = 0
		self.__stop_position = 0
	
	def set_segment (self, start, stop):
		# FIXME: should we validate "start < stop" and "> duration" here?
		self.__start_position = start
		self.__stop_position = stop
	
	def play (self):
		# FIXME: currently raise an error when segment is not available
		if not (self.__start_position or self.__stop_position):
			raise RuntimeError, "Cannot start playback, segment was not set!"
		self.__playbin.set_state (gst.STATE_PLAYING)
	
	#def pause (self):
	#	self.__playbin.set_state (gst.STATE_PAUSED)
	
	def stop (self, silent = False):
		self.__playbin.set_state (gst.STATE_NULL)
		if not silent:
			self.emit ("stopped")
	
	def is_playing (self):
		state_change, state, pending = self.__playbin.get_state ()
		# FIXME: we need to provide a better check here (and remember about the PAUSED state)
		return state == gst.STATE_PLAYING
	
	def do_stopped (self):
		if self.__timeout_id:
			gobject.source_remove (self.__timeout_id)
			self.__timeout_id = 0
	
	def __seek (self, start, stop, flush):
		# It looks like the ACCURATE mode is not needed right now
		flags = gst.SEEK_FLAG_SEGMENT #| gst.SEEK_FLAG_ACCURATE
		if flush:
			flags = flags | gst.SEEK_FLAG_FLUSH
		self.__playbin.seek (1.0, gst.FORMAT_TIME, flags,
			gst.SEEK_TYPE_SET, start,
			gst.SEEK_TYPE_SET, stop)
	
	def __on_timeout (self):
		# this fails sometimes, so let's put this in a try / except block for extra safety
		try:
			position = self.__playbin.query_position (gst.FORMAT_TIME) [0]
			self.emit ("position-updated", float (position))
		except:
			pass
		
		return True
	
	def __on_bus_message (self, bus, message):
		if message.type == gst.MESSAGE_ERROR:
			error, debug = message.parse_error ()
			self.stop () # FIXME: is this neccessary here?
			self.emit ("error", (error, debug))
		
		elif message.type == gst.MESSAGE_NEW_CLOCK:
			# we connect the timeout handler here to be sure that further queries succeed
			# interval = int ((self.__stop_position - self.__start_position) / (2 * gst.SECOND) + 50)
			interval = 200
			
			self.__timeout_id = gobject.timeout_add (interval, self.__on_timeout)
			self.__seek (self.__start_position, self.__stop_position, True)
		
		elif message.type == gst.MESSAGE_SEGMENT_DONE:
			print ">> Caught SEGMENT_DONE!"
			if self.loop:
				print ">> Continuing looping..."
				self.__seek (self.__start_position, self.__stop_position, False)
			else:
				print ">> Sending EOS from source..."
				src = self.__playbin.get_property ("source")
				pad = src.get_pad ('src')
				pad.push_event (gst.event_new_eos ())
				
				#self.__playbin.send_event (gst.event_new_flush_start ())
				#self.__playbin.send_event (gst.event_new_eos ())
				
				# # the good old way:
				# pads = src.src_pads ()
				# while True:
				# 	try:
				# 		pad = pads.next ()
				# 		pad.push_event (gst.event_new_eos ())
				# 	except:
				# 		break
		
		elif message.type == gst.MESSAGE_EOS:
			print ">> Caught EOS!"
			self.stop ()
		
		return True

def on_looper_stopped (looper):
	print ">>> Stopped!"
	mainloop.quit ()

def on_looper_pos_updated (looper, position):
	print position / gst.SECOND

def on_looper_error (looper, error_tuple):
	error, debug = error_tuple
	print error
	print debug
	mainloop.quit ()

if __name__ == "__main__":
	if len (sys.argv) != 5:
		print "Usage: segment-test.py <filename> <start_seconds> <stop_seconds> <loop = 0|1>"
		sys.exit (1)
	uri = "file://" + os.path.abspath (sys.argv [1])
	start = float (sys.argv [2]) * gst.SECOND
	stop = float (sys.argv [3]) * gst.SECOND
	loop = int (sys.argv [4])
	
	looper = Looper (uri)
	looper.set_segment (start, stop)
	looper.loop = loop
	
	looper.connect ("stopped", on_looper_stopped)
	looper.connect ("position-updated", on_looper_pos_updated)
	looper.connect ("error", on_looper_error)
	
	looper.play ()
	mainloop.run ()
