#ifndef __INTERPOLATOR_H__
#define __INTERPOLATOR_H__

#include <malloc.h>

#include "libsl.h"

namespace xrt
{	
	template <class T> class PUBLIC Interpolator
	{
	public:
		// Interpolator Interface
		virtual T at( const float u, const float v ) const = 0;
		virtual T du( const float u, const float v ) const = 0;
		virtual T dv( const float u, const float v ) const = 0;
		virtual ~Interpolator() { }
	};

	template <class T> class PUBLIC SequentialInterpolator: public Interpolator<T>
	{
	public:
		// SequentialInterpolator Interface
		virtual T operator [] ( unsigned int ) const = 0;
		virtual T& operator [] ( unsigned int ) = 0;
	};

	// ConstantInterpolator
	template <class T>
	class ConstantInterpolator : public SequentialInterpolator<T> 
	{
	public:
		ConstantInterpolator(const T &v) { value = v; }
		T at( const float u, const float v ) const 
		{
			return value;
		}
		T du( const float u, const float v ) const 
		{
			return 0.0f;
		}
		T dv( const float u, const float v ) const 
		{
			return 0.0f;
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value;
		}
		T& operator [] ( unsigned int i )
		{ 
			return value;
		}
	private:
		T value;
	};

	// LinearInterpolator
	template <class T>
	class LinearInterpolator : public SequentialInterpolator<T> 
	{
	public:
		LinearInterpolator() {}
		LinearInterpolator(const T &a, const T &b ) { value[0] = a; value[1] = b; }
		T at( const float u, const float v ) const 
		{
			return value[0] + v * ( value[1] - value[0] );
		}
		T du( const float u, const float v ) const 
		{
			return value[1] - value[0];
		}
		T dv( const float u, const float v ) const 
		{
			return 0.0f;
		}
		void initialize(const T &a, const T &b ) 
		{ 
			value[0] = a; value[1] = b; 
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value[i];
		}
		T& operator [] ( unsigned int i )
		{ 
			return value[i];
		}
	private:
		T value[2];
	};

	// TriangleInterpolator
	template <class T>
	class TriangleInterpolator : public SequentialInterpolator<T> 
	{
	public:
		TriangleInterpolator(const T &u, const T &v, const T &w ) { value[0] = u; value[1] = v; value[2] = w; }
		T at( const float u, const float v ) const 
		{
			return ( 1 - u - v ) * value[0] + u * value[1] + v * value[2];
		}
		T du( const float u, const float v ) const 
		{
			return value[1] - value[0];
		}
		T dv( const float u, const float v ) const 
		{
			return value[2] - value[0];
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value[i];
		}
		T& operator [] ( unsigned int i )
		{ 
			return value[i];
		}
	private:
		T value[3];
	};

	// BilinearInterpolator
	template <class T>
	class BilinearInterpolator : public SequentialInterpolator<T> 
	{
	public:
		BilinearInterpolator() {}
		BilinearInterpolator(const T &a, const T &b, const T &c, const T &d ) 
		{ 
			value[0] = a; value[1] = b; value[2] = c; value[3] = d; 
		}
		T at( const float u, const float v ) const 
		{
			return SL::mix( SL::mix( value[0], value[1], u ), SL::mix( value[2], value[3], u ), v );
		}
		T du( const float u, const float v ) const 
		{
			return SL::mix( value[1] - value[0], value[3] - value[2], v );  
		}
		T dv( const float u, const float v ) const 
		{
			return SL::mix( value[2] - value[0], value[3] - value[1], u );  
		}
		void initialize(const T &a, const T &b, const T &c, const T &d ) 
		{ 
			value[0] = a; value[1] = b; value[2] = c; value[3] = d; 
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value[i];
		}
		T& operator [] ( unsigned int i )
		{ 
			return value[i];
		}
	private:
		T value[4];
	};

	// CubicInterpolator
	template <class T>
	class CubicInterpolator : public SequentialInterpolator<T> 
	{
	public:
		CubicInterpolator() {}
		CubicInterpolator(const T &a, const T &b, const T &c, const T &d ) 
		{ 
			value[0] = a; value[1] = b; value[2] = c; value[3] = d; 
		}
		T at( const float u, const float v ) const 
		{
			return deCasteljau< T, 4 >( value, v );
		}
		T du( const float u, const float v ) const 
		{
			return 0.0f;  
		}
		T dv( const float u, const float v ) const 
		{
			T Q[3];

			for ( int i = 0; i < 3; i++ )
				Q[i] = value[i+1] - value[i];

			return 3 * deCasteljau< T, 3 >( Q, v );
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value[i];
		}
		T& operator [] ( unsigned int i )
		{ 
			return value[i];
		}
		void initialize(const T &a, const T &b, const T &c, const T &d ) 
		{ 
			value[0] = a; value[1] = b; value[2] = c; value[3] = d; 
		}
	protected:
		T value[4];
	};

	// BicubicInterpolator
	template <class T>
	class BicubicInterpolator : public SequentialInterpolator<T> 
	{
	public:
		BicubicInterpolator(const T &a00, const T &a01, const T &a02, const T &a03,
			const T &a10, const T &a11, const T &a12, const T &a13,
			const T &a20, const T &a21, const T &a22, const T &a23,
			const T &a30, const T &a31, const T &a32, const T &a33 )
		{ 
			value[0][0] = a00; value[0][1] = a11; value[0][2] = a02; value[0][3] = a03;
			value[1][0] = a10; value[1][1] = a11; value[1][2] = a12; value[1][3] = a13;
			value[2][0] = a20; value[2][1] = a21; value[2][2] = a22; value[2][3] = a23;
			value[3][0] = a30; value[3][1] = a31; value[3][2] = a32; value[3][3] = a33;
		}
		T at( const float u, const float v ) const 
		{
			T Q[4];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 4; j++ )
					Q[j] = value[i][j];

				curve[i] = deCasteljau< T, 4 >( Q, u );
			}

			return deCasteljau< T, 4 >( curve, v );
		}
		T du( const float u, const float v ) const 
		{
			T Q[3];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 3; j++ )
					Q[j] = value[i][j+1] - value[i][j];

				curve[i] = deCasteljau< T, 3 >( Q, u );
			}
			return 3 * deCasteljau< T, 4 >( curve, v );
		}
		T dv( const float u, const float v ) const 
		{
			T Q[3];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 3; j++ )
					Q[j] = value[j+1][i] - value[j][i];

				curve[i] = deCasteljau< T, 3 >( Q, v );
			}
			return 3 * deCasteljau< T, 4 >( curve, u );
		}
		T operator [] ( unsigned int i ) const
		{ 
			return value[i/4][i%4];
		}
		T& operator [] ( unsigned int i )
		{ 
			return value[i/4][i%4];
		}
	private:
		T value[4][4];
	};

	template <class T> class PUBLIC IndexedInterpolator: public Interpolator<T>
	{
	public:
		// IndexedInterpolator Interface
		virtual unsigned int operator [] ( unsigned int ) const = 0;
		virtual unsigned int operator [] ( unsigned int ) = 0;
		virtual const T* getValues() const = 0;
		virtual void setValues( const T* ) = 0;
	};

	// IndexedTriangleInterpolator
	template <class T>
	class IndexedTriangleInterpolator : public Interpolator<T> 
	{
	public:
		IndexedTriangleInterpolator( const T *values, unsigned int u, unsigned int v, unsigned int w ): values( values ) 
		{ 
			vertices[0] = u; vertices[1] = v; vertices[2] = w; 
		}
		T at( const float u, const float v ) const 
		{
			return ( 1 - u - v ) * values[vertices[0]] + u * values[vertices[1]] + v * values[vertices[2]];
		}
		T du( const float u, const float v ) const 
		{
			return values[vertices[1]] - values[vertices[0]];
		}
		T dv( const float u, const float v ) const 
		{
			return values[vertices[2]] - values[vertices[0]];
		}
		unsigned int operator [] ( unsigned int i ) const
		{ 
			return vertices[i];
		}
		unsigned int operator [] ( unsigned int i )
		{ 
			return vertices[i];
		}
		const T* getValues() const
		{
			return values;
		}
		void setValues( const T* values_ )
		{
			values = values_;
		}
	private:
		const T *values;
		unsigned int vertices[3];
	};

	// IndexedBilinearInterpolator
	template <class T>
	class IndexedBilinearInterpolator : public Interpolator<T> 
	{
	public:
		IndexedBilinearInterpolator( const T *values, unsigned int a, unsigned int b, unsigned int c, unsigned int d  ): values( values ) 
		{ 
			vertices[0] = a; vertices[1] = b; vertices[2] = c; vertices[3] = d; 
		}
		T at( const float u, const float v ) const 
		{
			return SL::mix( SL::mix( values[vertices[0]], values[vertices[1]], u ), SL::mix( values[vertices[2]], values[vertices[3]], u ), v );
		}
		T du( const float u, const float v ) const 
		{
			return SL::mix( values[vertices[1]] - values[vertices[0]], values[vertices[3]] - values[vertices[2]], v );  
		}
		T dv( const float u, const float v ) const 
		{
			return SL::mix( values[vertices[2]] - values[vertices[0]], values[vertices[3]] - values[vertices[1]], u );  
		}
		unsigned int operator [] ( unsigned int i ) const
		{ 
			return vertices[i];
		}
		unsigned int operator [] ( unsigned int i )
		{ 
			return vertices[i];
		}
		const T* getValues() const
		{
			return values;
		}
		void setValues( const T* values_ )
		{
			values = values_;
		}
	private:
		const T *values;
		unsigned int vertices[4];
	};

	// IndexedBicubicInterpolator
	template <class T>
	class IndexedBicubicInterpolator : public Interpolator<T> 
	{
	public:
		IndexedBicubicInterpolator( const T *values, unsigned int *index  ): values( values ) 
		{ 
			memcpy( vertices, index, 16 * sizeof( unsigned int ) );
		}
		T at( const float u, const float v ) const 
		{
			T Q[4];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 4; j++ )
					Q[j] = values[vertices[i][j]];

				curve[i] = deCasteljau< T, 4 >( Q, u );
			}

			return deCasteljau< T, 4 >( curve, v );
		}
		T du( const float u, const float v ) const 
		{
			T Q[3];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 3; j++ )
					Q[j] = values[vertices[i][j+1]] - values[vertices[i][j]];

				curve[i] = deCasteljau< T, 3 >( Q, u );
			}
			return 3 * deCasteljau< T, 4 >( curve, v );
		}
		T dv( const float u, const float v ) const 
		{
			T Q[3];
			T curve[4];

			for ( int i = 0; i < 4; i++ )
			{
				for ( int j = 0; j < 3; j++ )
					Q[j] = values[vertices[j+1][i]] - valuess[vertice[j][i]];

				curve[i] = deCasteljau< T, 3 >( Q, v );
			}
			return 3 * deCasteljau< T, 4 >( curve, u );
		}
		unsigned int operator [] ( unsigned int i ) const
		{ 
			return vertices[i/4][i%4];
		}
		unsigned int operator [] ( unsigned int i )
		{ 
			return vertices[i/4][i%4];
		}
		const T* getValues() const
		{
			return values;
		}
		void setValues( const T* values_ )
		{
			values = values_;
		}
	private:
		const T *values;
		unsigned int vertices[4][4];
	};
};

#endif // __INTERPOLATOR_H__
