#!/usr/bin/env python3
"""SC-Controller - Gestures

Everything related to non-GUI part of gesture detection lies here.
It's technically part of SCC-Daemon, separater into special module just to keep
it clean.
"""

import logging

from scc.actions import Action
from scc.constants import CPAD, CPAD_MIN, CPAD_X_MAX, CPAD_Y_MAX, STICK_PAD_MAX, STICK_PAD_MIN
from scc.tools import clamp

log = logging.getLogger("Gestures")


class GestureDetector(Action):
	"""Derived from Action, but not callable in profile.

	When daemon decides it's good time to start gesture, be it because of
	GestureAction special action or "Gesture:" message from client,
	it constructs instance of this class and leaves everything to it.
	"""

	UP = "U"
	DOWN = "D"
	LEFT = "L"
	RIGHT = "R"

	def __init__(self, up_direction, on_finished) -> None:
		Action.__init__(self)
		# TODO: Configurable resolution
		self._resolution = 3
		self._deadzone = 1.0 / self._resolution / self._resolution
		self._up_direction = up_direction
		self._on_finished = on_finished
		self._enabled = False
		self._positions = []
		self._result = []

	def enable(self):
		"""GestureDetector doesn't starts do detect anything until this is called"""
		self._enabled = True
		self._result = []

	def get_string(self):
		"""Returns string representation of (probably unfinished) gesture"""
		return "".join(self._result)

	def get_positions(self):
		"""Returns list of positions used to generate gesture"""
		return self._positions

	def get_resolution(self):
		"""Returns gesture resolution"""
		return self._resolution

	def whole(self, mapper, x, y, what):
		if self._enabled:
			if (x, y) == (0, 0):
				# Pad was released
				self._enabled = False
				self._on_finished(self, "".join(self._result))
				return
			# Convert positions on pad to position on grid
			if what == CPAD:
				x = clamp(0, float(x) / (CPAD_X_MAX - CPAD_MIN), 1.0)
				y = clamp(0, float(y) / (CPAD_Y_MAX - CPAD_MIN), 1.0)
				x *= self._resolution
				y *= self._resolution
			else:
				x -= STICK_PAD_MIN
				y = STICK_PAD_MAX - y
				x = float(x) / (float(STICK_PAD_MAX - STICK_PAD_MIN) / self._resolution)
				y = float(y) / (float(STICK_PAD_MAX - STICK_PAD_MIN) / self._resolution)
			# Check for deadzones around grid lines
			for i in range(1, self._resolution):
				if x > i - self._deadzone and x < i + self._deadzone:
					return
				if y > i - self._deadzone and y < i + self._deadzone:
					return
			# Round
			x = clamp(0, int(x), self._resolution - 1)
			y = clamp(0, int(y), self._resolution - 1)
			if self._positions:
				ox, oy = self._positions[-1]
				if (x, y) != (ox, oy):
					self._positions.append((x, y))
					while (x, y) != (ox, oy):
						if x < ox:
							self._result.append(self.LEFT)
							x += 1
						elif x > ox:
							self._result.append(self.RIGHT)
							x -= 1
						elif y < oy:
							self._result.append(self.UP)
							y += 1
						elif y > oy:
							self._result.append(self.DOWN)
							y -= 1
			else:
				self._positions.append((x, y))
