"""Eine Etage hoch oder runter - notfalls auch anders herum.

Der Wegpunkt gibt eine Richtung vor, in die gedrueckt wird, bis sich die
Etage aendert. Steht die Richtung falsch im Skript, lief der Bot frueher
endlos weiter: Er ist bis zur Wand gelaufen und hat dort weiter gedrueckt,
ohne je anzukommen. Genau so hing er am 23.07.2026 im Depot von Thais
fest - der Wegpunkt sagte 'north', die Treppe lag im Westen.

Darum hier zwei Dinge:

* Die Richtungen werden der Reihe nach durchprobiert. Zuerst die
  eingestellte, dann die, die auf der Karte als Uebergang eingetragen
  ist, dann der Rest.
* Wer danebenlaeuft, geht zurueck. Ohne das waere schon der erste
  Fehlversuch endgueltig, weil der Bot dann nicht mehr auf dem Feld
  steht, von dem aus es hochgeht.
"""

from time import time

from src.repositories.radar.core import getFloorChangeDirection
from src.utils.keyboard import press
from ....typings import Context
from .base import BaseTask


DIRECTIONS = {
    'north': 'up',
    'south': 'down',
    'west': 'left',
    'east': 'right',
}


class FloorChangeTask(BaseTask):
    """Basis fuer MoveUp und MoveDown; `step` ist -1 hoch, +1 runter."""

    step = 0

    def __init__(self, context: Context, direction: str):
        super().__init__()
        self.isRootTask = True
        self.direction = direction
        coordinate = context['ng_radar']['coordinate']
        self.floorLevel = coordinate[2] + self.step
        self.startCoordinate = (coordinate[0], coordinate[1])
        self.directions = self._order(direction, coordinate)
        self.directionIndex = 0
        self.walkedOff = False
        self.lastPressAt = None

    def _order(self, direction, coordinate):
        """Erst die eingestellte Richtung, dann die aus der Karte, dann der Rest."""
        order = [direction] if direction in DIRECTIONS else []
        fromMap = getFloorChangeDirection(coordinate)
        if fromMap is not None and fromMap not in order:
            order.append(fromMap)
        order += [item for item in DIRECTIONS if item not in order]
        return order

    def currentDirection(self) -> str:
        return self.directions[self.directionIndex % len(self.directions)]

    def _wayBack(self, coordinate) -> str:
        """Die Taste, die naeher an das Startfeld fuehrt."""
        xDistance = coordinate[0] - self.startCoordinate[0]
        if xDistance:
            return 'left' if xDistance > 0 else 'right'
        return 'up' if coordinate[1] > self.startCoordinate[1] else 'down'

    def do(self, context: Context) -> Context:
        self.lastPressAt = time()
        coordinate = context['ng_radar']['coordinate']
        if coordinate is None:
            press(DIRECTIONS[self.currentDirection()])
            return context
        if (coordinate[0], coordinate[1]) != self.startCoordinate:
            # Danebengelaufen: die Richtung fuehrt nicht auf den Uebergang.
            # Zurueck aufs Startfeld, danach die naechste Richtung.
            if not self.walkedOff:
                self.walkedOff = True
                self.directionIndex += 1
            press(self._wayBack(coordinate))
            return context
        self.walkedOff = False
        press(DIRECTIONS[self.currentDirection()])
        return context

    def did(self, context: Context) -> bool:
        coordinate = context['ng_radar']['coordinate']
        return coordinate is not None and coordinate[2] == self.floorLevel

    def shouldRestart(self, context: Context) -> bool:
        return (
            self.lastPressAt is not None
            and time() - self.lastPressAt >= 0.35
            and not self.did(context)
        )
