"""This module contains the base class for a motion.
There are four basic motion types that are supported:
- Absolute motion: The desired absolute coordinates of a limb or joint in the chosen reference frame.
- Relative motion: The displacement from the current position of a limb or joint (frame-independent).
- Velocity motion: The desired absolute velocity of a limb or joint (frame-independent).
- Torque motion: The desired torque of a limb or joint (frame-independent).
The bounds is a list of two floats representing the lower and upper bounds of the motion.
The shape is a tuple of integers representing the shape of the motion.
The reference_frame is a string representing the reference frame for the coordinates (only applies to absolute motions).
To create a new Pydantic model for a motion, inherit from the Motion class and define pydantic fields with the MotionField,
function as you would with any other Pydantic field.
Example:
    from mbodied_agents.motion import Motion, AbsoluteMotionField, MotionField, MotionType, VelocityMotionField
    from mbodied_agents.data.sample import Sample
    class Twist(Motion):
        x: float = VelocityMotionField(default=0.0, bounds=[-1.0, 1.0])
        y: float = VelocityMotionField(default=0.0, bounds=[-1.0, 1.0])
        z: float = VelocityMotionField(default=0.0, bounds=[-1.0, 1.0])
        roll: float = VelocityMotionField(default=0.0, bounds=['-pi', 'pi'])
        pitch: float = VelocityMotionField(default=0.0, bounds=['-pi', 'pi'])
        yaw: float = VelocityMotionField(default=0.0, bounds=['-pi', 'pi'])
This automatically generates a Pydantic model with the specified fields and the additional properties of a motion.
It is vectorizable, serializable, and validated according to its type. Furthermore, convience methods from
the class allow for direct conversion to numpy, pytorch, and gym spaces.
See the Sample class documentation for more information: https://mbodi-ai-mbodied-agents.readthedocs-hosted.com/en/latest/
See the Pydantic documentation for more information on how to define Pydantic models: https://pydantic-docs.helpmanual.io/
"""
from typing import Any
from pydantic import ConfigDict, Field
from pydantic_core import PydanticUndefined
from typing_extensions import Literal
from mbodied.types.sample import Sample
MotionType = Literal[
    "UNSPECIFIED",
    "OTHER",
    "ABSOLUTE",
    "RELATIVE",
    "VELOCITY",
    "TORQUE",
]
[docs]
def MotionField(  # noqa
    default: Any = PydanticUndefined,  # noqa: N805
    bounds: list[float] | None = None,  # noqa: N802, D417
    shape: tuple[int] | None = None,
    description: str | None = None,
    motion_type: MotionType = "UNSPECIFIED",
    **kwargs,
) -> Any:
    """Field for a motion.
    Args:
        default: Default value for the field.
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
        motion_type: Type of the motion. Can be "UNSPECIFIED", "OTHER", "ABSOLUTE", "RELATIVE", "VELOCITY", "TORQUE".
    """
    if description is None:
        description = ""
    if shape is not None and len(shape) > 1:
        description += f" Shape: {shape}"
    if bounds is not None:
        description += f" Bounds: {bounds}"
    if motion_type != "unspecified":
        description += f" Motion type: {motion_type}"
    return Field(
        default=default,
        description=description,
        json_schema_extra={"bounds": bounds, "motion_type": motion_type, "shape": shape},
        **kwargs,
    )  # type: ignore 
[docs]
def AbsoluteMotionField( # noqa
    default: Any = PydanticUndefined,
    bounds: list[float] | None = None,
    shape: tuple[int] | None = None,
    description: str | None = None,
    **kwargs,
) -> Any:
    """Field for an absolute motion.
    This field is used to define the shape and bounds of an absolute motion.
    Args:
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
    """
    return MotionField(
        default=default,
        bounds=bounds,
        shape=shape,
        description=description,
        motion_type="ABSOLUTE",
        **kwargs,
    ) 
[docs]
def RelativeMotionField( # noqa
    default: Any = PydanticUndefined,
    bounds: list[float] | None = None,
    shape: tuple[int] | None = None,
    description: str | None = None,
    **kwargs,
) -> Any:
    """Field for a relative motion.
    This field is used to define the shape and bounds of a relative motion.
    Args:
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
    """
    return MotionField(
        default=default,
        bounds=bounds,
        shape=shape,
        description=description,
        motion_type="RELATIVE",
        **kwargs,
    ) 
[docs]
def VelocityMotionField( # noqa
    default: Any = PydanticUndefined,
    bounds: list[float] | None = None,
    shape: tuple[int] | None = None,
    description: str | None = None,
    **kwargs,
) -> Any:
    """Field for a velocity motion.
    This field is used to define the shape and bounds of a velocity motion.
    Args:
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
    """
    return MotionField(
        default=default,
        bounds=bounds,
        shape=shape,
        description=description,
        motion_type="VELOCITY",
        **kwargs,
    ) 
[docs]
def TorqueMotionField( # noqa
    default: Any = PydanticUndefined,
    bounds: list[float] | None = None,
    shape: tuple[int] | None = None,
    description: str | None = None,
    **kwargs,
) -> Any:
    """Field for a torque motion.
    This field is used to define the shape and bounds of a torque motion.
    Args:
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
    """
    return MotionField(
        default=default,
        bounds=bounds,
        shape=shape,
        description=description,
        motion_type="TORQUE",
        **kwargs,
    ) 
[docs]
def OtherMotionField( # noqa
    default: Any = PydanticUndefined,
    bounds: list[float] | None = None,
    shape: tuple[int] | None = None,
    description: str | None = None,
    **kwargs,
) -> Any:
    """Field for an other motion.
    This field is used to define the shape and bounds of an other motion.
    Args:
        bounds: Bounds of the motion.
        shape: Shape of the motion.
        description: Description of the motion.
    """
    return MotionField(
        default=default,
        bounds=bounds,
        shape=shape,
        description=description,
        motion_type="OTHER",
        **kwargs,
    ) 
[docs]
class Motion(Sample):
    """Base class for a motion."""
    model_config: ConfigDict = ConfigDict(arbitrary_types_allowed=True, extra="forbid")