from .command import Command, CommandResult, CompositeCommand
from aamp_app.devices.newport_esp301 import NewportESP301
from typing import Optional


# Parent class, subclass from Command ABC
class NewportESP301ParentCommand(Command):
    """Parent class for all NewportESP301 commands."""

    receiver_cls = NewportESP301

    def __init__(self, receiver: NewportESP301, **kwargs):
        super().__init__(receiver, **kwargs)


# Recommended command classes
class NewportESP301Connect(NewportESP301ParentCommand):
    """Open the serial port to the ESP301 controller."""

    def __init__(self, receiver: NewportESP301, **kwargs):
        super().__init__(receiver, **kwargs)

    def execute(self) -> None:
        self._result = CommandResult(*self._receiver.start_serial())


class NewportESP301Initialize(NewportESP301ParentCommand):
    """Initialize the axes by homing them."""

    def __init__(self, receiver: NewportESP301, **kwargs):
        super().__init__(receiver, **kwargs)

    def execute(self) -> None:
        self._result = CommandResult(*self._receiver.initialize())


class NewportESP301Deinitialize(NewportESP301ParentCommand):
    """Deinitialize the axes by moving them to position zero."""

    def __init__(self, receiver: NewportESP301, reset_init_flag: bool = True, **kwargs):
        super().__init__(receiver, **kwargs)
        self._params["reset_init_flag"] = reset_init_flag

    def execute(self) -> None:
        self._result = CommandResult(
            *self._receiver.deinitialize(self._params["reset_init_flag"])
        )


# Device-related commands classes
class NewportESP301MoveSpeedAbsolute(NewportESP301ParentCommand):
    """Move axis to absolute position at specific speed (No speed uses default speed)."""

    def __init__(
        self,
        receiver: NewportESP301,
        axis_number: int = 1,
        position: Optional[float] = None,
        speed: Optional[float] = None,
        **kwargs
    ):
        super().__init__(receiver, **kwargs)
        self._params["position"] = position
        self._params["speed"] = speed
        self._params["axis_number"] = axis_number

    def execute(self) -> None:
        self._result = CommandResult(
            *self._receiver.move_speed_absolute(
                self._params["axis_number"],
                self._params["position"],
                self._params["speed"],
            )
        )


class NewportESP301MoveSpeedRelative(NewportESP301ParentCommand):
    """Move axis by relative distance at specific speed (No speed uses default speed)."""

    def __init__(
        self,
        receiver: NewportESP301,
        axis_number: int = 1,
        distance: Optional[float] = None,
        speed: Optional[float] = None,
        **kwargs
    ):
        super().__init__(receiver, **kwargs)
        self._params["distance"] = distance
        self._params["speed"] = speed
        self._params["axis_number"] = axis_number

    def execute(self) -> None:
        self._result = CommandResult(
            *self._receiver.move_speed_relative(
                self._params["axis_number"],
                self._params["distance"],
                self._params["speed"],
            )
        )


# Example of command with additional logic to determine the returned tuple of (success/fail: bool, success/fail message: str)
class NewportESP301SetDefaultSpeed(NewportESP301ParentCommand):
    """Set the default speed of the axes."""

    def __init__(self, receiver: NewportESP301, speed: float, **kwargs):
        super().__init__(receiver, **kwargs)
        self._params["speed"] = speed

    def execute(self) -> None:
        self._receiver.default_speed = self._params["speed"]
        # receiver's default_speed has a setter than checks the value is > 0 and < max_speed before setting
        # therefore, we can check if we actually changed the receiver's default speed, if not, it means the speed was out of range
        if self._receiver.default_speed == self._params["speed"]:
            self._result = CommandResult(
                True, "Default speed successfully set to " + str(self._params["speed"])
            )
        else:
            self._result = CommandResult(
                False,
                "Failed to set the default speed to "
                + str(self._params["speed"])
                + ". The default speed must be > 0 and < "
                + str(self._receiver._max_speed),
            )


# Derived commands
class NewportESP301HorzMoveSpeedAbsolute(NewportESP301MoveSpeedAbsolute):
    """desc"""

    # position no longer can be None here because we dont need axis num in sig
    def __init__(
        self,
        receiver: NewportESP301,
        position: float,
        speed: Optional[float] = None,
        **kwargs
    ):
        super().__init__(receiver, 1, position, speed, **kwargs)


# Just for testing composite commands
class NewportESP301Dance(CompositeCommand):
    def __init__(self, receiver, speed, distance, **kwargs):
        super().__init__(**kwargs)
        self.add_command(NewportESP301MoveSpeedRelative(receiver, -distance, speed))
        self.add_command(NewportESP301MoveSpeedRelative(receiver, distance, speed))
