#!/usr/bin/env python3
"""SC-Controller - Quick OSD Menu

Controled by buttons instead of stick. Fast to use, but can display only
limited number of items
"""

import logging
import os
import sys

from gi.repository import GLib, Gtk

from scc.config import Config
from scc.menu_data import MenuItem, Submenu
from scc.osd import OSDWindow
from scc.osd.menu import Menu, MenuIcon
from scc.paths import get_share_path
from scc.tools import _, find_icon, find_menu

log = logging.getLogger("osd.quickmenu")


class QuickMenu(Menu):
	BUTTONS = ["A", "B", "X", "Y", "LB", "RB"]
	BUTTON_INDEXES = [0, 1, 2, 3, 7, 8]  # indexes to gui->buttons list
	# in controller gui config

	def __init__(self, cls="osd-menu"):
		Menu.__init__(self, cls)
		self._cancel_with = "START"
		self._pressed = []
		self._icons = []
		self._timer = None

	def generate_widget(self, item):
		"""In QuickMenu, everything but submenus and simple
		menuitems is ignored.
		"""
		if self._button_index >= len(self.BUTTONS):
			return None
		if isinstance(item, (MenuItem, Submenu)):
			widget = Gtk.Button.new_with_label(item.label)
			widget.set_relief(Gtk.ReliefStyle.NONE)
			if hasattr(widget.get_children()[0], "set_xalign"):
				widget.get_children()[0].set_xalign(0)
			else:
				widget.get_children()[0].set_halign(Gtk.Align.START)
			if isinstance(item, Submenu):
				item.callback = self.show_submenu
				label1 = widget.get_children()[0]
				label2 = Gtk.Label(_(">>"))
				label2.set_property("margin-left", 30)
				box = Gtk.Box(Gtk.Orientation.HORIZONTAL)
				widget.remove(label1)
				box.pack_start(label1, True, True, 1)
				box.pack_start(label2, False, True, 1)
				widget.add(box)
				widget.set_name("osd-menu-item")
			elif item.id is None:
				# Ignored as well
				return None
			else:
				widget.set_name("osd-menu-item")

			item.button = self.BUTTONS[self._button_index]
			self._button_index += 1

			icon_file, has_colors = find_icon("buttons/%s" % item.button, False)
			icon = MenuIcon(icon_file, has_colors)
			label = widget.get_children()[0]
			for c in [] + widget.get_children():
				widget.remove(c)
			self._icons.append(icon)
			box = Gtk.Box()
			box.pack_start(icon, False, True, 0)
			box.pack_start(label, True, True, 10)
			widget.add(box)
			return widget
		return None

	def _add_arguments(self):
		OSDWindow._add_arguments(self)
		self.argparser.add_argument(
			"--cancel-with",
			type=str,
			metavar="button",
			default="START",
			help="button used to cancel menu (default: START)",
		)
		self.argparser.add_argument(
			"--timeout", type=int, default=5, help="how many seconds before menu is automatically canceled",
		)
		self.argparser.add_argument(
			"--cancel-with-release", action="store_true", help="cancel menu with button release instead of button press",
		)
		self.argparser.add_argument(
			"--from-profile", "-p", type=str, metavar="profile_file menu_name", help="load menu items from profile file",
		)
		self.argparser.add_argument(
			"--from-file", "-f", type=str, metavar="filename", help="load menu items from json file",
		)
		self.argparser.add_argument("--print-items", action="store_true", help="prints menu items to stdout")
		self.argparser.add_argument("items", type=str, nargs="*", metavar="id title", help="Menu items")

	def _check_on_screen_position(self, quick=False):
		pass

	def lock_inputs(self):
		def success(*a):
			log.error("Sucessfully locked input")
			config = self.controller.load_gui_config(os.path.join(get_share_path(), "images"))
			if config and config["gui"] and config["gui"]["buttons"]:
				buttons = config["gui"]["buttons"]
				try:
					for i in range(len(self._icons)):
						icon = self._icons[i]
						name = buttons[self.BUTTON_INDEXES[i]]
						filename, trash = find_icon("buttons/%s" % name)
						icon.set_filename(filename)
						icon.queue_draw()
				except IndexError:
					pass

		locks = [x for x in self.BUTTONS] + [self._cancel_with]
		self.controller.lock(success, self.on_failed_to_lock, *locks)

	def parse_arguments(self, argv):
		if not OSDWindow.parse_arguments(self, argv):
			print("failed to parse args")
			return False
		if not self.parse_menu():
			print("failed to parse menu")
			return False
		if not self.config:
			self.config = Config()

		self._cancel_with = self.args.cancel_with
		self._timeout = self.args.timeout

		# Create buttons that are displayed on screen
		items = self.items.generate(self)
		self.items = []
		self._button_index = 0
		for item in items:
			item.widget = self.generate_widget(item)
			if item.widget is not None:
				self.items.append(item)
		self.pack_items(self.parent, self.items)
		if len(self.items) == 0:
			print("%s: error: no items in menu" % (sys.argv[0]), file=sys.stderr)
			return False

		return True

	def next_item(self, direction):
		pass

	def select(self, index):
		pass

	def show_submenu(self, trash, trash2, trash3, menuitem):
		"""Called when user chooses menu item pointing to submenu"""
		filename = find_menu(menuitem.filename)
		if filename:
			self._submenu = QuickMenu()
			sub_pos = list(self.position)
			for i in (0, 1):
				sub_pos[i] = sub_pos[i] - self.SUBMENU_OFFSET if sub_pos[i] < 0 else sub_pos[i] + self.SUBMENU_OFFSET

			self._submenu.use_config(self.config)
			self._submenu.parse_arguments(
				[
					"menu.py",
					"-x",
					str(sub_pos[0]),
					"-y",
					str(sub_pos[1]),
					"--timeout",
					str(self._timeout),
					"--from-file",
					filename,
				],
			)
			self._submenu.set_is_submenu()
			self._submenu.use_daemon(self.daemon)
			self._submenu.connect("destroy", self.on_submenu_closed)
			self._submenu.controller = self.controller
			self._submenu.show()
			self.cancel_timer()

	def on_submenu_closed(self, *a):
		# Quickmenu can have submenus, but everything cancels at once when
		# last Quickmenu in hierarchy is canceled or timeouts
		if self._submenu.get_exit_code() in (0, -2):
			self._menuid = self._submenu._menuid
		self._selected = self._submenu._selected
		self.quit(self._submenu.get_exit_code())

	def pressed(self, what):
		"""Called when button is pressed. If menu with that button assigned
		exists, it is hilighted.
		"""
		for item in self.items:
			if item.button == what:
				self._pressed.append(item)
				item.widget.set_name("osd-menu-item-selected")

	def released(self, what):
		"""Called when button is pressed. If menu with that button assigned
		exists, it is hilighted.
		"""
		last = None
		for item in self.items:
			if item.button == what:
				while item in self._pressed:
					self._pressed.remove(item)
				item.widget.set_name("osd-menu-item")
				last = item

		if len(self._pressed) == 0 and last is not None:
			if last.callback:
				last.callback(self, self.daemon, self.controller, last)
			else:
				self._selected = last
				self.quit(0)

	def on_timeout(self, *a):
		self.quit(-1)

	def show(self, *a):
		Menu.show(self, *a)
		self.restart_timer()

	def restart_timer(self):
		self.cancel_timer()
		self._timer = GLib.timeout_add_seconds(self._timeout, self.on_timeout)

	def cancel_timer(self):
		if self._timer:
			GLib.source_remove(self._timer)
			self._timer = None

	def on_event(self, daemon, what, data):
		if self._submenu:
			return self._submenu.on_event(daemon, what, data)
		if what == self._cancel_with:
			if data[0] == 0:  # Button released
				self.quit(-1)
		elif what in self.BUTTONS:
			self.restart_timer()
			if data[0] == 1:  # Button pressed
				self.pressed(what)
			else:  # Released
				self.released(what)


if __name__ == "__main__":
	import gi

	gi.require_version("Gtk", "3.0")
	gi.require_version("Rsvg", "2.0")
	gi.require_version("GdkX11", "3.0")

	from scc.paths import get_share_path
	from scc.tools import init_logging

	init_logging()

	m = QuickMenu()
	if not m.parse_arguments(sys.argv):
		sys.exit(1)
	m.run()
	if m.get_exit_code() == 0:
		print(m.get_selected_item_id())
	sys.exit(m.get_exit_code())
