#!/usr/bin/env python

import dbus, argparse, os, subprocess

class DreamboxDBus(object):
	INTERFACE = "de.dreambox.ctl"
	OBJECT = "/de/dreambox/ctl"
	PROPERTY_VERSION = "version"

	EXIT_CODE_REBOOT = 2
	EXIT_CODE_RESTART = 3
	EXIT_CODE_SHUTDOWN = 8
	EXIT_CODE_RECOVERY = 16

	def __init__(self):
		self._bus = dbus.SystemBus()
		self._proxy = self._bus.get_object(DreamboxDBus.INTERFACE, DreamboxDBus.OBJECT)
		self._interface = dbus.Interface(self._proxy, DreamboxDBus.INTERFACE)
		#print("Connected to '%s' v%s" % (self.INTERFACE, self.version()))

	def version(self):
		return self._getProperty(DreamboxDBus.PROPERTY_VERSION)

	def _getProperty(self, name):
		return self._proxy.Get(self.INTERFACE, name, dbus_interface=dbus.PROPERTIES_IFACE)

	def screenshot(self, filename, width=0, height=0, desktop=0, mode='combined'):
		fmt = filename.split(".")[-1]
		osd = False
		video = False
		if mode == 'combined' or mode == 'osd':
			osd = True
		if mode == 'combined' or mode == 'video':
			video = True

		if self._interface.screenshot(str(filename), str(fmt), width, height, desktop, osd, video) == True:
			return True

		grabArgs = []
		grabArgs.append('/usr/bin/grab')
		if fmt == 'png':
			grabArgs.append('-p')
		elif fmt == 'jpg':
			grabArgs.append('-j80')

		if mode == 'osd':
			grabArgs.append('-o')
		if mode == 'video':
			grabArgs.append('-v')

		with open(os.devnull, 'w') as DEVNULL:
			popen = subprocess.Popen(grabArgs, stdout=DEVNULL)
			if popen.wait() == 0:
				return True

		return False

	def recordCount(self):
		return self._interface.recordCount()

	def isTimerPending(self):
		return self._interface.isTimerPending()

	def currentService(self):
		return self._interface.currentService()

	def play(self, val, isUri):
		return self._interface.play(val, isUri)

	def pause(self):
		return self._interface.pause()

	def resume(self):
		return self._interface.resume()

	def stop(self):
		return self._interface.stop()

	def getDuration(self):
		return self._interface.getDuration()

	def getPosition(self):
		return self._interface.getPosition()

	def setPosition(self, seconds):
		return self._interface.setPosition(seconds)

	def getVolume(self):
		return self._interface.getVolume()

	def setVolume(self, to):
		return self._interface.setVolume(to)

	def quit(self, code):
		return self._interface.quit(code)

def getArgs():
	parser = argparse.ArgumentParser(
		description='Dreambox CLI Control',
		formatter_class=argparse.ArgumentDefaultsHelpFormatter
	)
	parser._positionals.title = 'commands'
	subparsers = parser.add_subparsers(dest='cmd')
	#screenshots
	pshot = subparsers.add_parser('screenshot', help='take a screenshot of the OSD and save it to a file', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	pshot.add_argument('-f', '--filename', type=str, default='/tmp/screenshot.png', required=True, help='filename for the screenshot (requires full path)')
	pshot.add_argument('-iw', '--width', type=int, default=0, help='image width. 0 for original size')
	pshot.add_argument('-ih', '--height', type=int, default=0, help='image height. 0 for original size')
	pshot.add_argument('-d', '--desktop', type=int, default=0, help='desktop to take a screenshot of. 0 for TV, 1 for display')
	pshot.add_argument('-m', '--mode', type=str, default="combined", help='capture mode, values: osd, video, combined')
	#recordings
	prec = subparsers.add_parser('recordings', help='recordings related functions', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	prec.add_argument('-c', '--count', action='store_true', help='print the number of currently active recordings')
	#current service
	subparsers.add_parser('currentService', help='currently running service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	#play / pause / resume / stop
	pplay = subparsers.add_parser('play', help='play service')
	pgroup = pplay.add_mutually_exclusive_group(required=True)
	pgroup.add_argument('-u', '--uri', help='uri to play')
	pgroup.add_argument('-r', '--ref', help='ref to play')
	pgroup.add_argument('-f', '--file', help='local file to play')
	subparsers.add_parser('pause', help='pause running service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	subparsers.add_parser('resume', help='resume paused service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	subparsers.add_parser('stop', help='stop running service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	#duration
	subparsers.add_parser('duration', help='get duration of running service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	#position
	ppos = subparsers.add_parser('position', help='get/set position of running service', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
	posgroup = ppos.add_mutually_exclusive_group(required=False)
	posgroup.add_argument('-g', '--get', action="store_true", help='get current playback position')
	posgroup.add_argument('-s', '--set', type=int, help='jump to playback position (in seconds)')
	#volume
	pvol = subparsers.add_parser('volume', help='get/set volume')
	vgroup = pvol.add_mutually_exclusive_group(required=False)
	vgroup.add_argument('-g', '--get', action="store_true", help='get current volume')
	vgroup.add_argument('-s', '--set', type=int, help='set volume to value (0..100)')
	#power
	force_text = 'execute with (the) force (ignore recordings and other potential blockers)'
	pshut = subparsers.add_parser('shutdown', help='shut down the system')
	pshut.add_argument('-f', '--force', action='store_true', help=force_text)
	prebt = subparsers.add_parser('reboot', help='reboot the system')
	prebt.add_argument('-f', '--force', action='store_true', help=force_text)
	precv = subparsers.add_parser('recovery', help='reboot into the recovery system')
	precv.add_argument('-f', '--force', action='store_true', help=force_text)
	prest = subparsers.add_parser('restart', help='restart the graphical user interface')
	prest.add_argument('-f', '--force', action='store_true', help=force_text)

	return parser.parse_args()

def dbus_to_python(obj, expected_type=None):
	if obj is None:
		python_obj = obj
	elif isinstance(obj, dbus.Boolean):
		python_obj = bool(obj)
	elif isinstance(obj, dbus.String):
		python_obj = obj.encode('utf-8')
	elif isinstance(obj, dbus.UTF8String):  # Python3 has no UTF8String
		python_obj = str(obj)
	elif isinstance(obj, dbus.ObjectPath):
		python_obj = str(obj)
	elif isinstance(obj, dbus.Byte) or \
		 isinstance(obj, dbus.Int16) or \
		 isinstance(obj, dbus.Int32) or \
		 isinstance(obj, dbus.Int64) or \
		 isinstance(obj, dbus.UInt16) or \
		 isinstance(obj, dbus.UInt32) or \
		 isinstance(obj, dbus.UInt64):
		python_obj = int(obj)
	elif isinstance(obj, dbus.Double):
		python_obj = float(obj)
	elif isinstance(obj, dbus.Array):
		python_obj = [dbus_to_python(x) for x in obj]
	elif isinstance(obj, dbus.Struct):
		python_obj = tuple([dbus_to_python(x) for x in obj])
	elif isinstance(obj, dbus.Dictionary):
		python_obj = {dbus_to_python(k): dbus_to_python(v) for k, v in obj.items()}
	elif isinstance(obj, bool) or \
		 isinstance(obj, str) or isinstance(obj, bytes) or \
		 isinstance(obj, int) or isinstance(obj, float) or \
		 isinstance(obj, list) or isinstance(obj, tuple) or \
		 isinstance(obj, dict):
		python_obj = obj
	else:
		raise TypeError("Unhandled %s" % obj)

	if expected_type is not None:
		if (expected_type == bool and not isinstance(python_obj, bool)) or \
			 (expected_type == str and not isinstance(python_obj, str)) or \
			 (expected_type == int and not isinstance(python_obj, int)) or \
			 (expected_type == float and not isinstance(python_obj, float)) or \
			 (expected_type == list and not isinstance(python_obj, list)) or \
			 (expected_type == tuple and not isinstance(python_obj, tuple)) or \
			 (expected_type == dict and not isinstance(python_obj, dict)):
			raise TypeError("%s is %s, expected %s" % (python_obj, type(python_obj), expected_type))

	return python_obj

def main():
	ddbus = DreamboxDBus()
	args = getArgs()
	if args:
		if args.cmd == "screenshot":
			kwargs = vars(args)
			del kwargs["cmd"]
			if ddbus.screenshot(**kwargs):
				print("ok:screenshot %s" %args.filename)
				exit(0)
			else:
				print("error:screenshot failed")
				exit(1)
		elif args.cmd == "recordings":
			print(ddbus.recordCount())
		elif args.cmd == "currentService":
			res = dbus_to_python(ddbus.currentService(), dict)
			for k, v in res.iteritems():
				print("%s:%s" %(k,v))
			print("postion:%s" %(ddbus.getPosition()))
		elif args.cmd == "play":
			res = False
			if args.uri:
				args = str(args.uri), True
			elif args.file:
				f = args.file
				if not f.startswith("file://") and not f.startswith("/"):
					f = "%s/%s" %(os.getcwd(), f)
				args = f, True
			else:
				args = str(args.ref), False
			res = ddbus.play(*args)
			if res:
				print("ok:play %s" %(args,))
			else:
				print("error:play failed!")
		elif args.cmd == "pause":
			if ddbus.pause():
				print("ok:pause")
			else:
				print("error:pause")
		elif args.cmd == "resume":
			if ddbus.resume():
				print("ok:resume")
			else:
				print("error:resume")
		elif args.cmd == "stop":
			if ddbus.stop():
				print("ok:stop")
			else:
				print("error:stop")
		elif args.cmd == "duration":
			print(ddbus.getDuration())
		elif args.cmd == "position":
			if args.set:
				if ddbus.setPosition(args.set):
					print("ok:position %s" %(args.set,))
				else:
					print("error:position can't jump to %s" %(args.set))
			else:
				print(ddbus.getPosition())
		elif args.cmd == "volume":
			if args.set:
				if ddbus.setVolume(args.set):
					print("ok:volume %s" %(args.set,))
				else:
					print("error:volume %s may be invalid!" %(args.set,))
			else:
				print(ddbus.getVolume())
		elif args.cmd in ["shutdown", "reboot", "recovery", "restart"]:
			if ddbus.recordCount() and not args.force:
				print("error:recordings active, use (the) --force if you have to!")
				exit(1)
				return
			if ddbus.isTimerPending() and not args.force:
				print("error:timer pending, use (the) --force if you have to!")
				exit(1)
				return
			code = {
				"shutdown" : DreamboxDBus.EXIT_CODE_SHUTDOWN,
				"reboot" : DreamboxDBus.EXIT_CODE_REBOOT,
				"restart" : DreamboxDBus.EXIT_CODE_RESTART,
				"recovery" : DreamboxDBus.EXIT_CODE_RECOVERY
			}.get(args.cmd)
			ddbus.quit(code)
			print("OK")

if __name__ == "__main__":
	main()
