// SPDX-FileCopyrightText: 2025 Helge Eichhorn <git@helgeeichhorn.de>
//
// SPDX-License-Identifier: MPL-2.0

use lox_bodies::{DynOrigin, TryRotationalElements};
use lox_time::{
    Time,
    offsets::TryOffset,
    time_scales::{Tdb, TimeScale, Tt, Ut1},
};

use crate::{
    Cirf, DynFrame, Iau, Icrf, Itrf, Tirf,
    frames::{J2000, Mod, Pef, Teme, Tod},
    iers::{Iers1996, Iers2003, Iers2010, ReferenceSystem},
    rotations::{
        DynRotationError, Rotation, RotationError, RotationProvider, TryComposedRotation,
        TryRotation,
    },
};

// ICRF <-> IAU

impl<T, R, U> TryRotation<Icrf, Iau<R>, T> for U
where
    T: TimeScale + Copy,
    R: TryRotationalElements + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        target: Iau<R>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_iau(time, target)
    }
}

impl<T, R, U> TryRotation<Iau<R>, Icrf, T> for U
where
    T: TimeScale + Copy,
    R: TryRotationalElements + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Iau<R>,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.iau_to_icrf(time, origin)
    }
}

// ICRF <-> J2000

impl<T, U> TryRotation<Icrf, J2000, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        _target: J2000,
        _time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        Ok(self.icrf_to_j2000())
    }
}

impl<T, U> TryRotation<J2000, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: J2000,
        _target: Icrf,
        _time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        Ok(self.j2000_to_icrf())
    }
}

// J2000 <-> MOD

impl<T, U> TryRotation<J2000, Mod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: J2000,
        _target: Mod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.j2000_to_mod(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Mod<Iers1996>, J2000, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers1996>,
        _target: J2000,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_j2000(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<J2000, Mod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: J2000,
        target: Mod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.j2000_to_mod(time, ReferenceSystem::Iers2003(target.0.0))
    }
}

impl<T, U> TryRotation<Mod<Iers2003>, J2000, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Mod<Iers2003>,
        _target: J2000,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_j2000(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<J2000, Mod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: J2000,
        _target: Mod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.j2000_to_mod(time, ReferenceSystem::Iers2010)
    }
}

impl<T, U> TryRotation<Mod<Iers2010>, J2000, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers2010>,
        _target: J2000,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_j2000(time, ReferenceSystem::Iers2010)
    }
}

// ICRF <-> ITRF

impl<T, U> TryRotation<Icrf, Itrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        _target: Itrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_itrf(time)
    }
}

impl<T, U> TryRotation<Itrf, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Itrf,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.itrf_to_icrf(time)
    }
}

// ICRF <-> CIRF

impl<T, U> TryRotation<Icrf, Cirf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        _target: Cirf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_cirf(time)
    }
}

impl<T, U> TryRotation<Cirf, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Cirf,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.cirf_to_icrf(time)
    }
}

// CIRF <-> TIRF

impl<T, U> TryRotation<Cirf, Tirf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Cirf,
        _target: Tirf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.cirf_to_tirf(time)
    }
}

impl<T, U> TryRotation<Tirf, Cirf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tirf,
        _target: Cirf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tirf_to_cirf(time)
    }
}

// TIRF <-> ITRF

impl<T, U> TryRotation<Tirf, Itrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tirf,
        _target: Itrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tirf_to_itrf(time)
    }
}

impl<T, U> TryRotation<Itrf, Tirf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Itrf,
        _target: Tirf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.itrf_to_tirf(time)
    }
}

// ICRF <-> MOD

impl<T, U> TryRotation<Icrf, Mod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        _target: Mod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_mod(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Mod<Iers1996>, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers1996>,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_icrf(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Icrf, Mod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        target: Mod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_mod(time, ReferenceSystem::Iers2003(target.0.0))
    }
}

impl<T, U> TryRotation<Mod<Iers2003>, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Mod<Iers2003>,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_icrf(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Icrf, Mod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Icrf,
        _target: Mod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.icrf_to_mod(time, ReferenceSystem::Iers2010)
    }
}

impl<T, U> TryRotation<Mod<Iers2010>, Icrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers2010>,
        _target: Icrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_icrf(time, ReferenceSystem::Iers2010)
    }
}

// MOD <-> TOD

impl<T, U> TryRotation<Mod<Iers1996>, Tod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers1996>,
        _target: Tod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_tod(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Tod<Iers1996>, Mod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers1996>,
        _target: Mod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_mod(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Mod<Iers2003>, Tod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Mod<Iers2003>,
        _target: Tod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_tod(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Tod<Iers2003>, Mod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Tod<Iers2003>,
        _target: Mod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_mod(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Mod<Iers2010>, Tod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Mod<Iers2010>,
        _target: Tod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.mod_to_tod(time, ReferenceSystem::Iers2010)
    }
}

impl<T, U> TryRotation<Tod<Iers2010>, Mod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers2010>,
        _target: Mod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_mod(time, ReferenceSystem::Iers2010)
    }
}

// TOD <-> PEF

impl<T, U> TryRotation<Tod<Iers1996>, Pef<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers1996>,
        _target: Pef<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_pef(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Pef<Iers1996>, Tod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Pef<Iers1996>,
        _target: Tod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_tod(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Tod<Iers2003>, Pef<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Tod<Iers2003>,
        _target: Pef<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_pef(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Pef<Iers2003>, Tod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Pef<Iers2003>,
        _target: Tod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_tod(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Tod<Iers2010>, Pef<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers2010>,
        _target: Pef<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_pef(time, ReferenceSystem::Iers2010)
    }
}

impl<T, U> TryRotation<Pef<Iers2010>, Tod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Ut1>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Pef<Iers2010>,
        _target: Tod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_tod(time, ReferenceSystem::Iers2010)
    }
}

// PEF <-> ITRF

impl<T, U> TryRotation<Pef<Iers1996>, Itrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Pef<Iers1996>,
        _target: Itrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_itrf(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Itrf, Pef<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Itrf,
        _target: Pef<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.itrf_to_pef(time, ReferenceSystem::Iers1996)
    }
}

impl<T, U> TryRotation<Pef<Iers2003>, Itrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        origin: Pef<Iers2003>,
        _target: Itrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_itrf(time, ReferenceSystem::Iers2003(origin.0.0))
    }
}

impl<T, U> TryRotation<Itrf, Pef<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Itrf,
        target: Pef<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.itrf_to_pef(time, ReferenceSystem::Iers2003(target.0.0))
    }
}

impl<T, U> TryRotation<Pef<Iers2010>, Itrf, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Pef<Iers2010>,
        _target: Itrf,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.pef_to_itrf(time, ReferenceSystem::Iers2010)
    }
}

impl<T, U> TryRotation<Itrf, Pef<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Itrf,
        _target: Pef<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.itrf_to_pef(time, ReferenceSystem::Iers2010)
    }
}

// TOD <-> TEME

impl<T, U> TryRotation<Tod<Iers1996>, Teme, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers1996>,
        _target: Teme,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_teme(time)
    }
}

impl<T, U> TryRotation<Teme, Tod<Iers1996>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Teme,
        _target: Tod<Iers1996>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.teme_to_tod(time)
    }
}

impl<T, U> TryRotation<Tod<Iers2003>, Teme, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers2003>,
        _target: Teme,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_teme(time)
    }
}

impl<T, U> TryRotation<Teme, Tod<Iers2003>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Teme,
        _target: Tod<Iers2003>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.teme_to_tod(time)
    }
}

impl<T, U> TryRotation<Tod<Iers2010>, Teme, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Tod<Iers2010>,
        _target: Teme,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.tod_to_teme(time)
    }
}

impl<T, U> TryRotation<Teme, Tod<Iers2010>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tdb>,
{
    type Error = RotationError;

    fn try_rotation(
        &self,
        _origin: Teme,
        _target: Tod<Iers2010>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        self.teme_to_tod(time)
    }
}

// Composed Rotations

macro_rules! impl_composed {
    ($($origin:ty => $target:ty: [$($via:expr),+]),* $(,)?) => {
        $(
            impl<T, U> TryRotation<$origin, $target, T> for U
            where
                T: TimeScale + Copy,
                U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
            {
                type Error = RotationError;

                fn try_rotation(
                    &self,
                    origin: $origin,
                    target: $target,
                    time: Time<T>,
                ) -> Result<Rotation, Self::Error> {
                    (origin, $($via),+, target).try_composed_rotation(self, time)
                }
            }
        )*
    };
}

impl_composed!(
    // J2000 composed rotations
    J2000 => Cirf: [Icrf],
    J2000 => Tirf: [Icrf, Cirf],
    J2000 => Itrf: [Icrf, Cirf, Tirf],
    J2000 => Iau<DynOrigin>: [Icrf],
    J2000 => Tod<Iers1996>: [Mod(Iers1996)],
    J2000 => Tod<Iers2003>: [Mod(Iers2003::default())],
    J2000 => Tod<Iers2010>: [Mod(Iers2010)],
    J2000 => Pef<Iers1996>: [Mod(Iers1996), Tod(Iers1996)],
    J2000 => Pef<Iers2003>: [Mod(Iers2003::default()), Tod(Iers2003::default())],
    J2000 => Pef<Iers2010>: [Mod(Iers2010), Tod(Iers2010)],
    J2000 => Teme: [Mod(Iers1996), Tod(Iers1996)],
    Cirf => J2000: [Icrf],
    Tirf => J2000: [Cirf, Icrf],
    Itrf => J2000: [Tirf, Cirf, Icrf],
    Iau<DynOrigin> => J2000: [Icrf],
    Tod<Iers1996> => J2000: [Mod(Iers1996)],
    Tod<Iers2003> => J2000: [Mod(Iers2003::default())],
    Tod<Iers2010> => J2000: [Mod(Iers2010)],
    Pef<Iers1996> => J2000: [Tod(Iers1996), Mod(Iers1996)],
    Pef<Iers2003> => J2000: [Tod(Iers2003::default()), Mod(Iers2003::default())],
    Pef<Iers2010> => J2000: [Tod(Iers2010), Mod(Iers2010)],
    Teme => J2000: [Tod(Iers1996), Mod(Iers1996)],

    Cirf => Iau<DynOrigin>: [Icrf],
    Cirf => Itrf: [Tirf],
    Cirf => Mod<Iers1996>: [Icrf],
    Cirf => Mod<Iers2003>: [Icrf],
    Cirf => Mod<Iers2010>: [Icrf],
    Cirf => Pef<Iers1996>: [Icrf, Mod(Iers1996), Tod(Iers1996)],
    Cirf => Pef<Iers2003>: [Icrf, Mod(Iers2003::default()), Tod(Iers2003::default())],
    Cirf => Pef<Iers2010>: [Icrf, Mod(Iers2010), Tod(Iers2010)],
    Cirf => Teme: [Icrf, Mod(Iers1996), Tod(Iers1996)],
    Cirf => Tod<Iers1996>: [Icrf, Mod(Iers1996)],
    Cirf => Tod<Iers2003>: [Icrf, Mod(Iers2003::default())],
    Cirf => Tod<Iers2010>: [Icrf, Mod(Iers2010)],
    Iau<DynOrigin> => Cirf: [Icrf],
    Iau<DynOrigin> => Itrf: [Icrf, Cirf, Tirf],
    Iau<DynOrigin> => Tirf: [Icrf, Cirf],
    Iau<DynOrigin> => Mod<Iers1996>: [Icrf],
    Iau<DynOrigin> => Mod<Iers2003>: [Icrf],
    Iau<DynOrigin> => Mod<Iers2010>: [Icrf],
    Iau<DynOrigin> => Tod<Iers1996>: [Icrf, Mod(Iers1996)],
    Iau<DynOrigin> => Tod<Iers2003>: [Icrf, Mod(Iers2003::default())],
    Iau<DynOrigin> => Tod<Iers2010>: [Icrf, Mod(Iers2010)],
    Iau<DynOrigin> => Pef<Iers1996>: [Icrf, Mod(Iers1996), Tod(Iers1996)],
    Iau<DynOrigin> => Pef<Iers2003>: [Icrf, Mod(Iers2003::default()), Tod(Iers2003::default())],
    Iau<DynOrigin> => Pef<Iers2010>: [Icrf, Mod(Iers2010), Tod(Iers2010)],
    Iau<DynOrigin> => Teme: [Icrf, Mod(Iers1996), Tod(Iers1996)],
    Icrf => Pef<Iers1996>: [Mod(Iers1996), Tod(Iers1996)],
    Icrf => Pef<Iers2003>: [Mod(Iers2003::default()), Tod(Iers2003::default())],
    Icrf => Pef<Iers2010>: [Mod(Iers2010), Tod(Iers2010)],
    Icrf => Teme: [Mod(Iers1996), Tod(Iers1996)],
    Icrf => Tirf: [Cirf],
    Icrf => Tod<Iers1996>: [Mod(Iers1996)],
    Icrf => Tod<Iers2003>: [Mod(Iers2003::default())],
    Icrf => Tod<Iers2010>: [Mod(Iers2010)],
    Itrf => Cirf: [Tirf],
    Itrf => Iau<DynOrigin>: [Tirf, Cirf, Icrf],
    Itrf => Mod<Iers1996>: [Pef(Iers1996), Tod(Iers1996)],
    Itrf => Mod<Iers2003>: [Pef(Iers2003::default()), Tod(Iers2003::default())],
    Itrf => Mod<Iers2010>: [Pef(Iers2010), Tod(Iers2010)],
    Itrf => Tod<Iers1996>: [Pef(Iers1996)],
    Itrf => Tod<Iers2003>: [Pef(Iers2003::default())],
    Itrf => Tod<Iers2010>: [Pef(Iers2010)],
    Itrf => Teme: [Pef(Iers1996), Tod(Iers1996)],
    Mod<Iers1996> => Cirf: [Icrf],
    Mod<Iers2003> => Cirf: [Icrf],
    Mod<Iers2010> => Cirf: [Icrf],
    Mod<Iers1996> => Tirf: [Icrf, Cirf],
    Mod<Iers2003> => Tirf: [Icrf, Cirf],
    Mod<Iers2010> => Tirf: [Icrf, Cirf],
    Mod<Iers1996> => Itrf: [Tod(Iers1996), Pef(Iers1996)],
    Mod<Iers2003> => Itrf: [Tod(Iers2003::default()), Pef(Iers2003::default())],
    Mod<Iers2010> => Itrf: [Tod(Iers2010), Pef(Iers2010)],
    Mod<Iers1996> => Iau<DynOrigin>: [Icrf],
    Mod<Iers2003> => Iau<DynOrigin>: [Icrf],
    Mod<Iers2010> => Iau<DynOrigin>: [Icrf],
    Mod<Iers1996> => Pef<Iers1996>: [Tod(Iers1996)],
    Mod<Iers2003> => Pef<Iers2003>: [Tod(Iers2003::default())],
    Mod<Iers2010> => Pef<Iers2010>: [Tod(Iers2010)],
    Mod<Iers1996> => Teme: [Tod(Iers1996)],
    Mod<Iers2003> => Teme: [Tod(Iers2003::default())],
    Mod<Iers2010> => Teme: [Tod(Iers2010)],
    Pef<Iers1996> => Cirf: [Tod(Iers1996), Mod(Iers1996), Icrf],
    Pef<Iers2003> => Cirf: [Tod(Iers2003::default()), Mod(Iers2003::default()), Icrf],
    Pef<Iers2010> => Cirf: [Tod(Iers2010), Mod(Iers2010), Icrf],
    Pef<Iers1996> => Icrf: [Tod(Iers1996), Mod(Iers1996)],
    Pef<Iers2003> => Icrf: [Tod(Iers2003::default()), Mod(Iers2003::default())],
    Pef<Iers2010> => Icrf: [Tod(Iers2010), Mod(Iers2010)],
    Pef<Iers1996> => Tirf: [Itrf],
    Pef<Iers2003> => Tirf: [Itrf],
    Pef<Iers2010> => Tirf: [Itrf],
    Pef<Iers1996> => Iau<DynOrigin>: [Tod(Iers1996), Mod(Iers1996), Icrf],
    Pef<Iers2003> => Iau<DynOrigin>: [Tod(Iers2003::default()), Mod(Iers2003::default()), Icrf],
    Pef<Iers2010> => Iau<DynOrigin>: [Tod(Iers2010), Mod(Iers2010), Icrf],
    Pef<Iers1996> => Mod<Iers1996>: [Tod(Iers1996)],
    Pef<Iers2003> => Mod<Iers2003>: [Tod(Iers2003::default())],
    Pef<Iers2010> => Mod<Iers2010>: [Tod(Iers2010)],
    Teme => Cirf: [Tod(Iers1996), Mod(Iers1996), Icrf],
    Teme => Icrf: [Tod(Iers1996), Mod(Iers1996)],
    Teme => Tirf: [Tod(Iers1996), Mod(Iers1996), Icrf, Cirf],
    Teme => Itrf: [Tod(Iers1996), Pef(Iers1996)],
    Teme => Iau<DynOrigin>: [Tod(Iers1996), Mod(Iers1996), Icrf],
    Teme => Mod<Iers1996>: [Tod(Iers1996)],
    Teme => Mod<Iers2003>: [Tod(Iers2003::default())],
    Teme => Mod<Iers2010>: [Tod(Iers2010)],
    Teme => Pef<Iers1996>: [Tod(Iers1996)],
    Teme => Pef<Iers2003>: [Tod(Iers2003::default())],
    Teme => Pef<Iers2010>: [Tod(Iers2010)],
    Tirf => Iau<DynOrigin>: [Cirf, Icrf],
    Tirf => Icrf: [Cirf],
    Tirf => Mod<Iers1996>: [Cirf, Icrf],
    Tirf => Mod<Iers2003>: [Cirf, Icrf],
    Tirf => Mod<Iers2010>: [Cirf, Icrf],
    Tirf => Pef<Iers1996>: [Itrf],
    Tirf => Pef<Iers2003>: [Itrf],
    Tirf => Pef<Iers2010>: [Itrf],
    Tirf => Teme: [Cirf, Icrf, Mod(Iers1996), Tod(Iers1996)],
    Tirf => Tod<Iers1996>: [Cirf, Icrf, Mod(Iers1996)],
    Tirf => Tod<Iers2003>: [Cirf, Icrf, Mod(Iers2003::default())],
    Tirf => Tod<Iers2010>: [Cirf, Icrf, Mod(Iers2010)],
    Tod<Iers1996> => Cirf: [Mod(Iers1996), Icrf],
    Tod<Iers2003> => Cirf: [Mod(Iers2003::default()), Icrf],
    Tod<Iers2010> => Cirf: [Mod(Iers2010), Icrf],
    Tod<Iers1996> => Icrf: [Mod(Iers1996)],
    Tod<Iers2003> => Icrf: [Mod(Iers2003::default())],
    Tod<Iers2010> => Icrf: [Mod(Iers2010)],
    Tod<Iers1996> => Tirf: [Mod(Iers1996), Icrf, Cirf],
    Tod<Iers2003> => Tirf: [Mod(Iers2003::default()), Icrf, Cirf],
    Tod<Iers2010> => Tirf: [Mod(Iers2010), Icrf, Cirf],
    Tod<Iers1996> => Itrf: [Pef(Iers1996)],
    Tod<Iers2003> => Itrf: [Pef(Iers2003::default())],
    Tod<Iers2010> => Itrf: [Pef(Iers2010)],
    Tod<Iers1996> => Iau<DynOrigin>: [Mod(Iers1996), Icrf],
    Tod<Iers2003> => Iau<DynOrigin>: [Mod(Iers2003::default()), Icrf],
    Tod<Iers2010> => Iau<DynOrigin>: [Mod(Iers2010), Icrf],
    Pef<Iers1996> => Teme: [Tod(Iers1996)],
    Pef<Iers2003> => Teme: [Tod(Iers2003::default())],
    Pef<Iers2010> => Teme: [Tod(Iers2010)],
);

// Dynamic

impl<T, U> TryRotation<DynFrame, DynFrame, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
{
    type Error = DynRotationError;

    fn try_rotation(
        &self,
        origin: DynFrame,
        target: DynFrame,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        if origin == target {
            return Ok(Rotation::IDENTITY);
        }
        match (origin, target) {
            (DynFrame::Icrf, DynFrame::Cirf) => Ok(self.try_rotation(Icrf, Cirf, time)?),
            (DynFrame::Icrf, DynFrame::Tirf) => Ok(self.try_rotation(Icrf, Tirf, time)?),
            (DynFrame::Icrf, DynFrame::Itrf) => Ok(self.try_rotation(Icrf, Itrf, time)?),
            (DynFrame::Icrf, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(Icrf, Iau::try_new(body)?, time)?)
            }
            (DynFrame::Icrf, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Icrf, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Icrf, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Icrf, Mod(Iers2010), time)?),
            },
            (DynFrame::Icrf, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Icrf, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Icrf, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Icrf, Tod(Iers2010), time)?),
            },
            (DynFrame::Icrf, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Icrf, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Icrf, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Icrf, Pef(Iers2010), time)?),
            },
            (DynFrame::Icrf, DynFrame::Teme) => Ok(self.try_rotation(Icrf, Teme, time)?),
            (DynFrame::Icrf, DynFrame::J2000) => Ok(self.try_rotation(Icrf, J2000, time)?),
            (DynFrame::J2000, DynFrame::Icrf) => Ok(self.try_rotation(J2000, Icrf, time)?),
            (DynFrame::J2000, DynFrame::Cirf) => Ok(self.try_rotation(J2000, Cirf, time)?),
            (DynFrame::J2000, DynFrame::Tirf) => Ok(self.try_rotation(J2000, Tirf, time)?),
            (DynFrame::J2000, DynFrame::Itrf) => Ok(self.try_rotation(J2000, Itrf, time)?),
            (DynFrame::J2000, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(J2000, Iau::try_new(body)?, time)?)
            }
            (DynFrame::J2000, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(J2000, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(J2000, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(J2000, Mod(Iers2010), time)?),
            },
            (DynFrame::J2000, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(J2000, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(J2000, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(J2000, Tod(Iers2010), time)?),
            },
            (DynFrame::J2000, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(J2000, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(J2000, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(J2000, Pef(Iers2010), time)?),
            },
            (DynFrame::J2000, DynFrame::Teme) => Ok(self.try_rotation(J2000, Teme, time)?),
            (DynFrame::Cirf, DynFrame::Icrf) => Ok(self.try_rotation(Cirf, Icrf, time)?),
            (DynFrame::Cirf, DynFrame::Tirf) => Ok(self.try_rotation(Cirf, Tirf, time)?),
            (DynFrame::Cirf, DynFrame::Itrf) => Ok(self.try_rotation(Cirf, Itrf, time)?),
            (DynFrame::Cirf, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(Cirf, Iau::try_new(body)?, time)?)
            }
            (DynFrame::Cirf, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Cirf, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Cirf, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Cirf, Mod(Iers2010), time)?),
            },
            (DynFrame::Cirf, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Cirf, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Cirf, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Cirf, Tod(Iers2010), time)?),
            },
            (DynFrame::Cirf, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Cirf, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Cirf, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Cirf, Pef(Iers2010), time)?),
            },
            (DynFrame::Cirf, DynFrame::Teme) => Ok(self.try_rotation(Cirf, Teme, time)?),
            (DynFrame::Cirf, DynFrame::J2000) => Ok(self.try_rotation(Cirf, J2000, time)?),
            (DynFrame::Tirf, DynFrame::Icrf) => Ok(self.try_rotation(Tirf, Icrf, time)?),
            (DynFrame::Tirf, DynFrame::Cirf) => Ok(self.try_rotation(Tirf, Cirf, time)?),
            (DynFrame::Tirf, DynFrame::Itrf) => Ok(self.try_rotation(Tirf, Itrf, time)?),
            (DynFrame::Tirf, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(Tirf, Iau::try_new(body)?, time)?)
            }
            (DynFrame::Tirf, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tirf, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tirf, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tirf, Mod(Iers2010), time)?),
            },
            (DynFrame::Tirf, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tirf, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tirf, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tirf, Tod(Iers2010), time)?),
            },
            (DynFrame::Tirf, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tirf, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tirf, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tirf, Pef(Iers2010), time)?),
            },
            (DynFrame::Tirf, DynFrame::Teme) => Ok(self.try_rotation(Tirf, Teme, time)?),
            (DynFrame::Tirf, DynFrame::J2000) => Ok(self.try_rotation(Tirf, J2000, time)?),
            (DynFrame::Itrf, DynFrame::Icrf) => Ok(self.try_rotation(Itrf, Icrf, time)?),
            (DynFrame::Itrf, DynFrame::Cirf) => Ok(self.try_rotation(Itrf, Cirf, time)?),
            (DynFrame::Itrf, DynFrame::Tirf) => Ok(self.try_rotation(Itrf, Tirf, time)?),
            (DynFrame::Itrf, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(Itrf, Iau::try_new(body)?, time)?)
            }
            (DynFrame::Itrf, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Itrf, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Itrf, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Itrf, Mod(Iers2010), time)?),
            },
            (DynFrame::Itrf, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Itrf, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Itrf, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Itrf, Tod(Iers2010), time)?),
            },
            (DynFrame::Itrf, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Itrf, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Itrf, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Itrf, Pef(Iers2010), time)?),
            },
            (DynFrame::Itrf, DynFrame::Teme) => Ok(self.try_rotation(Itrf, Teme, time)?),
            (DynFrame::Itrf, DynFrame::J2000) => Ok(self.try_rotation(Itrf, J2000, time)?),
            (DynFrame::Iau(body), DynFrame::Icrf) => {
                Ok(self.try_rotation(Iau::try_new(body)?, Icrf, time)?)
            }
            (DynFrame::Iau(body), DynFrame::Cirf) => {
                Ok(self.try_rotation(Iau::try_new(body)?, Cirf, time)?)
            }
            (DynFrame::Iau(body), DynFrame::Tirf) => {
                Ok(self.try_rotation(Iau::try_new(body)?, Tirf, time)?)
            }
            (DynFrame::Iau(body), DynFrame::Itrf) => {
                Ok(self.try_rotation(Iau::try_new(body)?, Itrf, time)?)
            }
            (DynFrame::Iau(body), DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Mod(Iers1996), time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Iau::try_new(body)?,
                    Mod(Iers2003(iau2000_model)),
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Mod(Iers2010), time)?)
                }
            },
            (DynFrame::Iau(body), DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Tod(Iers1996), time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Iau::try_new(body)?,
                    Tod(Iers2003(iau2000_model)),
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Tod(Iers2010), time)?)
                }
            },
            (DynFrame::Iau(body), DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Pef(Iers1996), time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Iau::try_new(body)?,
                    Pef(Iers2003(iau2000_model)),
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Iau::try_new(body)?, Pef(Iers2010), time)?)
                }
            },
            (DynFrame::Iau(body), DynFrame::Teme) => {
                Ok(self.try_rotation(Iau::try_new(body)?, Teme, time)?)
            }
            (DynFrame::Iau(body), DynFrame::J2000) => {
                Ok(self.try_rotation(Iau::try_new(body)?, J2000, time)?)
            }
            (DynFrame::Mod(sys), DynFrame::Icrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), Icrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), Icrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), Icrf, time)?),
            },
            (DynFrame::Mod(sys), DynFrame::Cirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), Cirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), Cirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), Cirf, time)?),
            },
            (DynFrame::Mod(sys), DynFrame::Tirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), Tirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), Tirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), Tirf, time)?),
            },
            (DynFrame::Mod(sys), DynFrame::Itrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), Itrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), Itrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), Itrf, time)?),
            },
            (DynFrame::Mod(sys), DynFrame::Iau(body)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Mod(Iers1996), Iau::try_new(body)?, time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Mod(Iers2003(iau2000_model)),
                    Iau::try_new(body)?,
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Mod(Iers2010), Iau::try_new(body)?, time)?)
                }
            },
            (DynFrame::Mod(sys1), DynFrame::Tod(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Mod(Iers1996), Tod(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Mod(Iers2003(iau2000_model)),
                        Tod(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Mod(Iers2010), Tod(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Mod(sys1), DynFrame::Pef(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Mod(Iers1996), Pef(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Mod(Iers2003(iau2000_model)),
                        Pef(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Mod(Iers2010), Pef(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Mod(sys), DynFrame::Teme) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), Teme, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), Teme, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), Teme, time)?),
            },
            (DynFrame::Mod(sys), DynFrame::J2000) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Mod(Iers1996), J2000, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Mod(Iers2003(iau2000_model)), J2000, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Mod(Iers2010), J2000, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::Icrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), Icrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), Icrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), Icrf, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::Cirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), Cirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), Cirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), Cirf, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::Tirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), Tirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), Tirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), Tirf, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::Itrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), Itrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), Itrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), Itrf, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::Iau(body)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Tod(Iers1996), Iau::try_new(body)?, time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Tod(Iers2003(iau2000_model)),
                    Iau::try_new(body)?,
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Tod(Iers2010), Iau::try_new(body)?, time)?)
                }
            },
            (DynFrame::Tod(sys1), DynFrame::Mod(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Tod(Iers1996), Mod(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Tod(Iers2003(iau2000_model)),
                        Mod(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Tod(Iers2010), Mod(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Tod(sys1), DynFrame::Pef(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Tod(Iers1996), Pef(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Tod(Iers2003(iau2000_model)),
                        Pef(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Tod(Iers2010), Pef(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Tod(sys), DynFrame::Teme) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), Teme, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), Teme, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), Teme, time)?),
            },
            (DynFrame::Tod(sys), DynFrame::J2000) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Tod(Iers1996), J2000, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Tod(Iers2003(iau2000_model)), J2000, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Tod(Iers2010), J2000, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::Icrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), Icrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), Icrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), Icrf, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::Cirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), Cirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), Cirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), Cirf, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::Tirf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), Tirf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), Tirf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), Tirf, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::Itrf) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), Itrf, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), Itrf, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), Itrf, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::Iau(body)) => match sys {
                ReferenceSystem::Iers1996 => {
                    Ok(self.try_rotation(Pef(Iers1996), Iau::try_new(body)?, time)?)
                }
                ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                    Pef(Iers2003(iau2000_model)),
                    Iau::try_new(body)?,
                    time,
                )?),
                ReferenceSystem::Iers2010 => {
                    Ok(self.try_rotation(Pef(Iers2010), Iau::try_new(body)?, time)?)
                }
            },
            (DynFrame::Pef(sys1), DynFrame::Mod(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Pef(Iers1996), Mod(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Pef(Iers2003(iau2000_model)),
                        Mod(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Pef(Iers2010), Mod(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Pef(sys1), DynFrame::Tod(sys2)) => {
                if sys1 != sys2 {
                    return Err(DynRotationError::IncompatibleReferenceSystems);
                }
                match sys1 {
                    ReferenceSystem::Iers1996 => {
                        Ok(self.try_rotation(Pef(Iers1996), Tod(Iers1996), time)?)
                    }
                    ReferenceSystem::Iers2003(iau2000_model) => Ok(self.try_rotation(
                        Pef(Iers2003(iau2000_model)),
                        Tod(Iers2003(iau2000_model)),
                        time,
                    )?),
                    ReferenceSystem::Iers2010 => {
                        Ok(self.try_rotation(Pef(Iers2010), Tod(Iers2010), time)?)
                    }
                }
            }
            (DynFrame::Pef(sys), DynFrame::Teme) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), Teme, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), Teme, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), Teme, time)?),
            },
            (DynFrame::Pef(sys), DynFrame::J2000) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Pef(Iers1996), J2000, time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Pef(Iers2003(iau2000_model)), J2000, time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Pef(Iers2010), J2000, time)?),
            },
            (DynFrame::Teme, DynFrame::Icrf) => Ok(self.try_rotation(Teme, Icrf, time)?),
            (DynFrame::Teme, DynFrame::Cirf) => Ok(self.try_rotation(Teme, Cirf, time)?),
            (DynFrame::Teme, DynFrame::Tirf) => Ok(self.try_rotation(Teme, Tirf, time)?),
            (DynFrame::Teme, DynFrame::Itrf) => Ok(self.try_rotation(Teme, Itrf, time)?),
            (DynFrame::Teme, DynFrame::Iau(body)) => {
                Ok(self.try_rotation(Teme, Iau::try_new(body)?, time)?)
            }
            (DynFrame::Teme, DynFrame::Mod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Teme, Mod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Teme, Mod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Teme, Mod(Iers2010), time)?),
            },
            (DynFrame::Teme, DynFrame::Tod(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Teme, Tod(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Teme, Tod(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Teme, Tod(Iers2010), time)?),
            },
            (DynFrame::Teme, DynFrame::Pef(sys)) => match sys {
                ReferenceSystem::Iers1996 => Ok(self.try_rotation(Teme, Pef(Iers1996), time)?),
                ReferenceSystem::Iers2003(iau2000_model) => {
                    Ok(self.try_rotation(Teme, Pef(Iers2003(iau2000_model)), time)?)
                }
                ReferenceSystem::Iers2010 => Ok(self.try_rotation(Teme, Pef(Iers2010), time)?),
            },
            (DynFrame::Teme, DynFrame::J2000) => Ok(self.try_rotation(Teme, J2000, time)?),
            (_, _) => Ok(Rotation::IDENTITY),
        }
    }
}

// Mixed DynFrame <-> ConcreteFrame rotations
//
// These impls allow rotation providers to handle `DynFrame → R` and `R → DynFrame`
// for concrete frame types. They delegate to the existing `DynFrame → DynFrame` impl
// by converting the concrete frame via `Into<DynFrame>`.

macro_rules! impl_mixed_dyn_rotation {
    ($($frame:ty),* $(,)?) => {
        $(
            impl<T, U> TryRotation<DynFrame, $frame, T> for U
            where
                T: TimeScale + Copy,
                U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
            {
                type Error = DynRotationError;

                fn try_rotation(
                    &self,
                    origin: DynFrame,
                    target: $frame,
                    time: Time<T>,
                ) -> Result<Rotation, Self::Error> {
                    let target_dyn: DynFrame = target.into();
                    <Self as TryRotation<DynFrame, DynFrame, T>>::try_rotation(
                        self, origin, target_dyn, time,
                    )
                }
            }

            impl<T, U> TryRotation<$frame, DynFrame, T> for U
            where
                T: TimeScale + Copy,
                U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
            {
                type Error = DynRotationError;

                fn try_rotation(
                    &self,
                    origin: $frame,
                    target: DynFrame,
                    time: Time<T>,
                ) -> Result<Rotation, Self::Error> {
                    let origin_dyn: DynFrame = origin.into();
                    <Self as TryRotation<DynFrame, DynFrame, T>>::try_rotation(
                        self, origin_dyn, target, time,
                    )
                }
            }
        )*
    };
}

impl_mixed_dyn_rotation!(
    Icrf,
    J2000,
    Cirf,
    Tirf,
    Itrf,
    Teme,
    Mod<Iers1996>,
    Mod<Iers2003>,
    Mod<Iers2010>,
    Tod<Iers1996>,
    Tod<Iers2003>,
    Tod<Iers2010>,
    Pef<Iers1996>,
    Pef<Iers2003>,
    Pef<Iers2010>,
);

// Iau<DynOrigin> mixed impls — used by the Dyn path for body-fixed frames.
impl<T, U> TryRotation<DynFrame, Iau<DynOrigin>, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
{
    type Error = DynRotationError;

    fn try_rotation(
        &self,
        origin: DynFrame,
        target: Iau<DynOrigin>,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        let target_dyn: DynFrame = target.into();
        <Self as TryRotation<DynFrame, DynFrame, T>>::try_rotation(self, origin, target_dyn, time)
    }
}

impl<T, U> TryRotation<Iau<DynOrigin>, DynFrame, T> for U
where
    T: TimeScale + Copy,
    U: RotationProvider<T> + TryOffset<T, Tt> + TryOffset<T, Tdb> + TryOffset<T, Ut1>,
{
    type Error = DynRotationError;

    fn try_rotation(
        &self,
        origin: Iau<DynOrigin>,
        target: DynFrame,
        time: Time<T>,
    ) -> Result<Rotation, Self::Error> {
        let origin_dyn: DynFrame = origin.into();
        <Self as TryRotation<DynFrame, DynFrame, T>>::try_rotation(self, origin_dyn, target, time)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::providers::DefaultRotationProvider;
    use lox_time::time_scales::Tai;

    fn tai_j2000() -> Time<Tai> {
        Time::j2000(Tai)
    }

    #[test]
    fn test_mixed_icrf_to_dynframe() {
        let time = tai_j2000();
        let rot: Rotation = DefaultRotationProvider
            .try_rotation(Icrf, DynFrame::Icrf, time)
            .unwrap();
        assert!(
            rot.position_matrix()
                .abs_diff_eq(glam::DMat3::IDENTITY, 1e-14)
        );
    }

    #[test]
    fn test_mixed_dynframe_to_icrf() {
        let time = tai_j2000();
        let rot: Rotation = DefaultRotationProvider
            .try_rotation(DynFrame::Icrf, Icrf, time)
            .unwrap();
        assert!(
            rot.position_matrix()
                .abs_diff_eq(glam::DMat3::IDENTITY, 1e-14)
        );
    }

    #[test]
    fn test_mixed_iau_dynorigin_to_dynframe() {
        let time = tai_j2000();
        let iau_earth = Iau::try_new(DynOrigin::Earth).unwrap();
        let rot: Rotation = DefaultRotationProvider
            .try_rotation(iau_earth, DynFrame::Icrf, time)
            .unwrap();
        assert!(
            !rot.position_matrix()
                .abs_diff_eq(glam::DMat3::IDENTITY, 1e-6)
        );
    }

    #[test]
    fn test_mixed_dynframe_to_iau_dynorigin() {
        let time = tai_j2000();
        let iau_earth = Iau::try_new(DynOrigin::Earth).unwrap();
        let rot: Rotation = DefaultRotationProvider
            .try_rotation(DynFrame::Icrf, iau_earth, time)
            .unwrap();
        assert!(
            !rot.position_matrix()
                .abs_diff_eq(glam::DMat3::IDENTITY, 1e-6)
        );
    }

    #[test]
    fn test_mixed_roundtrip_icrf_iau_earth() {
        let time = tai_j2000();
        let iau_earth = Iau::try_new(DynOrigin::Earth).unwrap();
        let rot_fwd: Rotation = DefaultRotationProvider
            .try_rotation(Icrf, DynFrame::Iau(DynOrigin::Earth), time)
            .unwrap();
        let rot_bwd: Rotation = DefaultRotationProvider
            .try_rotation(iau_earth, DynFrame::Icrf, time)
            .unwrap();
        let product = rot_fwd.position_matrix() * rot_bwd.position_matrix();
        assert!(product.abs_diff_eq(glam::DMat3::IDENTITY, 1e-14));
    }
}
