#!/usr/bin/python3

from gi.repository import GLib

import sys
import dbus
import dbus.service
import dbus.mainloop.glib
import signal

def handler(signum, frame):
	raise Exception("\nSingle tone is finished!")

class GoBack(dbus.DBusException):
	_dbus_error_name = "org.ofono.Error.GoBack"

class EndSession(dbus.DBusException):
	_dbus_error_name = "org.ofono.Error.EndSession"

class Busy(dbus.DBusException):
	_dbus_error_name = "org.ofono.Error.Busy"

class StkAgent(dbus.service.Object):
	exit_on_release = True
	timeout_id = 0
	timeout_reply_handler = None

	def set_exit_on_release(self, exit_on_release):
		self.exit_on_release = exit_on_release

	def timeout_callback(self):
		self.timeout_id = 0
		self.timeout_reply_handler()
		return False

	def call_added(self, path, properties):
		print("call added %s" % (path))
		if (self.timeout_id > 0):
			GLib.source_remove(self.timeout_id)
			self.timeout_callback()

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="", out_signature="")
	def Release(self):
		print("Release")
		if self.exit_on_release:
			mainloop.quit()

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sya(sy)n", out_signature="y")
	def RequestSelection(self, title, icon, items, default):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		index = 0
		for item in items:
			print("%d. %s (icon: %d)" %
				(index, item[0], int(item[1])))
			index += 1

		print("\nDefault: %d" % (default))
		select = input("Enter Selection (t, b):")

		if select == 'b':
			raise GoBack("User wishes to go back")
		elif select == 't':
			raise EndSession("User wishes to terminate session")
		else:
			return int(select)

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="syb", out_signature="",
					async_callbacks=("reply_func",
								"error_func"))
	def DisplayText(self, title, icon, urgent, reply_func, error_func):
		print("DisplayText (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		print("Urgent: (%d)" % (urgent))
		key = input("Press return to clear ('t' terminates, "
						"'b' goes back, 'n' busy, "
						"'w' return and wait):")

		if key == 'w':
			seconds = 60
		else:
			seconds = 0

		if key == 'b':
			raise GoBack("User wishes to go back")
		elif key == 't':
			raise EndSession("User wishes to terminate session")
		elif key == 'n':
			raise Busy("User wishes to simulate busy screen")

		if (seconds > 0):
			print("Waiting for %d seconds" % (seconds))

		self.timeout_reply_handler = reply_func
		self.timeout_id = GLib.timeout_add_seconds(seconds,
							self.timeout_callback)

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sysyyb", out_signature="s")
	def RequestInput(self, title, icon, default, min_chars, max_chars,
				hide_typing):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		print("Default: (%s)" % (default))
		print("Hide typing: (%s)" % (hide_typing))
		print("Enter characters, min: %d, max: %d:" % (min_chars,
								max_chars))
		userin = input("")

		return userin

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sysyyb", out_signature="s")
	def RequestDigits(self, title, icon, default, min_chars, max_chars,
				hide_typing):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		print("Default: (%s)" % (default))
		print("Hide typing: (%s)" % (hide_typing))
		print("Enter digits, min: %d, max: %d:" % (min_chars,
								max_chars))
		userin = input("'t' terminates, 'b' goes back:")

		if userin == 'b':
			raise GoBack("User wishes to go back")
		elif userin == 't':
			raise EndSession("User wishes to terminate session")
		else:
			return userin

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sy", out_signature="s")
	def RequestKey(self, title, icon):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		key = input("Enter Key (t, b):")

		if key == 'b':
			raise GoBack("User wishes to go back")
		elif key == 't':
			raise EndSession("User wishes to terminate session")
		else:
			return key

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sy", out_signature="s")
	def RequestDigit(self, title, icon):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		key = input("Enter Digit (t, b):")

		if key == 'b':
			raise GoBack("User wishes to go back")
		elif key == 't':
			raise EndSession("User wishes to terminate session")
		else:
			return key

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sy", out_signature="s")
	def RequestQuickDigit(self, title, icon):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		key = input("Quick digit (0-9, *, #, t, b):")

		if key == 'b':
			raise GoBack("User wishes to go back")
		elif key == 't':
			raise EndSession("User wishes to terminate session")
		else:
			return key

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sy", out_signature="b")
	def RequestConfirmation(self, title, icon):
		print("Title: (%s)" % (title))
		print("Icon: (%d)" % (int(icon)))
		key = input("Enter Confirmation (t, b, y, n):")

		if key == 'b':
			raise GoBack("User wishes to go back")
		elif key == 't':
			raise EndSession("User wishes to terminate session")
		elif key == 'y':
			return True
		else:
			return False

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sy", out_signature="b")
	def ConfirmCallSetup(self, info, icon):
		print("Information: (%s)" % (info))
		print("Icon: (%d)" % (int(icon)))
		key = input("Enter Confirmation (t, y, n):")

		if key == 't':
			raise EndSession("User wishes to terminate session")
		elif key == 'y':
			return True
		else:
			return False

	@dbus.service.method("org.ofono.SimToolkitAgent",
				in_signature="sys", out_signature="b")
	def ConfirmLaunchBrowser(self, info, icon, url):
		print("Information: (%s)" % (info))
		print("Icon: (%d)" % (int(icon)))
		print("URL (%s)" % (url))
		key = input("Enter Confirmation (y, n):")

		if key == 'y':
			return True
		else:
			return False

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="", out_signature="")
	def Cancel(self):
		print("Cancel")

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="ssy", out_signature="")
	def PlayTone(self, tone, text, icon):
		print("PlayTone: %s" % (tone))
		print("Text: %s" % (text))
		print("Icon: %d" % (int(icon)))

		signal.signal(signal.SIGALRM, handler)
		signal.alarm(5)

		try:
			key = input("Press return to end before end of"
							 " single tone (t):")
			signal.alarm(0)

			if key == 't':
				raise EndSession("User wishes to terminate"
								 " session")
		except Exception as exc:
			print(exc)

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="ssy", out_signature="",
					async_callbacks=("reply_func",
								"error_func"))
	def LoopTone(self, tone, text, icon, reply_func, error_func):
		print("LoopTone: %s" % (tone))
		print("Text: %s" % (text))
		print("Icon: %d" % (int(icon)))
		key = input("Press return to end before timeout "
				"('t' terminates, 'w' return and wait):")

		if key == 'w':
			seconds = 60
		else:
			seconds = 0

		if key == 't':
			raise EndSession("User wishes to terminate session")

		if (seconds > 0):
			print("Waiting for %d seconds" % (seconds))

		self.timeout_reply_handler = reply_func
		self.timeout_id = GLib.timeout_add_seconds(seconds,
							self.timeout_callback)

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="sy", out_signature="")
	def DisplayActionInformation(self, text, icon):
		print("Text: %s" % (text))
		print("Icon: %d" % (int(icon)))

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="sy", out_signature="")
	def DisplayAction(self, text, icon):
		print("Text: (%s)" % (text))
		print("Icon: (%d)" % (int(icon)))
		key = input("Press 't' to terminate the session ")

		if key == 't':
			raise EndSession("User wishes to terminate session")

	@dbus.service.method("org.ofono.SimToolkitAgent",
					in_signature="sy", out_signature="b")
	def ConfirmOpenChannel(self, info, icon):
		print("Open channel confirmation: (%s)" % (info))
		print("Icon: (%d)" % (int(icon)))
		key = input("Enter Confirmation (t, y, n):")

		if key == 't':
			raise EndSession("User wishes to terminate session")
		elif key == 'y':
			return True
		else:
			return False

_dbus2py = {
	dbus.String : str,
	dbus.UInt32 : int,
	dbus.Int32 : int,
	dbus.Int16 : int,
	dbus.UInt16 : int,
	dbus.UInt64 : int,
	dbus.Int64 : int,
	dbus.Byte : int,
	dbus.Boolean : bool,
	dbus.ByteArray : str,
	dbus.ObjectPath : str
    }

def dbus2py(d):
	t = type(d)
	if t in _dbus2py:
		return _dbus2py[t](d)
	if t is dbus.Dictionary:
		return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()])
	if t is dbus.Array and d.signature == "y":
		return "".join([chr(b) for b in d])
	if t is dbus.Array or t is list:
		return [dbus2py(v) for v in d]
	if t is dbus.Struct or t is tuple:
		return tuple([dbus2py(v) for v in d])
	return d

def pretty(d):
	d = dbus2py(d)
	t = type(d)

	if t in (dict, tuple, list) and len(d) > 0:
		if t is dict:
			d = ", ".join(["%s = %s" % (k, pretty(v))
					for k, v in d.items()])
			return "{ %s }" % d

		d = " ".join([pretty(e) for e in d])

		if t is tuple:
			return "( %s )" % d

	if t is str:
		return "%s" % d

	return str(d)

def property_changed(name, value):
	print("SimToolKit property: %s changed to '%s'" % (name, pretty(value)))

if __name__ == '__main__':
	if len(sys.argv) == 2:
		mode = sys.argv[1]
	else:
		mode = 'menu'

	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

	bus = dbus.SystemBus()
	manager = dbus.Interface(bus.get_object("org.ofono", "/"),
							"org.ofono.Manager")

	modems = manager.GetModems()

	for path, properties in modems:
		if "org.ofono.SimToolkit" in properties["Interfaces"]:
			stk = dbus.Interface(bus.get_object('org.ofono', path),
							'org.ofono.SimToolkit')
		if "org.ofono.VoiceCallManager" in properties["Interfaces"]:
			vcm = dbus.Interface(bus.get_object('org.ofono', path),
						'org.ofono.VoiceCallManager')

	stk.connect_to_signal("PropertyChanged", property_changed)

	properties = stk.GetProperties()

	if mode == 'menu':
		if "MainMenuTitle" in properties:
			print("Main Menu:")
			print("%s" % (properties["MainMenuTitle"]))
			print("\n")

		if "MainMenu" in properties:
			print("Items:")
			index = 0
			for item in properties["MainMenu"]:
				print("%d. %s" % (index, item[0]))
				index += 1

		path = "/test/agent"
		agent = StkAgent(bus, path)

		try:
			vcm.connect_to_signal("CallAdded", agent.call_added)
		except:
			pass

		select = int(input("Enter Selection: "))
		stk.SelectItem(select, path)
	elif mode == 'agent':
		path = "/test/agent"
		agent = StkAgent(bus, path)

		try:
			vcm.connect_to_signal("CallAdded", agent.call_added)
		except:
			pass

		stk.RegisterAgent(path)

		print("Default Agent registered - Waiting for STK command...")
	else:
		print("%s [menu|agent]" % (sys.argv[0]))
		exit(0)

	mainloop = GLib.MainLoop()
	mainloop.run()
