# Generated by sila2.code_generator; sila2.__version__: 0.12.2
from __future__ import annotations

from abc import ABC, abstractmethod
from queue import Queue
from typing import TYPE_CHECKING, List, Optional, Union

from sila2.server import FeatureImplementationBase, MetadataDict

from .ufcontroller_types import (
    CloseArm_Responses,
    CloseGripper_Responses,
    InitializeArm_Responses,
    MoveJoints_Responses,
    MoveToStoredPosition_Responses,
    OpenGripper_Responses,
    ResetArm_Responses,
    SetManualMode_Responses,
    StorePosition_Responses,
)

if TYPE_CHECKING:

    from ...server import Server


class UFControllerBase(FeatureImplementationBase, ABC):
    parent_server: Server

    _JointAngles_producer_queue: Queue[Union[List[float], Exception]]
    _JointAngles_current_value: List[float]

    _GripperPosition_producer_queue: Queue[Union[float, Exception]]
    _GripperPosition_current_value: float

    _JointVelocities_producer_queue: Queue[Union[List[float], Exception]]
    _JointVelocities_current_value: List[float]

    def __init__(self, parent_server: Server):
        """
        This controller is intended to be a SiLA2 compliant controller for Ufactory robot arms.
        """
        super().__init__(parent_server=parent_server)

        self._JointAngles_producer_queue = Queue()

        self._GripperPosition_producer_queue = Queue()

        self._JointVelocities_producer_queue = Queue()

    @abstractmethod
    def get_ArmIPAddress(self, *, metadata: MetadataDict) -> str:
        """
        The IP Address of the connected robotic arm.

        :param metadata: The SiLA Client Metadata attached to the call
        :return: The IP Address of the connected robotic arm.
        """

    def update_JointAngles(self, JointAngles: List[float], queue: Optional[Queue[List[float]]] = None) -> None:
        """
        Return the joint positions to the client.

        This method updates the observable property 'JointAngles'.

        :param queue: The queue to send updates to. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._JointAngles_producer_queue
            self._JointAngles_current_value = JointAngles
        queue.put(JointAngles)

    def JointAngles_on_subscription(self, *, metadata: MetadataDict) -> Optional[Queue[List[float]]]:
        """
        Return the joint positions to the client.

        This method is called when a client subscribes to the observable property 'JointAngles'

        :param metadata: The SiLA Client Metadata attached to the call
        :return: Optional `Queue` that should be used for updating this property.
            If None, the default Queue will be used.
        """

    def abort_JointAngles_subscriptions(self, error: Exception, queue: Optional[Queue[List[float]]] = None) -> None:
        """
        Return the joint positions to the client.

        This method aborts subscriptions to the observable property 'JointAngles'.

        :param error: The Exception to be sent to the subscribing client.
            If it is no DefinedExecutionError or UndefinedExecutionError, it will be wrapped in an UndefinedExecutionError.
        :param queue: The queue to abort. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._JointAngles_producer_queue
        queue.put(error)

    @property
    def current_JointAngles(self) -> List[float]:
        try:
            return self._JointAngles_current_value
        except AttributeError:
            raise AttributeError("Observable property JointAngles has never been set")

    def update_GripperPosition(self, GripperPosition: float, queue: Optional[Queue[float]] = None) -> None:
        """
        Returns the current gripper position.

        This method updates the observable property 'GripperPosition'.

        :param queue: The queue to send updates to. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._GripperPosition_producer_queue
            self._GripperPosition_current_value = GripperPosition
        queue.put(GripperPosition)

    def GripperPosition_on_subscription(self, *, metadata: MetadataDict) -> Optional[Queue[float]]:
        """
        Returns the current gripper position.

        This method is called when a client subscribes to the observable property 'GripperPosition'

        :param metadata: The SiLA Client Metadata attached to the call
        :return: Optional `Queue` that should be used for updating this property.
            If None, the default Queue will be used.
        """

    def abort_GripperPosition_subscriptions(self, error: Exception, queue: Optional[Queue[float]] = None) -> None:
        """
        Returns the current gripper position.

        This method aborts subscriptions to the observable property 'GripperPosition'.

        :param error: The Exception to be sent to the subscribing client.
            If it is no DefinedExecutionError or UndefinedExecutionError, it will be wrapped in an UndefinedExecutionError.
        :param queue: The queue to abort. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._GripperPosition_producer_queue
        queue.put(error)

    @property
    def current_GripperPosition(self) -> float:
        try:
            return self._GripperPosition_current_value
        except AttributeError:
            raise AttributeError("Observable property GripperPosition has never been set")

    def update_JointVelocities(self, JointVelocities: List[float], queue: Optional[Queue[List[float]]] = None) -> None:
        """
        Returns the velocities of each joint in real-time.

        This method updates the observable property 'JointVelocities'.

        :param queue: The queue to send updates to. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._JointVelocities_producer_queue
            self._JointVelocities_current_value = JointVelocities
        queue.put(JointVelocities)

    def JointVelocities_on_subscription(self, *, metadata: MetadataDict) -> Optional[Queue[List[float]]]:
        """
        Returns the velocities of each joint in real-time.

        This method is called when a client subscribes to the observable property 'JointVelocities'

        :param metadata: The SiLA Client Metadata attached to the call
        :return: Optional `Queue` that should be used for updating this property.
            If None, the default Queue will be used.
        """

    def abort_JointVelocities_subscriptions(self, error: Exception, queue: Optional[Queue[List[float]]] = None) -> None:
        """
        Returns the velocities of each joint in real-time.

        This method aborts subscriptions to the observable property 'JointVelocities'.

        :param error: The Exception to be sent to the subscribing client.
            If it is no DefinedExecutionError or UndefinedExecutionError, it will be wrapped in an UndefinedExecutionError.
        :param queue: The queue to abort. If None, the default Queue will be used.
        """
        if queue is None:
            queue = self._JointVelocities_producer_queue
        queue.put(error)

    @property
    def current_JointVelocities(self) -> List[float]:
        try:
            return self._JointVelocities_current_value
        except AttributeError:
            raise AttributeError("Observable property JointVelocities has never been set")

    @abstractmethod
    def OpenGripper(self, *, metadata: MetadataDict) -> OpenGripper_Responses:
        """
        Open the gripper as far as possible.


        :param metadata: The SiLA Client Metadata attached to the call

        """

    @abstractmethod
    def CloseGripper(self, *, metadata: MetadataDict) -> CloseGripper_Responses:
        """
        Close the gripper until it encounters a counter force.


        :param metadata: The SiLA Client Metadata attached to the call

        """

    @abstractmethod
    def InitializeArm(self, *, metadata: MetadataDict) -> InitializeArm_Responses:
        """
        Initialize the robotic arm and set it in a ready state.


        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - Success: Indicates whether the initialization was successful.


        """

    @abstractmethod
    def CloseArm(self, *, metadata: MetadataDict) -> CloseArm_Responses:
        """
        Safely shut down and disconnect the robotic arm.


        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - Success: Indicates whether the shutdown was successful.


        """

    @abstractmethod
    def ResetArm(self, *, metadata: MetadataDict) -> ResetArm_Responses:
        """
        Resets the arm by closing and reopening the connection.


        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - Success: Indicates whether the reset was successful.


        """

    @abstractmethod
    def SetManualMode(self, *, metadata: MetadataDict) -> SetManualMode_Responses:
        """
        Switches the robotic arm to manual control mode.


        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - Success: Indicates whether the arm was successfully switched to manual mode.


        """

    @abstractmethod
    def MoveJoints(self, JointAngles: List[float], *, metadata: MetadataDict) -> MoveJoints_Responses:
        """
        Move the robot arm to a new position


        :param JointAngles: The list of joint angles with their names and new positions.

        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - MoveJointsResponses: Response indicating the success of the move and the current joint angles.


        """

    @abstractmethod
    def StorePosition(self, *, metadata: MetadataDict) -> StorePosition_Responses:
        """
        Add the current position to the list of stored positions. Will return the number.


        :param metadata: The SiLA Client Metadata attached to the call

        :return:

            - PositionIdentifier: The position in the list of stored poses


        """

    @abstractmethod
    def MoveToStoredPosition(
        self, PositionIdentifier: int, *, metadata: MetadataDict
    ) -> MoveToStoredPosition_Responses:
        """
        Move the robot arm to a previously stored position


        :param PositionIdentifier: The position in the list of stored poses as previously returned by store position

        :param metadata: The SiLA Client Metadata attached to the call

        """
