#!/usr/bin/env python3
"""SC-Controller - Controller Registration - Tester

Class that interacts with `scc hid_test` and `scc evdev_test` commands.
"""

import logging

from gi.repository import Gio, GObject

from scc.tools import find_binary

log = logging.getLogger("CReg.Tester")


class Tester(GObject.GObject):
	"""List of signals:
	ready ()
		Emited when subprocess signalizes it's ready to send data
	error (code)
		Emited when driver test subprocess exits with non-zero return code
	finished ()
		Emited when driver test subprocess exits with zero return code
	axis (number, value)
		Emited when position on axis is changed
	button (keycode, pressed)
		Emited when button on tested gamepad is pressed or released
	"""

	__gsignals__ = {
		"error": (GObject.SignalFlags.RUN_FIRST, None, (int,)),
		"ready": (GObject.SignalFlags.RUN_FIRST, None, ()),
		"finished": (GObject.SignalFlags.RUN_FIRST, None, ()),
		"axis": (GObject.SignalFlags.RUN_FIRST, None, (int, int)),
		"button": (GObject.SignalFlags.RUN_FIRST, None, (int, bool)),
	}

	def __init__(self, driver, device_id):
		GObject.GObject.__init__(self)
		self.buffer = b""
		self.buttons = []
		self.axes = []
		self.subprocess = None
		self.driver = driver
		self.device_id = device_id
		self.errorred = False  # To prevent sending 'error' signal multiple times

	def __del__(self):
		if self.subprocess:
			self.subprocess.send_signal(9)

	def start(self):
		"""Starts driver test subprocess"""
		cmd = [find_binary("scc")] + ["test_" + self.driver, self.device_id]
		self.subprocess = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.STDOUT_PIPE)
		self.subprocess.wait_async(None, self._on_finished)
		self.subprocess.get_stdout_pipe().read_bytes_async(32, 0, None, self._on_read)

	def stop(self):
		if self.subprocess:
			self.subprocess.send_signal(2)  # Sigint

	def _on_finished(self, subprocess, result):
		subprocess.wait_finish(result)
		if self.errorred:
			return
		if subprocess.get_exit_status() == 0:
			self.emit("finished")
		else:
			self.errorred = True
			self.emit("error", subprocess.get_exit_status())

	def _on_read(self, stream, result):
		try:
			data = stream.read_bytes_finish(result).get_data()
		except Exception as e:
			log.exception(e)
			self.subprocess.send_signal(2)
			if not self.errorred:
				self.errorred = True
				self.emit("error", 1)
			return
		if len(data) > 0:
			self.buffer += data
			while b"\n" in self.buffer:
				line, self.buffer = self.buffer.split(b"\n", 1)
				try:
					self._on_line(line)
				except Exception as e:
					log.exception(e)
			self.subprocess.get_stdout_pipe().read_bytes_async(32, 0, None, self._on_read)

	def _on_line(self, line):
		if line.startswith(b"Axis"):
			trash, number, value = line.split(b" ")
			number, value = int(number), int(value)
			self.emit("axis", number, value)
		elif line.startswith(b"ButtonPress"):
			trash, code = line.split(b" ")
			self.emit("button", int(code), True)
		elif line.startswith(b"ButtonRelease"):
			trash, code = line.split(b" ")
			self.emit("button", int(code), False)
		elif line.startswith(b"Ready"):
			self.emit("ready")
		elif line.startswith(b"Axes:"):
			self.axes = [int(x) for x in line.split(b" ")[1:] if len(x.strip())]
		elif line.startswith(b"Buttons:"):
			self.buttons = [int(x) for x in line.split(b" ")[1:] if len(x.strip())]
