//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright Contributors to the OpenEXR Project.
//

// clang-format off

#include <Python.h>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/python.hpp>
#include <boost/python/make_constructor.hpp>
#include <boost/format.hpp>
#include <ImathVec.h>
#include "PyImath.h"
#include "PyImathMathExc.h"
#include "PyImathEuler.h"
#include "PyImathDecorators.h"
#include "PyImathExport.h"
#include "PyImathOperators.h"

// XXX incomplete array wrapping, docstrings missing

namespace PyImath {
template<> const char *PyImath::EulerfArray::name() { return "EulerfArray"; }
template<> const char *PyImath::EulerdArray::name() { return "EulerdArray"; }
}

namespace PyImath {
using namespace boost::python;
using namespace IMATH_NAMESPACE;

template <class T> struct EulerName { static const char *value; };
template<> const char *EulerName<float>::value  = "Eulerf";
template<> const char *EulerName<double>::value = "Eulerd";

template <class T>
static std::string nameOfOrder(typename IMATH_NAMESPACE::Euler<T>::Order order)
{
    switch(order)
    {
        case IMATH_NAMESPACE::Euler<T>::XYZ:
            return "EULER_XYZ";
        case IMATH_NAMESPACE::Euler<T>::XZY:
            return "EULER_XZY";
        case IMATH_NAMESPACE::Euler<T>::YZX:
            return "EULER_YZX";
        case IMATH_NAMESPACE::Euler<T>::YXZ:
            return "EULER_YXZ";
        case IMATH_NAMESPACE::Euler<T>::ZXY:
            return "EULER_ZXY";
        case IMATH_NAMESPACE::Euler<T>::ZYX:
            return "EULER_ZYX";
        case IMATH_NAMESPACE::Euler<T>::XZX:
            return "EULER_XZX";
        case IMATH_NAMESPACE::Euler<T>::XYX:
            return "EULER_XYX";
        case IMATH_NAMESPACE::Euler<T>::YXY:
            return "EULER_YXY";
        case IMATH_NAMESPACE::Euler<T>::YZY:
            return "EULER_YZY";
        case IMATH_NAMESPACE::Euler<T>::ZYZ:
            return "EULER_ZYZ";
        case IMATH_NAMESPACE::Euler<T>::ZXZ:
            return "EULER_ZXZ";
        case IMATH_NAMESPACE::Euler<T>::XYZr:
            return "EULER_XYZr";
        case IMATH_NAMESPACE::Euler<T>::XZYr:
            return "EULER_XZYr";
        case IMATH_NAMESPACE::Euler<T>::YZXr:
            return "EULER_YZXr";
        case IMATH_NAMESPACE::Euler<T>::YXZr:
            return "EULER_YXZr";
        case IMATH_NAMESPACE::Euler<T>::ZXYr:
            return "EULER_ZXYr";
        case IMATH_NAMESPACE::Euler<T>::ZYXr:
            return "EULER_ZYXr";
        case IMATH_NAMESPACE::Euler<T>::XZXr:
            return "EULER_XZXr";
        case IMATH_NAMESPACE::Euler<T>::XYXr:
            return "EULER_XYXr";
        case IMATH_NAMESPACE::Euler<T>::YXYr:
            return "EULER_YXYr";
        case IMATH_NAMESPACE::Euler<T>::YZYr:
            return "EULER_YZYr";
        case IMATH_NAMESPACE::Euler<T>::ZYZr:
            return "EULER_ZYZr";
        case IMATH_NAMESPACE::Euler<T>::ZXZr:
            return "EULER_ZXZr";
        default:
            break;
    }
    
    return "";
}

template <class T>
static std::string Euler_str(const Euler<T> &e)
{
    std::stringstream stream;
    stream << EulerName<T>::value << "(" << e.x << ", " << e.y << ", " << e.z << ", " 
           << nameOfOrder<T> (e.order()) << ")";
    return stream.str();
}

// Non-specialized repr is same as str
template <class T>
static std::string Euler_repr(const Euler<T> &e)
{
    return Euler_str(e);
}

// Specialization for float to full precision
template <>
std::string Euler_repr(const Euler<float> &e)
{
    return (boost::format("%s(%.9g, %.9g, %.9g, %s)")
                        % EulerName<float>::value
                        % e.x % e.y % e.z
                        % nameOfOrder<float>(e.order()).c_str()).str();
}

// Specialization for double to full precision
template <>
std::string Euler_repr(const Euler<double> &e)
{
    return (boost::format("%s(%.17g, %.17g, %.17g, %s)")
                        % EulerName<double>::value
                        % e.x % e.y % e.z
                        % nameOfOrder<double>(e.order()).c_str()).str();
}


template <class T>
static bool
equal(const Euler<T> &e0, const Euler<T> &e1)
{
    if(e0.x == e1.x && e0.y == e1.y && e0.z == e1.z && (e0.order())==(e1.order()))
        return true;
    else
        return false;
}

template <class T>
static bool
notequal(const Euler<T> &e0, const Euler<T> &e1)
{
    if(e0.x != e1.x || e0.y != e1.y || e0.z != e1.z || (e0.order()) != (e1.order()))
    {
        return true;
    }
    else
        return false;
}

template <class T>
static IMATH_NAMESPACE::Vec3 <int> getAngleOrder(Euler <T> &euler)
{
    int i, j, k;
    euler.angleOrder(i, j, k);
    return IMATH_NAMESPACE::Vec3 <int> (i, j, k);
}

template <class T>
static void
setXYZTuple(Euler<T> &euler, const tuple &t)
{
    MATH_EXC_ON;
    Vec3<T> v;
    if(t.attr("__len__")() == 3)
    {
        v.x = extract<T>(t[0]);
        v.y = extract<T>(t[1]);
        v.z = extract<T>(t[2]); 
        
        euler.setXYZVector(v);
    }
    else
        throw std::invalid_argument ("Euler expects tuple of length 3");
}

// needed to convert Eulerf::Order to Euler<T>::Order
template <class T>
static typename Euler<T>::Order interpretOrder(typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = Euler<T>::XYZ;
    switch(order)
    {
        case IMATH_NAMESPACE::Eulerf::XYZ:
        {
            o = Euler<T>::XYZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZY:
        {
            o = Euler<T>::XZY;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZX:
        {
            o = Euler<T>::YZX;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXZ:
        {
            o = Euler<T>::YXZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXY:
        {
            o = Euler<T>::ZXY;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYX:
        {
            o = Euler<T>::ZYX;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZX:
        {
            o = Euler<T>::XZX;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYX:
        {
            o = Euler<T>::XYX;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXY:
        {
            o = Euler<T>::YXY;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZY:
        {
            o = Euler<T>::YZY;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYZ:
        {
            o = Euler<T>::ZYZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXZ:
        {
            o = Euler<T>::ZXZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYZr:
        {
            o = Euler<T>::XYZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZYr:
        {
            o = Euler<T>::XZYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZXr:
        {
            o = Euler<T>::YZXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXZr:
        {
            o = Euler<T>::YXZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXYr:
        {
            o = Euler<T>::ZXYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYXr:
        {
            o = Euler<T>::ZYXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZXr:
        {
            o = Euler<T>::XZXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYXr:
        {
            o = Euler<T>::XYXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXYr:
        {
            o = Euler<T>::YXYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZYr:
        {
            o = Euler<T>::YZYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYZr:
        {
            o = Euler<T>::ZYZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXZr:
        {
            o = Euler<T>::ZXZr;
        }break;            
        default:
            break;
    }
    
    return o;
}

// needed to convert Eulerf::InputLayout to Euler<T>::InputLayout
template <class T>
static typename Euler<T>::InputLayout interpretInputLayout(typename IMATH_NAMESPACE::Eulerf::InputLayout layout)
{
    if (layout == IMATH_NAMESPACE::Eulerf::XYZLayout)
        return Euler<T>::XYZLayout;
    return Euler<T>::IJKLayout;
}

// needed to convert Eulerf::Axis to Euler<T>::Axis
template <class T>
static typename Euler<T>::Axis interpretAxis(typename IMATH_NAMESPACE::Eulerf::Axis axis)
{
    if (axis == IMATH_NAMESPACE::Eulerf::X)
        return Euler<T>::X;
    else if (axis == IMATH_NAMESPACE::Eulerf::Y)
        return Euler<T>::Y;
    else
        return Euler<T>::Z;
}

template <class T>
static Euler<T> *
eulerConstructor1(const Vec3<T> &v,
                  typename IMATH_NAMESPACE::Eulerf::Order order,
                  typename IMATH_NAMESPACE::Eulerf::InputLayout layout = IMATH_NAMESPACE::Eulerf::IJKLayout)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    typename Euler<T>::InputLayout l = interpretInputLayout<T>(layout);
    return new Euler<T>(v, o, l);
}

template <class T>
static Euler<T> *
eulerConstructor1a(const Vec3<T> &v)
{
    return eulerConstructor1 (v, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor1b(const Vec3<T> &v, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(v, o);
}

//

template <class T>
static Euler<T> *
eulerConstructor1d(const Euler<T>& e, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(e, o);
}

template <class T>
static Euler<T> *
eulerConstructor1e(const Euler<T>& e, int iorder, int layout)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    typename Euler<T>::InputLayout l = typename Euler<T>::InputLayout (layout);
    return new Euler<T>(e, o, l);
}


template <class T>
static Euler<T> *
eulerConstructor2(T i, T j, T k,
                  typename IMATH_NAMESPACE::Eulerf::Order order,
                  typename IMATH_NAMESPACE::Eulerf::InputLayout layout = IMATH_NAMESPACE::Eulerf::IJKLayout)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    typename Euler<T>::InputLayout l = interpretInputLayout<T>(layout);
    return new Euler<T>(i, j, k, o, l);
}

template <class T>
static Euler<T> *
eulerConstructor2a(T i, T j, T k)
{
    return eulerConstructor2 (i, j, k, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor2b(T i, T j, T k, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(i, j, k, o);
}

template <class T>
static Euler<T> *
eulerConstructor3(const Matrix33<T> &mat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor3a(const Matrix33<T> &mat)
{
    return eulerConstructor3 (mat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor3b(const Matrix33<T> &mat, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor4(const Matrix44<T> &mat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor4a(const Matrix44<T> &mat)
{
    return eulerConstructor4 (mat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor4b(const Matrix44<T> &mat, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor5(typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor5a()
{
    typename Euler<T>::Order o = interpretOrder<T>(IMATH_NAMESPACE::Eulerf::Default);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor5b(int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor6(T x, T y, T z)
{
    return new Euler<T>(Vec3<T>(x,y,z));
}

template <class T>
static Euler<T> *
eulerConstructor7(const Quat<T> &quat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    Euler<T> *e = eulerConstructor5<T>(order);
    e->extract(quat);
    return e;
}

template <class T>
static Euler<T> *
eulerConstructor7a(const Quat<T> &quat)
{
    return eulerConstructor7(quat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor7b(const Quat<T> &quat, int iorder)
{
    Euler<T> *e = eulerConstructor5b<T>(iorder);
    e->extract(quat);
    return e;
}

template <class T, class S>
static Euler<T> *
eulerConversionConstructor(const Euler<S> &euler)
{
    MATH_EXC_ON;
    Euler<T> *e = new Euler<T>;
    *e = euler;
    return e;
}

template <class T>
static void
eulerMakeNear(Euler<T> &euler, Euler<T> &target)
{
    MATH_EXC_ON;
    euler.makeNear (target);
}

template <class T>
static void
eulerSetOrder(Euler<T> &euler, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    euler.setOrder (o);
}
 
template <class T>
static void
eulerSet(Euler<T> &euler, IMATH_NAMESPACE::Eulerf::Axis axis, int relative, int parityEven, int firstRepeats)
{
    MATH_EXC_ON;
    typename Euler<T>::Axis a = interpretAxis<T>(axis);
    euler.set (a, relative, parityEven, firstRepeats);
}

template <class T>
static void 
extract1(Euler<T> &euler, const Matrix33<T> &m)
{
    MATH_EXC_ON;
    euler.extract(m);
}

template <class T>
static void 
extract2(Euler<T> &euler, const Matrix44<T> &m)
{
    MATH_EXC_ON;
    euler.extract(m);
}

template <class T>
static void 
extract3(Euler<T> &euler, const Quat<T> &q)
{
    MATH_EXC_ON;
    euler.extract(q);
}

template <class T>
static Matrix33<T>
toMatrix33(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toMatrix33();
}

template <class T>
static Matrix44<T>
toMatrix44(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toMatrix44();
}

template <class T>
static Quat<T>
toQuat(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toQuat();
}

template <class T>
static Vec3<T>
toXYZVector(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toXYZVector();
}

template <class T>
class_<Euler<T>,bases<IMATH_NAMESPACE::Vec3<T> > >
register_Euler()
{
    class_<Euler<T>,bases<Vec3<T> > > euler_class(EulerName<T>::value,EulerName<T>::value,init<Euler<T> >("copy construction"));
    euler_class
        .def(init<>("imath Euler default construction"))
        .def("__init__", make_constructor(eulerConstructor1<T>))
        .def("__init__", make_constructor(eulerConstructor1a<T>))
        .def("__init__", make_constructor(eulerConstructor1b<T>))
        .def("__init__", make_constructor(eulerConstructor1d<T>))
        .def("__init__", make_constructor(eulerConstructor1e<T>))
        .def("__init__", make_constructor(eulerConstructor2<T>))
        .def("__init__", make_constructor(eulerConstructor2a<T>))
        .def("__init__", make_constructor(eulerConstructor2b<T>))
        .def("__init__", make_constructor(eulerConstructor3<T>),
             "Euler-from-matrix construction assumes, but does\n"
             "not verify, that the matrix includes no shear or\n"
             "non-uniform scaling.  If necessary, you can fix\n"
             "the matrix by calling the removeScalingAndShear()\n"
             "function.\n")
        .def("__init__", make_constructor(eulerConstructor3a<T>))
        .def("__init__", make_constructor(eulerConstructor3b<T>))
        .def("__init__", make_constructor(eulerConstructor4<T>))
        .def("__init__", make_constructor(eulerConstructor4a<T>))
        .def("__init__", make_constructor(eulerConstructor4b<T>))
        .def("__init__", make_constructor(eulerConstructor5<T>))
        .def("__init__", make_constructor(eulerConstructor5a<T>))
        .def("__init__", make_constructor(eulerConstructor5b<T>))
        .def("__init__", make_constructor(eulerConstructor6<T>))
        .def("__init__", make_constructor(eulerConstructor7<T>))
        .def("__init__", make_constructor(eulerConstructor7a<T>))
        .def("__init__", make_constructor(eulerConstructor7b<T>))
        .def("__init__", make_constructor(eulerConversionConstructor<T, float>))
        .def("__init__", make_constructor(eulerConversionConstructor<T, double>))
        
        .def("angleOrder", &getAngleOrder<T>, "angleOrder() set the angle order")
        
        .def("frameStatic", &Euler<T>::frameStatic, 
             "e.frameStatic() -- returns true if the angles of e\n"
             "are measured relative to a set of fixed axes,\n"
             "or false if the angles of e are measured relative to\n"
             "each other\n")
            
        .def("initialAxis", &Euler<T>::initialAxis, 
             "e.initialAxis() -- returns the initial rotation\n"
             "axis of e (EULER_X_AXIS, EULER_Y_AXIS, EULER_Z_AXIS)")
        
        .def("initialRepeated", &Euler<T>::initialRepeated,
             "e.initialRepeated() -- returns 1 if the initial\n"
             "rotation axis of e is repeated (for example,\n"
             "e.order() == EULER_XYX); returns 0 if the initial\n"
             "rotation axis is not repeated.\n")
             
        .def("makeNear", &eulerMakeNear<T>,
             "e.makeNear(t) -- adjusts Euler e so that it\n"
             "represents the same rotation as before, but the\n"
             "individual angles of e differ from the angles of\n"
             "t by as little as possible.\n"
             "This method might not make sense if e.order()\n"
             "and t.order() are different\n")
        
        .def("order", &Euler<T>::order,
             "e.order() -- returns the rotation order in e\n"
             "(EULER_XYZ, EULER_XZY, ...)")
        
        .def("parityEven", &Euler<T>::parityEven, 
             "e.parityEven() -- returns the parity of the\n"
             "axis permutation of e\n")
        
        .def("set", &eulerSet<T>,
             "e.set(i,r,p,f) -- sets the rotation order in e\n"
             "according to the following flags:\n"
             "\n"
             "   i   initial axis (EULER_X_AXIS,\n"
             "       EULER_Y_AXIS or EULER_Z_AXIS)\n"
             "\n"
             "   r   rotation angles are measured relative\n"
             "       to each other (r == 1), or relative to a\n"
             "       set of fixed axes (r == 0)\n"
             "\n"
             "   p   parity of axis permutation is even (r == 1)\n"
             "       or odd (r == 0)\n"
             "\n"
             "   f   first rotation axis is repeated (f == 1)\n"
             "	or not repeated (f == 0)\n")
        
        .def("setOrder", &eulerSetOrder<T>,
             "e.setOrder(o) -- sets the rotation order in e\n"
             "to o (EULER_XYZ, EULER_XZY, ...)")
             
        .def("setXYZVector", &Euler<T>::setXYZVector,
             "e.setXYZVector(v) -- sets the three rotation\n"
             "angles in e to v[0], v[1], v[2]")
        .def("setXYZVector", &setXYZTuple<T>)
        
        .def("extract", &extract1<T>,
             "e.extract(m) -- extracts the rotation component\n"
             "from 3x3 matrix m and stores the result in e.\n"
             "Assumes that m does not contain shear or non-\n"
             "uniform scaling.  If necessary, you can fix m\n"
             "by calling m.removeScalingAndShear().")
        
        .def("extract", &extract2<T>,
             "e.extract(m) -- extracts the rotation component\n"
             "from 4x4 matrix m and stores the result in e.\n"
             "Assumes that m does not contain shear or non-\n"
             "uniform scaling.  If necessary, you can fix m\n"
             "by calling m.removeScalingAndShear().")
        
        .def("extract", &extract3<T>,
             "e.extract(q) -- extracts the rotation component\n"
             "from quaternion q and stores the result in e")            
        
        .def("toMatrix33", &toMatrix33<T>, "e.toMatrix33() -- converts e into a 3x3 matrix\n")
        
        .def("toMatrix44", &toMatrix44<T>, "e.toMatrix44() -- converts e into a 4x4 matrix\n")
        
        .def("toQuat", &toQuat<T>, "e.toQuat() -- converts e into a quaternion\n")
        
        .def("toXYZVector", &toXYZVector<T>, 
             "e.toXYZVector() -- converts e into an XYZ\n"
             "rotation vector")
        .def("__str__", &Euler_str<T>)
        .def("__repr__", &Euler_repr<T>)
        
        .def("__eq__", &equal<T>)
        .def("__ne__", &notequal<T>)
        ;
    
    // fill in the Euler scope
    {
        scope euler_scope(euler_class);
        enum_<typename Euler<T>::Order> euler_order("Order");
        euler_order
            .value("XYZ",Euler<T>::XYZ)
            .value("XZY",Euler<T>::XZY)
            .value("YZX",Euler<T>::YZX)
            .value("YXZ",Euler<T>::YXZ)
            .value("ZXY",Euler<T>::ZXY)
            .value("ZYX",Euler<T>::ZYX)
            .value("XZX",Euler<T>::XZX)
            .value("XYX",Euler<T>::XYX)
            .value("YXY",Euler<T>::YXY)
            .value("YZY",Euler<T>::YZY)
            .value("ZYZ",Euler<T>::ZYZ)
            .value("ZXZ",Euler<T>::ZXZ)
            .value("XYZr",Euler<T>::XYZr)
            .value("XZYr",Euler<T>::XZYr)
            .value("YZXr",Euler<T>::YZXr)
            .value("YXZr",Euler<T>::YXZr)
            .value("ZXYr",Euler<T>::ZXYr)
            .value("ZYXr",Euler<T>::ZYXr)
            .value("XZXr",Euler<T>::XZXr)
            .value("XYXr",Euler<T>::XYXr)
            .value("YXYr",Euler<T>::YXYr)
            .value("YZYr",Euler<T>::YZYr)
            .value("ZYZr",Euler<T>::ZYZr)
            .value("ZXZr",Euler<T>::ZXZr)

            // don't export these, they're not really part of the public interface
            //.value("Legal",Euler<T>::Legal)
            //.value("Min",Euler<T>::Min)
            //.value("Max",Euler<T>::Max)

            // handle Default seperately since boost sets up a 1-1 mapping for enum values
            //.value("Default",Euler<T>::Default)
            .export_values()
            ;
        // just set it to the XYZ value manually
        euler_scope.attr("Default") = euler_scope.attr("XYZ");

        enum_<typename Euler<T>::Axis>("Axis")
            .value("X",Euler<T>::X)
            .value("Y",Euler<T>::Y)
            .value("Z",Euler<T>::Z)
            .export_values()
            ;

        enum_<typename Euler<T>::InputLayout>("InputLayout")
            .value("XYZLayout",Euler<T>::XYZLayout)
            .value("IJKLayout",Euler<T>::IJKLayout)
            .export_values()
            ;
    }

    decoratecopy(euler_class);

    return euler_class;
}

// XXX fixme - template this
// really this should get generated automatically...

/*
template <class T,int index>
static FixedArray<T>
EulerArray_get(FixedArray<IMATH_NAMESPACE::Euler<T> > &qa)
{
    return FixedArray<T>( &(qa[0].r)+index, qa.len(), 4*qa.stride());
}
*/

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor7a(const FixedArray<IMATH_NAMESPACE::Quat<T> > &q)
{
    MATH_EXC_ON;
    size_t len = q.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);
    for (size_t i = 0; i < len; ++i) {
        (*result)[i].extract(q[i]);
    }
    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor8a(const FixedArray<IMATH_NAMESPACE::Vec3<T> >& v)
{
    MATH_EXC_ON;
    size_t len = v.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);

    for (size_t i = 0; i < len; ++i)
        (*result)[i] = Euler<T>(v[i]);

    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor9a(const FixedArray<IMATH_NAMESPACE::Vec3<T> >& v, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    MATH_EXC_ON;
    size_t len = v.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);

    typename Euler<T>::Order o = interpretOrder<T>(order);
    for (size_t i = 0; i < len; ++i)
        (*result)[i] = Euler<T>(v[i], o);

    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Vec3<T> >
EulerArray_toXYZVector(const FixedArray<IMATH_NAMESPACE::Euler<T> >& e)
{
    MATH_EXC_ON;
    size_t len = e.len();
    FixedArray<IMATH_NAMESPACE::Vec3<T> > result(len, UNINITIALIZED);
    for (size_t i = 0; i < len; ++i)
        result[i] = e[i].toXYZVector();
    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Quat<T> >
EulerArray_toQuat(const FixedArray<IMATH_NAMESPACE::Euler<T> >& e)
{
    MATH_EXC_ON;
    size_t len = e.len();
    FixedArray<IMATH_NAMESPACE::Quat<T> > result(len, UNINITIALIZED);
    for (size_t i = 0; i < len; ++i)
        result[i] = e[i].toQuat();
    return result;
}


template <class T>
class_<FixedArray<IMATH_NAMESPACE::Euler<T> > >
register_EulerArray()
{
    class_<FixedArray<IMATH_NAMESPACE::Euler<T> > > eulerArray_class = FixedArray<IMATH_NAMESPACE::Euler<T> >::register_("Fixed length array of IMATH_NAMESPACE::Euler");
    eulerArray_class
        //.add_property("x",&EulerArray_get<T,1>)
        //.add_property("y",&EulerArray_get<T,2>)
        //.add_property("z",&EulerArray_get<T,3>)
        .def("__init__", make_constructor(EulerArray_eulerConstructor7a<T>))
        .def("__init__", make_constructor(EulerArray_eulerConstructor8a<T>))
        .def("__init__", make_constructor(EulerArray_eulerConstructor9a<T>))
        .def("toXYZVector", EulerArray_toXYZVector<T>)
        .def("toQuat", EulerArray_toQuat<T>)
        ;

    add_comparison_functions(eulerArray_class);
    PyImath::add_explicit_construction_from_type<IMATH_NAMESPACE::Matrix33<T> >(eulerArray_class);
    PyImath::add_explicit_construction_from_type<IMATH_NAMESPACE::Matrix44<T> >(eulerArray_class);
    return eulerArray_class;
}

template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Euler<float>,bases<IMATH_NAMESPACE::Vec3<float> > > register_Euler<float>();
template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Euler<double>,bases<IMATH_NAMESPACE::Vec3<double> > > register_Euler<double>();

template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Euler<float> > > register_EulerArray<float>();
template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Euler<double> > > register_EulerArray<double>();

template<> PYIMATH_EXPORT IMATH_NAMESPACE::Euler<float> FixedArrayDefaultValue<IMATH_NAMESPACE::Euler<float> >::value() { return IMATH_NAMESPACE::Euler<float>(); }
template<> PYIMATH_EXPORT IMATH_NAMESPACE::Euler<double> FixedArrayDefaultValue<IMATH_NAMESPACE::Euler<double> >::value() { return IMATH_NAMESPACE::Euler<double>(); }

} // namespace PyImath
