"""SC-Controller - Input Display

NOTE(Martin): Seems that this module was only meant to be called manually and the Input Mapper makes it seem redundant.
"""

import logging
import os
import signal
import sys

from gi.repository import Gtk

from scc.constants import STICK_PAD_MAX, SCButtons, SCPads, SCSticks
from scc.gui.daemon_manager import DaemonManager
from scc.gui.svg_widget import SVGWidget
from scc.osd import OSDWindow

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


class InputDisplay(OSDWindow):
	IMAGE = "inputdisplay.svg"
	HILIGHT_COLOR = "#FF00FF00"  # ARGB
	OBSERVE_COLOR = "#00007FFF"  # ARGB

	def __init__(self, imagepath="/usr/share/scc/images") -> None:
		OSDWindow.__init__(self, "osd-menu")
		self.daemon = None
		self.config = None
		self.hilights = {self.HILIGHT_COLOR: set(), self.OBSERVE_COLOR: set()}
		self.imagepath = imagepath

		self._eh_ids = []

	def show(self) -> None:
		self.main_area = Gtk.Fixed()
		self.background = SVGWidget(os.path.join(self.imagepath, self.IMAGE))
		self.lpadTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg"))
		self.rpadTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg"))
		self.lstickTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg"))

		self.main_area.set_property("margin-left", 10)
		self.main_area.set_property("margin-right", 10)
		self.main_area.set_property("margin-top", 10)
		self.main_area.set_property("margin-bottom", 10)

		self.main_area.put(self.background, 0, 0)
		self.main_area.put(self.lpadTest, 40, 40)
		self.main_area.put(self.rpadTest, 290, 90)
		self.main_area.put(self.lstickTest, 150, 40)

		self.add(self.main_area)

		OSDWindow.show(self)
		self.lpadTest.hide()
		self.rpadTest.hide()
		self.lstickTest.hide()

	def run(self):
		self.daemon = DaemonManager()
		self._connect_handlers()
		OSDWindow.run(self)

	def use_daemon(self, d) -> None:
		"""Allows (re)using already existing DaemonManager instance in same process.

		use_config() should be be called before parse_arguments() if this is used.
		"""
		self.daemon = d
		self._connect_handlers()
		self.on_daemon_connected(self.daemon)

	def _connect_handlers(self) -> None:
		self._eh_ids += [
			(self.daemon, self.daemon.connect("dead", self.on_daemon_died)),
			(self.daemon, self.daemon.connect("error", self.on_daemon_died)),
			(self.daemon, self.daemon.connect("alive", self.on_daemon_connected)),
		]

	def on_daemon_connected(self, *a) -> None:
		c = self.daemon.get_controllers()[0]
		c.unlock_all()
		c.observe(
			DaemonManager.nocallback,
			self.on_observe_failed,
			"A",
			"B",
			"C",
			"X",
			"Y",
			"START",
			"BACK",
			"LB",
			"RB",
			"LPAD",
			"LPADPRESS",
			"RPAD",
			"RPADPRESS",
			"LGRIP",
			"RGRIP",
			"LT",
			"RT",
			"LSTICK",
			"LSTICKPRESS",
		)
		c.connect("event", self.on_daemon_event_observer)
		c.connect("lost", self.on_controller_lost)

	def on_observe_failed(self, error) -> None:
		log.error("Failed to enable test mode: %s", error)
		if "Sniffing" in error:
			log.error("")
			log.error("=================================================================================")
			log.error("[!!] Please, enable 'Input Test Mode' on 'Advanced' tab in SC Controller settings")
			log.error("=================================================================================")
		self.quit(3)

	def on_daemon_event_observer(self, daemon, what, data) -> None:
		if what in (SCPads.LPAD, SCPads.RPAD, SCSticks.LSTICK):
			widget, area = {
				SCPads.LPAD: (self.lpadTest, "LPADTEST"),
				SCPads.RPAD: (self.rpadTest, "RPADTEST"),
				SCSticks.LSTICK: (self.lstickTest, "LSTICKTEST"),
			}[what]
			# Check if stick or pad is released
			if data[0] == data[1] == 0:
				widget.hide()
				return
			if not widget.is_visible():
				widget.show()
			# Grab values
			ax, ay, aw, trash = self.background.get_area_position(area)
			cw = widget.get_allocation().width
			# Compute center
			x, y = ax + aw * 0.5 - cw * 0.5, ay + 1.0 - cw * 0.5
			# Add pad position
			x += data[0] * aw / STICK_PAD_MAX * 0.5
			y -= data[1] * aw / STICK_PAD_MAX * 0.5
			# Move circle
			self.main_area.move(widget, x, y)
		elif what in ("LPADPRESS", "RPADPRESS"):
			what = {
				"LPADPRESS": "LPAD",
				"RPADPRESS": "RPAD",
			}[what]
			if data[0]:
				self.hilights[self.OBSERVE_COLOR].add(what)
			else:
				self.hilights[self.OBSERVE_COLOR].remove(what)
			self._update_background()
		elif hasattr(SCButtons, what):
			try:
				if data[0]:
					self.hilights[self.OBSERVE_COLOR].add(what)
				else:
					self.hilights[self.OBSERVE_COLOR].remove(what)
				self._update_background()
			except KeyError:
				# Non fatal
				pass
		else:
			log.debug("Unprocessed event in on_daemon_event_observer(): %s", what)

	def _update_background(self):
		h = {}
		for color in self.hilights:
			for i in self.hilights[color]:
				h[i] = color
		self.background.hilight(h)


def sigint(*a):
	print("\n*break*")
	sys.exit(0)


if __name__ == "__main__":
	signal.signal(signal.SIGINT, sigint)

	import gi

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

	from scc.tools import init_logging

	init_logging()

	m = InputDisplay()
	if not m.parse_arguments(sys.argv):
		sys.exit(1)
	m.run()
	sys.exit(m.get_exit_code())
