from google.protobuf.duration_pb2 import Duration

from .... import types
from ....compiler.registry import register_action

# API imports
from ...base import Action
from ...datatypes import common as common
from ...datatypes import control as params

# Type imports
from ...datatypes.common import Response
from ...utils import consume_last


@register_action
class Connect(Action):
    """Connect to the vehicle.

    Connects to the underlying vehicle hardware. Generally, this
    method is called by the law authority on startup and is not
    called by user code.
    """

    async def execute(self) -> Response:
        """Execute the Connect action.

        Returns:
            Response: response generated by the RPC call.
        """

        return await types.VEHICLE.connect(**self.__dict__)


@register_action
class Disconnect(Action):
    """Disconnect from the vehicle.

    Disconnects from the underlying vehicle hardware. Generally,
    this method is called by the law authority when it attempts
    a driver restart and is not called by user code.
    """

    async def execute(self) -> Response:
        """Execute the Disconnect action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.disconnect(**self.__dict__)


@register_action
class Arm(Action):
    """Order the vehicle to arm.

    Arms the vehicle. This is required before any other commands
    are run, otherwise the methods will return `FAILED_PRECONDITION`.
    Once the vehicle is armed, all subsequent actuation methods
    _will move the vehicle_. Make sure to go over the manufacturer
    recommended vehicle-specific pre-operation checklist before arming.
    """

    async def execute(self) -> Response:
        """Execute the Arm action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.arm(**self.__dict__)


@register_action
class Disarm(Action):
    """Order the vehicle to disarm.

    Disarms the vehicle. Prevents any further actuation methods
    from executing, unless the vehicle is re-armed.
    """

    async def execute(self) -> Response:
        """Execute the Disarm action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.disarm(**self.__dict__)


@register_action
class Joystick(Action):
    """Send a joystick command to the vehicle.

    Causes the vehicle to accelerate towards a provided velocity
    setpoint over a provided duration. This is useful for fine-grained
    control based on streamed datasink results or for tele-operating
    the vehicle from a remote commander.

    Attributes:
        velocity (common.Velocity): target velocity to move towards
        duration (Duration): time of actuation after which the vehicle will Hold
    """

    velocity: common.Velocity
    duration: Duration | None = None

    async def execute(self) -> Response:
        """Execute the Joystick action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.joystick(**self.__dict__)

@register_action
class SetGimbalPoseTarget(Action):
    gimbal_id: int
    pose: common.Pose
    pose_mode: params.PoseMode | None
    frame: params.ReferenceFrame | None

    async def execute(self) -> Response:
        """Execute the SetGimbalPoseTarget action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.set_gimbal_pose_target(**self.__dict__)
    
@register_action
class TakeOff(Action):
    """Order the vehicle to take off.

    Causes the vehicle to take off to a specified take off altitude.
    If the vehicle is not a UAV, this method will be unimplemented.

    Attributes:
        take_off_altitude (float): take off height in relative altitude [meters]
    """

    take_off_altitude: float

    async def execute(self) -> Response:
        """Execute the TakeOff action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.take_off(**self.__dict__))


@register_action
class Land(Action):
    """Order the vehicle to land.

    Causes the vehicle to land at its current location. If the
    vehicle is not a UAV, this method will be unimplemented.
    """

    async def execute(self) -> Response:
        """Execute the Land action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.land(**self.__dict__))


@register_action
class Hold(Action):
    """Order the vehicle to hold/loiter.

    Causes the vehicle to hold at its current location and to
    cancel any ongoing movement commands (`ReturnToHome` e.g.).
    """

    async def execute(self) -> Response:
        """Execute the Hold action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.hold(**self.__dict__))


@register_action
class Kill(Action):
    """Orders an emergency shutdown of the vehicle motors.

    Causes the vehicle to immediately turn off its motors. _If the
    vehicle is a UAV, this will result in a freefall_. Use this
    method only in emergency situations.
    """

    async def execute(self) -> Response:
        """Execute the Kill action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.kill(**self.__dict__))


@register_action
class SetHome(Action):
    """Set the home location of the vehicle.

    Changes the home location of the vehicle. Future `ReturnToHome`
    commands will move the vehicle to the provided location instead
    of its starting position.

    Attributes:
        location (common.Location): new home location
    """

    location: common.Location

    async def execute(self) -> Response:
        """Execute the SetHome action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_home(**self.__dict__))


@register_action
class ReturnToHome(Action):
    """Order the vehicle to return to its home position.

    Causes the vehicle to return to its home position. If the home position
    has not been explicitly set, this will be its start position (defined
    as its takeoff position for UAVs). If the home position has been
    explicitly set, by `SetHome`, the vehicle will return to that
    position instead.
    """

    async def execute(self) -> Response:
        """Execute the ReturnToHome action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.return_to_home(**self.__dict__))


@register_action
class SetGlobalPosition(Action):
    """Order the vehicle to move to a global position.

    Causes the vehicle to transit to the provided global position. The vehicle
    will interpret the heading of travel according to `heading_mode`:
    - `TO_TARGET` -> turn to face the target position bearing
    - `HEADING_START` -> turn to face the provided heading in the global position object.

    This will be the heading the vehicle maintains for the duration of transit.
    Generally only UAVs will support `HEADING_START`.

    The vehicle will move towards the target at the specified maximum velocity
    until the vehicle has reached its destination. Error tolerance is determined
    by the driver. Maximum velocity is interpreted from `max_velocity` as follows:
    - `x_vel` -> maximum _horizontal_ velocity
    - `y_vel` -> ignored
    - `z_vel` -> maximum _vertical_ velocity _(UAV only)_

    If no maximum velocity is provided, the driver will use a preset speed usually
    determined by the manufacturer or hardware settings.

    _(UAV only)_ During motion, the vehicle will also ascend or descend towards the
    target altitude, linearly interpolating this movement over the duration of
    travel. The vehicle will interpret altitude from `altitude_mode` as follows:
    - `ABSOLUTE` -> altitude is relative to MSL (Mean Sea Level)
    - `RELATIVE` -> altitude is relative to take off position

    Attributes:
        location (common.Location): target global position
        heading_mode (Optional[params.HeadingMode]): determines how the vehicle will orient during transit (default: `TO_TARGET`)
        altitude_mode (Optional[params.AltitudeMode]): determines how the vehicle will interpret altitude (default: `ABSOLUTE`)
        max_velocity (Optional[common.Velocity]): maximum velocity during transit
    """

    location: common.Location
    heading_mode: params.HeadingMode | None
    altitude_mode: params.AltitudeMode | None
    max_velocity: common.Velocity | None

    async def execute(self) -> Response:
        """Execute the SetGlobalPosition action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_global_position(**self.__dict__))


@register_action
class SetRelativePosition(Action):
    """Order the vehicle to move to a relative position.

    Causes the vehicle to transit to the provided relative position. The vehicle
    will interpret the input position according to `frame` as follows:
    - `BODY` -> (`x`, `y`, `z`) = (forward offset, right offset, up offset) _from current position_
    - `NEU` -> (`x`, `y`, `z`) = (north offset, east offset, up offset) _from start position_

    The vehicle will move towards the target at the specified maximum velocity
    until the vehicle has reached its destination. Error tolerance is determined
    by the driver. Maximum velocity is interpreted from `max_velocity` as follows:
    - `x_vel` -> maximum _horizontal_ velocity
    - `y_vel` -> ignored
    - `z_vel` -> maximum _vertical_ velocity _(UAV only)_

    If no maximum velocity is provided, the driver will use a preset speed usually
    determined by the manufacturer or hardware settings.

    Attributes:
        position (common.Position): target relative position
        max_velocity (Optional[common.Velocity]): maximum velocity during transit
        frame (Optional[params.ReferenceFrame]): frame of reference
    """

    position: common.Position
    max_velocity: common.Velocity | None
    frame: params.ReferenceFrame | None

    async def execute(self) -> Response:
        """Execute the SetRelativePosition action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_relative_position(**self.__dict__))


@register_action
class SetVelocity(Action):
    """Order the vehicle to accelerate to a velocity.

    Causes the vehicle to accelerate until it reaches a provided velocity.
    Error tolerance is determined by the driver. The vehicle will interpret
    the input velocity according to `frame` as follows:
    - `BODY` -> (`x_vel`, `y_vel`, `z_vel`) = (forward velocity, right velocity, up velocity)
    - `NEU` -> (`x_vel`, `y_vel`, `z_vel`) = (north velocity, east velocity, up velocity)

    Attributes:
        velocity (common.Velocity): target velocity
        frame (Optional[params.ReferenceFrame]): frame of reference
    """

    velocity: common.Velocity
    frame: params.ReferenceFrame | None

    async def execute(self) -> Response:
        """Execute the SetVelocity action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_velocity(**self.__dict__))


@register_action
class SetHeading(Action):
    """Order the vehicle to set a new heading.

    Causes the vehicle to turn to face the provided global position. The vehicle
    will interpret the final heading according to `heading_mode`:
    - `TO_TARGET` -> turn to face the target position bearing
    - `HEADING_START` -> turn to face the provided heading in the global position object.

    Attributes:
        location (common.Location): target heading or global location to look at
        heading_mode (Optional[params.HeadingMode]): determines how the vehicle will orient
    """

    location: common.Location
    heading_mode: params.HeadingMode | None

    async def execute(self) -> Response:
        """Execute the SetHeading action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_heading(**self.__dict__))

    
@register_action
class SetGimbalPose(Action):
    """Order the vehicle to set the pose of a gimbal.

    Causes the vehicle to actuate a gimbal to a new pose. The vehicle
    will interpret the new pose type from `pose_mode` as follows:
    - `ABSOLUTE` -> absolute angle
    - `RELATIVE` -> angle relative to current position
    - `VELOCITY` -> angular velocities

    The vehicle will interpret the new pose angles according to `frame`
    as follows:
    - `BODY` -> (`pitch`, `roll`, `yaw`) = (body pitch, body roll, body yaw)
    - `NEU` -> (`pitch`, `roll`, `yaw`) = (body pitch, body roll, global yaw)

    Attributes:
        gimbal_id (int): ID of the target gimbal
        pose (common.Pose): target pose
        pose_mode (Optional[params.PoseMode]): specifies how to interpret the target pose
        frame (Optional[params.ReferenceFrame]): frame of reference
    """

    gimbal_id: int
    pose: common.Pose
    pose_mode: params.PoseMode | None
    frame: params.ReferenceFrame | None

    async def execute(self) -> Response:
        """Execute the SetGimbalPose action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await consume_last(types.VEHICLE.set_gimbal_pose(**self.__dict__))


@register_action
class ConfigureImagingSensorStream(Action):
    """Configure the vehicle imaging stream.

    Sets which imaging sensors are streaming and sets their target
    frame rates.

    Attributes:
        configurations (List[params.ImagingSensorConfiguration]): list of configurations to be updated
    """

    configurations: list[params.ImagingSensorConfiguration]

    async def execute(self) -> Response:
        """Execute the ConfigureImagingSensorStream action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.configure_imaging_sensor_stream(**self.__dict__)


@register_action
class ConfigureTelemetryStream(Action):
    """Configure the vehicle telemetry stream.

    Sets the frequency of the telemetry stream.

    Attributes:
        frequency (int): target frequency of telemetry generation, in Hz
    """

    frequency: int

    async def execute(self) -> Response:
        """Execute the ConfigureTelemetryStream action.

        Returns:
            Response: response generated by the RPC call.
        """
        return await types.VEHICLE.configure_telemetry_stream(**self.__dict__)
