use nalgebra as na;

#[cxx::bridge(namespace = "roplat")]
mod ffi {
    pub struct Vec1 {
        data: f64,
    }
    pub struct Vec2 {
        data: [f64; 2],
    }
    pub struct Vec3 {
        data: [f64; 3],
    }
    pub struct Vec4 {
        data: [f64; 4],
    }
    pub struct Vec5 {
        data: [f64; 5],
    }
    pub struct Vec6 {
        data: [f64; 6],
    }

    pub struct VecX {
        data: Vec<f64>,
    }

    pub struct Mat1 {
        data: [f64; 1],
    }

    pub struct Mat2 {
        data: [f64; 4],
    }

    pub struct Mat3 {
        data: [f64; 9],
    }

    pub struct Mat4 {
        data: [f64; 16],
    }

    pub struct Mat5 {
        data: [f64; 25],
    }

    pub struct Mat6 {
        data: [f64; 36],
    }

    pub struct Mat {
        data: Vec<f64>,
        rows: usize,
        cols: usize,
    }
}

pub use ffi::*;

impl From<Vec1> for na::Vector1<f64> {
    fn from(v: Vec1) -> Self {
        na::Vector1::new(v.data)
    }
}

impl From<na::Vector1<f64>> for Vec1 {
    fn from(v: na::Vector1<f64>) -> Self {
        Vec1 { data: v[0] }
    }
}

impl From<Vec2> for na::Vector2<f64> {
    fn from(v: Vec2) -> Self {
        na::Vector2::new(v.data[0], v.data[1])
    }
}

impl From<na::Vector2<f64>> for Vec2 {
    fn from(v: na::Vector2<f64>) -> Self {
        Vec2 { data: [v[0], v[1]] }
    }
}

impl From<Vec3> for na::Vector3<f64> {
    fn from(v: Vec3) -> Self {
        na::Vector3::new(v.data[0], v.data[1], v.data[2])
    }
}

impl From<na::Vector3<f64>> for Vec3 {
    fn from(v: na::Vector3<f64>) -> Self {
        Vec3 { data: [v[0], v[1], v[2]] }
    }
}

impl From<Vec4> for na::Vector4<f64> {
    fn from(v: Vec4) -> Self {
        na::Vector4::new(v.data[0], v.data[1], v.data[2], v.data[3])
    }
}

impl From<na::Vector4<f64>> for Vec4 {
    fn from(v: na::Vector4<f64>) -> Self {
        Vec4 { data: [v[0], v[1], v[2], v[3]] }
    }
}

impl From<Vec5> for na::Vector5<f64> {
    fn from(v: Vec5) -> Self {
        na::Vector5::new(v.data[0], v.data[1], v.data[2], v.data[3], v.data[4])
    }
}

impl From<na::Vector5<f64>> for Vec5 {
    fn from(v: na::Vector5<f64>) -> Self {
        Vec5 { data: [v[0], v[1], v[2], v[3], v[4]] }
    }
}

impl From<Vec6> for na::Vector6<f64> {
    fn from(v: Vec6) -> Self {
        na::Vector6::new(
            v.data[0], v.data[1], v.data[2], v.data[3], v.data[4], v.data[5],
        )
    }
}

impl From<na::Vector6<f64>> for Vec6 {
    fn from(v: na::Vector6<f64>) -> Self {
        Vec6 { data: [v[0], v[1], v[2], v[3], v[4], v[5]] }
    }
}

impl From<VecX> for na::DVector<f64> {
    fn from(value: VecX) -> Self {
        na::DVector::from_vec(value.data)
    }
}

impl From<na::DVector<f64>> for VecX {
    fn from(value: na::DVector<f64>) -> Self {
        VecX { data: value.data.into() }
    }
}

impl From<Mat1> for na::Matrix1<f64> {
    fn from(m: Mat1) -> Self {
        na::Matrix1::from_row_slice(&m.data)
    }
}

impl From<na::Matrix1<f64>> for Mat1 {
    fn from(m: na::Matrix1<f64>) -> Self {
        Mat1 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat2> for na::Matrix2<f64> {
    fn from(m: Mat2) -> Self {
        na::Matrix2::from_row_slice(&m.data)
    }
}

impl From<na::Matrix2<f64>> for Mat2 {
    fn from(m: na::Matrix2<f64>) -> Self {
        Mat2 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat3> for na::Matrix3<f64> {
    fn from(m: Mat3) -> Self {
        na::Matrix3::from_row_slice(&m.data)
    }
}

impl From<na::Matrix3<f64>> for Mat3 {
    fn from(m: na::Matrix3<f64>) -> Self {
        Mat3 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat4> for na::Matrix4<f64> {
    fn from(m: Mat4) -> Self {
        na::Matrix4::from_row_slice(&m.data)
    }
}

impl From<na::Matrix4<f64>> for Mat4 {
    fn from(m: na::Matrix4<f64>) -> Self {
        Mat4 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat5> for na::Matrix5<f64> {
    fn from(m: Mat5) -> Self {
        na::Matrix5::from_row_slice(&m.data)
    }
}

impl From<na::Matrix5<f64>> for Mat5 {
    fn from(m: na::Matrix5<f64>) -> Self {
        Mat5 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat6> for na::Matrix6<f64> {
    fn from(m: Mat6) -> Self {
        na::Matrix6::from_row_slice(&m.data)
    }
}

impl From<na::Matrix6<f64>> for Mat6 {
    fn from(m: na::Matrix6<f64>) -> Self {
        Mat6 { data: m.as_slice().try_into().unwrap() }
    }
}

impl From<Mat> for na::DMatrix<f64> {
    fn from(m: Mat) -> Self {
        na::DMatrix::from_row_slice(m.rows, m.cols, &m.data)
    }
}
