/*
    MIT License

    Copyright (c) 2016 Marcel Steinbeck

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
*/

#pragma once

#include "tinyspline.h"
#include <vector>
#include <string>

#ifndef TINYSPLINECXX_API
#define TINYSPLINECXX_API TINYSPLINE_API
#endif

/*! @name Emscripten Extensions
 *
 * Please see the following references for more details on how to use
 * Emscripten with C++:
 *
 * https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
 * https://emscripten.org/docs/api_reference/bind.h.html
 *
 * @{
 */
#ifdef TINYSPLINE_EMSCRIPTEN

// Additional includes and namespaces.
#include <stdexcept>
#include <emscripten/bind.h>
using namespace emscripten;

/* Used by value_objects to guard read-only properties. */
void inline
cannotWrite()
{ throw std::runtime_error("cannot write read-only property"); }

/* Allow clients to read exception messages in the Javascript binding. */
std::string
exceptionMessage(int ptr)
{ return std::string(reinterpret_cast<std::exception *>(ptr)->what()); }

// Map: std::vector <--> JS array
// https://github.com/emscripten-core/emscripten/issues/11070#issuecomment-717675128
namespace emscripten {
namespace internal {
template <typename T, typename Allocator>
struct BindingType<std::vector<T, Allocator>> {
	using ValBinding = BindingType<val>;
	using WireType = ValBinding::WireType;

	static WireType
	toWireType(const std::vector<T, Allocator> &vec)
	{ return ValBinding::toWireType(val::array(vec)); }

	static std::vector<T, Allocator>
	fromWireType(WireType value)
	{ return vecFromJSArray<T>(ValBinding::fromWireType(value)); }
};

template <typename T>
struct TypeID<T, // this is ridiculous...
              typename std::enable_if_t<std::is_same<
                  typename Canonicalized<T>::type,
                  std::vector<typename Canonicalized<T>::type::value_type,
                  typename Canonicalized<T>::type::allocator_type>>::value>> {
	static constexpr TYPEID
	get() { return TypeID<val>::get(); }
};
}  // namespace internal
}  // namespace emscripten
#endif
/*! @} */



namespace tinyspline {

/*! @name API Configuration
 *
 * @{
 */
typedef tsReal real;
/*! @} */



/*! @name Swig Type Mapping
 *
 * Methods that do not return or set the value of a class attribute (let's call
 * these methods `non-accessor methods') must return/take instances of
 * std::vector as pointer. Otherwise, they aren't type mapped by Swig to the
 * std::vector representation of the target language.
 *
 * @{
 */
#ifdef SWIG
using std_real_vector_in = std::vector<tinyspline::real> *;
using std_real_vector_out = std::vector<tinyspline::real> *;
#else
using std_real_vector_in = const std::vector<tinyspline::real> &;
using std_real_vector_out = std::vector<tinyspline::real>;
#endif
/*! @} */



/*! @name Vector Math
 *
 * Wrapper classes for TinySpline's vector math.
 *
 * @{
 */
class TINYSPLINECXX_API Vec2 {
public:
	Vec2();
	Vec2(real x, real y);

	Vec2 operator+(const Vec2 &other);
	Vec2 operator-(const Vec2 &other);
	Vec2 operator*(real scalar);

	real x() const;
	void setX(real val);
	real y() const;
	void setY(real val);
	std::vector<real> values() const;

	Vec2 add(const Vec2 &other) const;
	Vec2 subtract(const Vec2 &other) const;
	Vec2 multiply(real scalar) const;
	Vec2 normalize() const;
	real magnitude() const;
	real dot(const Vec2 &other) const;
	real angle(const Vec2 &other) const;
	real distance(const Vec2 &other) const;

	std::string toString() const;

private:
	real m_vals[2];

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	void setValues(std::vector<real>) { cannotWrite(); }
#endif
};

class TINYSPLINECXX_API Vec3 {
public:
	Vec3();
	Vec3(real x, real y, real z);

	Vec3 operator+(const Vec3 &other);
	Vec3 operator-(const Vec3 &other);
	Vec3 operator*(real scalar);

	real x() const;
	void setX(real val);
	real y() const;
	void setY(real val);
	real z() const;
	void setZ(real val);
	std::vector<real> values() const;

	Vec3 add(const Vec3 &other) const;
	Vec3 subtract(const Vec3 &other) const;
	Vec3 multiply(real scalar) const;
	Vec3 cross(const Vec3 &other) const;
	Vec3 normalize() const;
	real magnitude() const;
	real dot(const Vec3 &other) const;
	real angle(const Vec3 &other) const;
	real distance(const Vec3 &other) const;

	std::string toString() const;

private:
	real m_vals[3];

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	void setValues(std::vector<real>) { cannotWrite(); }
#endif
};

class TINYSPLINECXX_API Vec4 {
public:
	Vec4();
	Vec4(real x, real y, real z, real w);

	Vec4 operator+(const Vec4 &other);
	Vec4 operator-(const Vec4 &other);
	Vec4 operator*(real scalar);

	real x() const;
	void setX(real val);
	real y() const;
	void setY(real val);
	real z() const;
	void setZ(real val);
	real w() const;
	void setW(real val);
	std::vector<real> values() const;

	Vec4 add(const Vec4 &other) const;
	Vec4 subtract(const Vec4 &other) const;
	Vec4 multiply(real scalar) const;
	Vec4 normalize() const;
	real magnitude() const;
	real dot(const Vec4 &other) const;
	real angle(const Vec4 &other) const;
	real distance(const Vec4 &other) const;

	std::string toString() const;

private:
	real m_vals[4];

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	void setValues(std::vector<real>) { cannotWrite(); }
#endif
};

#ifdef TINYSPLINE_EMSCRIPTEN
class VecMath {
public:
	/*! @name Add
	*
	* @{
	*/
	static Vec2
	add2(const Vec2 &a, const Vec2 &b)
	{ return a.add(b); }

	static Vec3
	add3(const Vec3 &a, const Vec3 &b)
	{ return a.add(b); }

	static Vec4
	add4(const Vec4 &a, const Vec4 &b)
	{ return a.add(b); }
	/*! @} */

	/*! @name Subtract
	*
	* @{
	*/
	static Vec2
	subtract2(const Vec2 &a, const Vec2 &b)
	{ return a.subtract(b); }

	static Vec3
	subtract3(const Vec3 &a, const Vec3 &b)
	{ return a.subtract(b); }

	static Vec4
	subtract4(const Vec4 &a, const Vec4 &b)
	{ return a.subtract(b); }
	/*! @} */

	/*! @name Multiply
	*
	* @{
	*/
	static Vec2
	multiply2(const Vec2 &vec, real scalar)
	{ return vec.multiply(scalar); }

	static Vec3
	multiply3(const Vec3 &vec, real scalar)
	{ return vec.multiply(scalar); }

	static Vec4
	multiply4(const Vec4 &vec, real scalar)
	{ return vec.multiply(scalar); }
	/*! @} */

	/*! @name Cross
	*
	* @{
	*/
	static Vec3
	cross3(const Vec3 &a, Vec3 &b)
	{ return a.cross(b); }
	/*! @} */

	/*! @name Normalize
	*
	* @{
	*/
	static Vec2
	normalize2(const Vec2 &vec)
	{ return vec.normalize(); }

	static Vec3
	normalize3(const Vec3 &vec)
	{ return vec.normalize(); }

	static Vec4
	normalize4(const Vec4 &vec)
	{ return vec.normalize(); }
	/*! @} */

	/*! @name Magnitude
	*
	* @{
	*/
	static real
	magnitude2(const Vec2 &vec)
	{ return vec.magnitude(); }

	static real
	magnitude3(const Vec3 &vec)
	{ return vec.magnitude(); }

	static real
	magnitude4(const Vec4 &vec)
	{ return vec.magnitude(); }
	/*! @} */

	/*! @name Dot
	*
	* @{
	*/
	static real
	dot2(const Vec2 &a, Vec2 &b)
	{ return a.dot(b); }

	static real
	dot3(const Vec3 &a, Vec3 &b)
	{ return a.dot(b); }

	static real
	dot4(const Vec4 &a, Vec4 &b)
	{ return a.dot(b); }
	/*! @} */

	/*! @name Angle
	*
	* @{
	*/
	static real
	angle2(const Vec2 &a, Vec2 &b)
	{ return a.angle(b); }

	static real
	angle3(const Vec3 &a, Vec3 &b)
	{ return a.angle(b); }

	static real
	angle4(const Vec4 &a, Vec4 &b)
	{ return a.angle(b); }
	/*! @} */

	/*! @name Distance
	*
	* @{
	*/
	static real
	distance2(const Vec2 &a, Vec2 &b)
	{ return a.distance(b); }

	static real
	distance3(const Vec3 &a, Vec3 &b)
	{ return a.distance(b); }

	static real
	distance4(const Vec4 &a, Vec4 &b)
	{ return a.distance(b); }
	/*! @} */
};
#endif
/*! @} */



/*! @name Spline Framing
 *
 * Wrapper classes for ::tsFrame (::Frame) and sequences of ::tsFrame
 * (::FrameSeq). Instances of ::FrameSeq are created by ::BSpline::computeRMF.
 *
 * @{
 */
class TINYSPLINECXX_API Frame {
public:
	Frame(Vec3 &position,
	      Vec3 &tangent,
	      Vec3 &normal,
	      Vec3 &binormal);

	Vec3 position() const;
	Vec3 tangent() const;
	Vec3 normal() const;
	Vec3 binormal() const;

	std::string toString() const;

private:
	Vec3 m_position,
	     m_tangent,
	     m_normal,
	     m_binormal;

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	Frame() {}
	void setPosition(Vec3) { cannotWrite(); }
	void setTangent(Vec3) { cannotWrite(); }
	void setNormal(Vec3) { cannotWrite(); }
	void setBinormal(Vec3) { cannotWrite(); }
#endif
};

class TINYSPLINECXX_API FrameSeq {
public:
	FrameSeq();
	FrameSeq(const FrameSeq &other);
	FrameSeq(FrameSeq &&other);
	virtual ~FrameSeq();

	FrameSeq &operator=(const FrameSeq &other);
	FrameSeq &operator=(FrameSeq &&other);

	size_t size() const;
	Frame at(size_t idx) const;

	std::string toString() const;

private:
	tsFrame *m_frames;
	size_t m_size;
	FrameSeq(tsFrame *frames,
	         size_t len);
	friend class BSpline;
};
/*! @} */



/*! @name Utility Classes
 *
 * Little helper classes, such as value classes or classes with only static
 * methods. These are primarily classes that do not really fit into another
 * group or are too small to form their own group.
 *
 * @{
 */
class TINYSPLINECXX_API Domain {
public:
	Domain(real min, real max);

	real min() const;
	real max() const;

	std::string toString() const;

private:
	real m_min,
	     m_max;

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	Domain()
	: Domain(TS_DOMAIN_DEFAULT_MIN, TS_DOMAIN_DEFAULT_MAX)
	{}
	void setMin(real) { cannotWrite(); }
	void setMax(real) { cannotWrite(); }
#endif
};
/*! @} */



/*! @name DeBoorNet
 *
 * Wrapper class for ::tsDeBoorNet.
 *
 * @{
 */
class TINYSPLINECXX_API DeBoorNet {
public:
	DeBoorNet(const DeBoorNet &other);
	DeBoorNet(DeBoorNet &&other);
	virtual ~DeBoorNet();

	DeBoorNet & operator=(const DeBoorNet &other);
	DeBoorNet & operator=(DeBoorNet &&other);

	real knot() const;
	size_t index() const;
	size_t multiplicity() const;
	size_t numInsertions() const;
	size_t dimension() const;
	std::vector<real> points() const;
	std::vector<real> result() const;

	/**
	* Returns the result at \p idx as ::Vec2. Note that, by design, \p idx
	* cannot be greater than \c 1. It is safe to call this method even if
	* ::dimension is less than \c 2. In this case, the missing components
	* are set to \c 0. If ::dimension is greater than \c 2, the excess
	* values are ignored.
	*
	* @return
	* 	The result at \p idx as ::Vec2.
	* @throws std::out_of_range
	* 	If \p idx is greater than \c 1, or if \p idx is \c 1, but there
	* 	is only one result.
	*/
	Vec2 resultVec2(size_t idx = 0) const;

	/**
	* Returns the result at \p idx as ::Vec3. Note that, by design, \p idx
	* cannot be greater than \c 1. It is safe to call this method even if
	* ::dimension is less than \c 3. In this case, the missing components
	* are set to \c 0. If ::dimension is greater than \c 3, the excess
	* values are ignored.
	*
	* @return
	* 	The result at \p idx as ::Vec3.
	* @throws std::out_of_range
	* 	If \p idx is greater than \c 1, or if \p idx is \c 1, but there
	* 	is only one result.
	*/
	Vec3 resultVec3(size_t idx = 0) const;

	/**
	* Returns the result at \p idx as ::Vec4. Note that, by design, \p idx
	* cannot be greater than \c 1. It is safe to call this method even if
	* ::dimension is less than \c 4. In this case, the missing components
	* are set to \c 0. If ::dimension is greater than \c 4, the excess
	* values are ignored.
	*
	* @return
	* 	The result at \p idx as ::Vec4.
	* @throws std::out_of_range
	* 	If \p idx is greater than \c 1, or if \p idx is \c 1, but there
	* 	is only one result.
	*/
	Vec4 resultVec4(size_t idx = 0) const;

	std::string toString() const;

private:
	tsDeBoorNet m_net;

	/* Constructors & Destructors */
	explicit DeBoorNet(tsDeBoorNet &data);

	friend class BSpline;

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	DeBoorNet() : m_net(ts_deboornet_init()) {}
	void setKnot(real) { cannotWrite(); }
	void setIndex(size_t) { cannotWrite(); }
	void setMultiplicity(size_t) { cannotWrite(); }
	void setNumInsertions(size_t) { cannotWrite(); }
	void setDimension(size_t) { cannotWrite(); }
	void setPoints(std::vector<real>) { cannotWrite(); }
	void setResult(std::vector<real>) { cannotWrite(); }
#endif
};
/*! @} */



/*! @name BSpline
 *
 * Wrapper class for ::tsBSpline.
 *
 * @{
 */
class Morphism;
class ChordLengths;
class TINYSPLINECXX_API BSpline {
public:
	enum Type { Opened, Clamped, Beziers };

	/* Constructors & Destructors */
	BSpline();
	BSpline(const BSpline &other);
	BSpline(BSpline &&other);
	explicit BSpline(size_t numControlPoints,
	                 size_t dimension = 2,
	                 size_t degree = 3,
	                 Type type = Type::Clamped);
	virtual ~BSpline();

	/* Create from static method */
	static BSpline interpolateCubicNatural(std_real_vector_in points,
	                                       size_t dimension);
	static BSpline interpolateCatmullRom(std_real_vector_in points,
	                                     size_t dimension,
	                                     real alpha = (real) 0.5,
	                                     std::vector<real> *first = nullptr,
	                                     std::vector<real> *last = nullptr,
	                                     real epsilon = TS_POINT_EPSILON);
	//static BSpline parseJson(std::string json);
	//static BSpline load(std::string path);

	static bool knotsEqual(real x, real y);

	/* Operators */
	BSpline & operator=(const BSpline &other);
	BSpline & operator=(BSpline &&other);
	DeBoorNet operator()(real knot) const;

	/* Accessors */
	size_t degree() const;
	size_t order() const;
	size_t dimension() const;
	std::vector<real> controlPoints() const;
	Vec2 controlPointVec2At(size_t idx) const;
	Vec3 controlPointVec3At(size_t idx) const;
	Vec4 controlPointVec4At(size_t idx) const;
	std::vector<real> knots() const;
	real knotAt(size_t idx) const;

	/* Query */
	size_t numControlPoints() const;
	DeBoorNet eval(real knot) const;
	std_real_vector_out evalAll(std_real_vector_in knots) const;
	std_real_vector_out sample(size_t num = 0) const;
	DeBoorNet bisect(real value,
	                 real epsilon = (real) 0.0,
	                 bool persnickety = false,
	                 size_t index = 0,
	                 bool ascending = true,
	                 size_t maxIter = 50) const;
	Domain domain() const;
	bool isClosed(real epsilon = TS_POINT_EPSILON) const;
	FrameSeq computeRMF(std_real_vector_in knots,
	                    Vec3 *firstNormal = nullptr) const;
	BSpline subSpline(real knot0, real knot1) const;
	std_real_vector_out uniformKnotSeq(size_t num = 100) const;
	std_real_vector_out equidistantKnotSeq(size_t num = 100,
	                                       size_t numSamples = 0) const;
	ChordLengths chordLengths(std_real_vector_in knots) const;
	ChordLengths chordLengths(size_t numSamples = 200) const;

	/* Serialization */
	//std::string toJson() const;
	//void save(std::string path) const;

	/* Modifications */
	void setControlPoints(const std::vector<real> &ctrlp);
	void setControlPointVec2At(size_t idx, Vec2 &cp);
	void setControlPointVec3At(size_t idx, Vec3 &cp);
	void setControlPointVec4At(size_t idx, Vec4 &cp);
	void setKnots(const std::vector<real> &knots);
	void setKnotAt(size_t idx, real knot);

	/* Transformations */
	BSpline insertKnot(real knot, size_t num) const;
	BSpline split(real knot) const;
	BSpline tension(real beta) const;
	BSpline toBeziers() const;
	BSpline derive(size_t num = 1,
	               real eps = TS_POINT_EPSILON) const;
	BSpline elevateDegree(size_t amount,
	                      real eps = TS_POINT_EPSILON) const;
	BSpline alignWith(const BSpline &other,
	                  BSpline &otherAligned,
	                  real eps = TS_POINT_EPSILON) const;
	Morphism morphTo(const BSpline &other,
	                 real eps = TS_POINT_EPSILON) const;

	/* Debug */
	std::string toString() const;

private:
	tsBSpline m_spline;

	/* Constructors & Destructors */
	explicit BSpline(tsBSpline &data);

	/* Needs to access ::spline. */
	friend class Morphism;

#ifdef TINYSPLINE_EMSCRIPTEN
public:
	std_real_vector_out sample0() const { return sample(); }
	std_real_vector_out sample1(size_t num) const { return sample(num); }
	BSpline derive0() const { return derive(); }
	BSpline derive1(size_t n) const { return derive(n); }
	BSpline derive2(size_t n, real eps) const { return derive(n, eps); }
#endif
};
/*! @} */



/*! @name Spline Morphing
 *
 * @{
 */
class TINYSPLINECXX_API Morphism {
public:
	Morphism(const BSpline &origin,
		 const BSpline &target,
		 real epsilon = TS_POINT_EPSILON);

	BSpline origin() const;
	BSpline target() const;
	real epsilon() const;

	BSpline eval(real t);
	BSpline operator()(real t);

	std::string toString() const;
private:
	BSpline m_origin, m_target;
	real m_epsilon;
	BSpline m_originAligned, m_targetAligned;
	BSpline m_buffer;
};
/*! @} */



/*! @name Reparametrization by Arc Length
 * @{
 */
class TINYSPLINECXX_API ChordLengths {
public:
	ChordLengths();
	ChordLengths(const ChordLengths &other);
	ChordLengths(ChordLengths &&other);
	virtual ~ChordLengths();

	ChordLengths &operator=(const ChordLengths &other);
	ChordLengths &operator=(ChordLengths &&other);

	BSpline spline() const;
	std::vector<real> knots() const;
	std::vector<real> lengths() const;
	TS_DEPRECATED std::vector<real> values() const;
	size_t size() const;

	real arcLength() const;
	real lengthToKnot(real len) const;
	real tToKnot(real t) const;
	std_real_vector_out equidistantKnotSeq(size_t num = 100) const;

	std::string toString() const;
private:
	BSpline m_spline;
	real *m_knots, *m_lengths;
	size_t m_size;
	ChordLengths(const BSpline &spline,
	             real *knots,
	             real *lengths,
	             size_t size);
	friend class BSpline;
};
/*! @} */

}



#ifdef TINYSPLINE_EMSCRIPTEN
using namespace tinyspline;
EMSCRIPTEN_BINDINGS(tinyspline) {
	function("exceptionMessage", &exceptionMessage);

	// Vector Math
	value_object<Vec2>("Vec2")
		.field("x",
		       &Vec2::x,
		       &Vec2::setX)
		.field("y",
		       &Vec2::y,
		       &Vec2::setY)
		.field("values",
		       &Vec2::values,
		       &Vec2::setValues)
	;
	value_object<Vec3>("Vec3")
		.field("x",
		       &Vec3::x,
		       &Vec3::setX)
		.field("y",
		       &Vec3::y,
		       &Vec3::setY)
		.field("z",
		       &Vec3::z,
		       &Vec3::setZ)
		.field("values",
		       &Vec3::values,
		       &Vec3::setValues)
	;
	value_object<Vec4>("Vec4")
		.field("x",
		       &Vec4::x,
		       &Vec4::setX)
		.field("y",
		       &Vec4::y,
		       &Vec4::setY)
		.field("z",
		       &Vec4::z,
		       &Vec4::setZ)
		.field("w",
		       &Vec4::w,
		       &Vec4::setW)
		.field("values",
		       &Vec4::values,
		       &Vec4::setValues)
	;
	class_<VecMath>("VecMath")
		.class_function("add", &VecMath::add2)
		.class_function("add", &VecMath::add3)
		.class_function("add", &VecMath::add4)
		.class_function("subtract", &VecMath::subtract2)
		.class_function("subtract", &VecMath::subtract3)
		.class_function("subtract", &VecMath::subtract4)
		.class_function("multiply", &VecMath::multiply2)
		.class_function("multiply", &VecMath::multiply3)
		.class_function("multiply", &VecMath::multiply4)
		.class_function("cross", &VecMath::cross3)
		.class_function("normalize", &VecMath::normalize2)
		.class_function("normalize", &VecMath::normalize3)
		.class_function("normalize", &VecMath::normalize4)
		.class_function("magnitude", &VecMath::magnitude2)
		.class_function("magnitude", &VecMath::magnitude3)
		.class_function("magnitude", &VecMath::magnitude4)
		.class_function("dot", &VecMath::dot2)
		.class_function("dot", &VecMath::dot3)
		.class_function("dot", &VecMath::dot4)
		.class_function("angle", &VecMath::angle2)
		.class_function("angle", &VecMath::angle3)
		.class_function("angle", &VecMath::angle4)
		.class_function("distance", &VecMath::distance2)
		.class_function("distance", &VecMath::distance3)
		.class_function("distance", &VecMath::distance4)
	;

	// Spline Framing
	value_object<Frame>("Frame")
		.field("position",
		       &Frame::position,
		       &Frame::setPosition)
		.field("tangent",
		       &Frame::tangent,
		       &Frame::setTangent)
		.field("normal",
		       &Frame::normal,
		       &Frame::setNormal)
		.field("binormal",
		       &Frame::normal,
		       &Frame::setNormal)
	;
	class_<FrameSeq>("FrameSeq")
		.constructor<>()
		.constructor<FrameSeq>()
		.function("size", &FrameSeq::size)
		.function("at", &FrameSeq::at)
		.function("toString", &FrameSeq::toString)
	;

	// Utility Classes
	value_object<Domain>("Domain")
		.field("min",
		       &Domain::min,
		       &Domain::setMin)
		.field("max",
		       &Domain::max,
		       &Domain::setMax)
	;

	value_object<DeBoorNet>("DeBoorNet")
		.field("knot",
		       &DeBoorNet::knot,
		       &DeBoorNet::setKnot)
		.field("index",
		       &DeBoorNet::index,
		       &DeBoorNet::setIndex)
		.field("multiplicity",
		       &DeBoorNet::multiplicity,
		       &DeBoorNet::setMultiplicity)
		.field("numInsertions",
		       &DeBoorNet::numInsertions,
		       &DeBoorNet::setNumInsertions)
		.field("dimension",
		       &DeBoorNet::dimension,
		       &DeBoorNet::setDimension)
		.field("points",
		       &DeBoorNet::points,
		       &DeBoorNet::setPoints)
		.field("result",
		       &DeBoorNet::result,
		       &DeBoorNet::setResult)
	;

	class_<BSpline>("BSpline")
	        .constructor<>()
	        //.constructor<BSpline>()
	        .constructor<size_t>()
	        .constructor<size_t, size_t>()
	        .constructor<size_t, size_t, size_t>()
	        .constructor<size_t, size_t, size_t, BSpline::Type>()

	        .class_function("interpolateCubicNatural",
			&BSpline::interpolateCubicNatural)
	        .class_function("interpolateCatmullRom",
			&BSpline::interpolateCatmullRom,
			allow_raw_pointers())
	        .class_function("parseJson", &BSpline::parseJson)

	        .property("degree", &BSpline::degree)
	        .property("order", &BSpline::order)
	        .property("dimension", &BSpline::dimension)
	        .property("controlPoints", &BSpline::controlPoints,
			&BSpline::setControlPoints)
	        .property("knots", &BSpline::knots, &BSpline::setKnots)
	        .property("domain", &BSpline::domain)

	        /* Property by index */
	        .function("knotAt", &BSpline::knotAt)
	        .function("setKnotAt", &BSpline::setKnotAt)

	        /* Query */
	        .function("numControlPoints", &BSpline::numControlPoints)
	        .function("eval", &BSpline::eval)
	        .function("evalAll", &BSpline::evalAll)
	        .function("sample",
			select_overload<std_real_vector_out() const>
			(&BSpline::sample0))
	        .function("sample",
			select_overload<std_real_vector_out(size_t) const>
			(&BSpline::sample1))
	        .function("sample", &BSpline::sample)
	        .function("bisect", &BSpline::bisect)
	        .function("isClosed", &BSpline::isClosed)

		/* Serialization */
	        .function("toJson", &BSpline::toJson)

	        /* Transformations */
	        .function("insertKnot", &BSpline::insertKnot)
	        .function("split", &BSpline::split)
	        .function("tension", &BSpline::tension)
	        .function("toBeziers", &BSpline::toBeziers)
	        .function("derive",
			select_overload<BSpline() const>
			(&BSpline::derive0))
	        .function("derive",
			select_overload<BSpline(size_t) const>
			(&BSpline::derive1))
	        .function("derive",
			select_overload<BSpline(size_t, real) const>
			(&BSpline::derive2))

	        /* Debug */
	        .function("toString", &BSpline::toString)
	;

	enum_<BSpline::Type>("BSplineType")
	        .value("Opened", BSpline::Type::Opened)
	        .value("Clamped", BSpline::Type::Clamped)
	        .value("Beziers", BSpline::Type::Beziers)
	;
}
#endif
