#ifndef __LIBSL_H__
#define __LIBSL_H__

#include <math.h>
#include <string>
#include <vector>
#include <algorithm>
#include <stdarg.h>

#include "ustring.h"

using std::min;
using std::max;

#include "tuple.h"
#include "color.h"
#include "matrix.h"

#include "libmath.h"

namespace xrt
{
	class Matrix;
	class ShaderEnvironment;
};

namespace SL
{
// Mathematical Functions

	inline float radians( float degrees ) { return M_PI * degrees / 180.0; }
	inline float degrees( float radians ) { return 180.0 * radians / M_PI; }
	inline float pow( float x, float y )
	{
		if ( x <= 0.0f ) 
		{
			float yf = (float)floor (y);
			if ( yf == y && yf >= 1.0f ) 		     
				return ::pow( x, y );
			else 
				return 0.0f;
		}
		else
		{
			float ylnx = y * log( x );
			if ( ylnx <= -87.0f )
				return 0.0f;
			else 
				return exp( ylnx );
		}
	}

	inline float inversesqrt( float x ) { return 1 / sqrt( x ); }

	inline float mod( float x, float y ) { float result = fmodf( x, y ); return result >= 0 ? result : result + y; }
	//inline int mod( int x, int y ) { int result = x % y; return result >= 0 ? result : result + y; }
	//inline float abs( float x ) { return x < 0 ? -x : x; }
	inline float sign( float x ) { return x < 0 ? -1 : x > 0 ? 1 : 0; }

	inline float min( float x, float y )
	{
		return ::min( x, y );
	}
	inline float max( float x, float y )
	{
		return ::max( x, y );
	}

	template <typename T> T min( const T &a, const T &b )
	{
		return T( ::min( a[0], b[0] ), ::min( a[1], b[1], ), ::min( a[2], b[2] ) );
	}
	template <typename T> T min( const T &a, float f )
	{
		return min( a, T(f) );
	}
	template <typename T> T min( float f, const T &a )
	{
		return min( a, T(f) );
	}
	template <typename T> T max( const T &a, const T &b )
	{
		return T( ::max( a[0], b[0] ), ::max( a[1], b[1] ), ::max( a[2], b[2] ) );
	}
	template <typename T> T max( const T &a, float f )
	{
		return max( a, T(f) );
	}
	template <typename T> T max( float f, const T &a )
	{
		return max( a, T(f) );
	}

	inline float min ( float x, float y, int n, ... )
	{
		va_list args;

		va_start( args, n );

		float m = ::min( x, y );

		for ( int i = 0; i < n; i++ )
			m = ::min( m, va_arg( args, double ) );

		return m;
	}

	inline float max ( float x, float y, int n, ... )
	{
		va_list args;

		va_start( args, n );

		float m = ::max( x, y );

		for ( int i = 0; i < n; i++ )
			m = ::max( m, va_arg( args, double ) );

		return m;
	}

	template <typename T> T min(const T &a, const T &b, int n, ... )
	{
		va_list args;

		va_start( args, n );

		T m = ::min( x, y );

		for ( int i = 0; i < n; i++ )
			m = ::min( m, va_arg( args, T ) );

		return m;
	}

	template <typename T> T max( const T &a, const T &b, int n, ... )
	{
		va_list args;

		va_start( args, n );

		T m = ::max( x, y );

		for ( int i = n - 2; i ; i-- )
			m = ::max( m, va_arg( args, T ) );

		return m;
	}

	inline float clamp( const float x, const float min, const float max ) { return x < min ? min : x > max ? max : x; }

	template <typename T> T clamp( const T &x, const T &min, const T &max )
	{
		return T( clamp( x[0], min[0], max[0] ), clamp( x[1], min[1], max[1] ), clamp( x[2], min[2], max[2] ) );
	}

	inline float mix( float x, float y, float alpha )
	{
		return ( 1.0f - alpha ) * x + alpha * y;
	}

	template <typename T> T mix( const T &x, const T &y, const float alpha )
	{
		return ( 1.0f - alpha ) * x + alpha * y;
	}

	template <typename T> T mix( const T &x, const T &y, const T& alpha )
	{
		return ( 1.0f - alpha ) * x + alpha * y;
	}

	inline float round ( float x ) 
	{ 
		return x > 0 ? (float) ( (int) ( x + 0.5f ) ) : (float) ( (int) ( x - 0.5f ) ); 
	}

	inline float step( float min, float x ) { return x < min ? 0 : 1; }
	inline float smoothstep( float min, float max, float x ) 
	{ 
		if ( x > max )
			return 1;
		else if ( x < min )
			return 0;
		else
		{
			float val = ( x - min ) / ( max - min );
			return ( 3 - 2 * val ) * val * val;
		}
	}
	inline float filterstep( xrt::ShaderEnvironment&, float edge, float value, ... )
	{
		return step( edge, value );
	}
	#if 0
	float filterstep ( float edge, s1; ...parameterlist...)
	float filterstep ( float edge, s1, s2; ...parameterlist...)
	#endif
	EXPORT float splinef ( float, std::vector<float>& );
	EXPORT float splinef ( float, unsigned int, ...);
	EXPORT float splinef ( ustring basis, float, std::vector<float>& );
	EXPORT float splinef ( ustring basis, float, unsigned int, ...);
	EXPORT xrt::Color splinec ( float, std::vector<xrt::Color>& );
	EXPORT xrt::Color splinec ( float, unsigned int, ...);
	EXPORT xrt::Color splinec ( ustring basis, float, std::vector<xrt::Color>& );
	EXPORT xrt::Color splinec ( ustring basis, float, unsigned int, ...);
	EXPORT xrt::Tuple splinep ( float, std::vector<xrt::Tuple>& );
	EXPORT xrt::Tuple splinep ( float, unsigned int, ...);
	EXPORT xrt::Tuple splinep ( ustring basis, float, std::vector<xrt::Tuple>& );
	EXPORT xrt::Tuple splinep ( ustring basis, float, unsigned int, ...);
	EXPORT float Du ( xrt::ShaderEnvironment&, float ), Dv ( xrt::ShaderEnvironment&, float ), Deriv ( xrt::ShaderEnvironment&, float, float );
	EXPORT xrt::Color Du ( xrt::ShaderEnvironment&, xrt::Color& ), Dv ( xrt::ShaderEnvironment&, xrt::Color& ), Deriv ( xrt::ShaderEnvironment&, xrt::Color&, float );
	EXPORT xrt::Tuple Du ( xrt::ShaderEnvironment&, xrt::Tuple& ), Dv ( xrt::ShaderEnvironment&, xrt::Tuple& ), Deriv ( xrt::ShaderEnvironment&, xrt::Tuple&, float );
	inline float randomf() { return random(); }
	inline xrt::Color randomc() { return xrt::Color( random(), random(), random() ); }
	inline xrt::Tuple randomp() { return xrt::Tuple( random(), random(), random() ); }
	EXPORT float noise ( float ), noise ( float, float ), 
		noise ( float, float, float ), noise ( float, float, float, float );
	inline float noisef ( float u ) { return 0.5f * noise( u ) + 0.5f; }
	inline float noisef ( float u, float v ) { return 0.5f * noise( u, v ) + 0.5f; }
	inline float noisef ( xrt::Tuple &p ) { return 0.5f * noise( p.x, p.y, p.z ) + 0.5f; }
	inline float noisef ( xrt::Tuple &p, float t ) { return 0.5f * noise( p.x, p.y, p.z, t ) + 0.5f; }
	inline xrt::Tuple noisep ( float u ) 
	{ 
		return 0.5f * xrt::Tuple( noise( u ), noise( u + u ), noise( u + u + u ) ) + 0.5f; 
	}
	inline xrt::Tuple noisep ( float u, float v ) 
	{ 
		return 0.5f * xrt::Tuple ( noise( u, v ), noise( v, u ), noise( u + v, u - v ) ) + 0.5f; 
	}
	inline xrt::Tuple noisep ( xrt::Tuple &p ) 
	{ 
		return 0.5f * xrt::Tuple ( noise( p.x, p.y, p.z ), noise( p.y, p.z, p.x ), noise( p.z, p.x, p.y ) ) + 0.5f; 
	}
	inline xrt::Tuple noisep ( xrt::Tuple &p, float t ) 
	{ 
		return 0.5f * xrt::Tuple ( noise( p.x, p.y, p.z, t ), noise( p.y, p.z, p.x, t ), noise( p.z, p.x, p.y, t ) ) + 0.5f; 
	}
	inline xrt::Color noisec ( float u ) 
	{ 
		return 0.5f * xrt::Color( noise( u ), noise( u + u ), noise( u + u + u ) ) + 0.5f; 
	}
	inline xrt::Color noisec ( float u, float v ) 
	{ 
		return 0.5f * xrt::Color ( noise( u, v ), noise( v, u ), noise( u + v, u - v ) ) + 0.5f; 
	}
	inline xrt::Color noisec ( xrt::Tuple &p ) 
	{ 
		return 0.5f * xrt::Color ( noise( p.x, p.y, p.z ), noise( p.y, p.z, p.x ), noise( p.z, p.x, p.y ) ) + 0.5f; 
	}
	inline xrt::Color noisec ( xrt::Tuple &p, float t ) 
	{ 
		return 0.5f * xrt::Color ( noise( p.x, p.y, p.z, t ), noise( p.y, p.z, p.x, t ), noise( p.z, p.x, p.y, t ) ) + 0.5f; 
	}
	xrt::Tuple noisev ( float ), noisev ( float , float ), noisev ( xrt::Tuple& ), noisev ( xrt::Tuple&, float );

	inline float pnoisef ( float u, float uperiod )
	{
		return noisef( mod( u, uperiod ) );
	}
	inline float pnoisef ( float u, float v, float uperiod, float vperiod )
	{
		return noisef( mod( u, uperiod ), mod( v, vperiod ) );
	}
	inline float pnoisef ( xrt::Tuple& p, xrt::Tuple& period )
	{
		return 0.5f * noise( mod( p.x, period.x ), mod( p.y, period.y ), mod( p.z, period.z ) ) + 0.5f;
	}
	inline float pnoisef ( xrt::Tuple& p, float t, xrt::Tuple& period, float tperiod )
	{
		return 0.5f * noise( mod( p.x, period.x ), mod( p.y, period.y ), mod( p.z, period.z ), mod( t, tperiod ) ) + 0.5f;
	}
	xrt::Color pnoisec ( float, float  ), pnoisec ( float , float, float , float  ), pnoisec ( xrt::Tuple&, xrt::Tuple&  ), pnoisec ( xrt::Tuple&, float, xrt::Tuple&, float  );
	xrt::Tuple pnoisep ( float, float  ), pnoisep ( float , float, float , float  ), pnoisep ( xrt::Tuple&, xrt::Tuple&  ), pnoisep ( xrt::Tuple&, float, xrt::Tuple&, float  );
	xrt::Tuple pnoisev ( float, float  ), pnoisev ( float , float, float , float  ), pnoisev ( xrt::Tuple&, xrt::Tuple&  ), pnoisev ( xrt::Tuple&, float, xrt::Tuple&, float  );
	EXPORT float cellnoisef ( float u ); 
	EXPORT float cellnoisef ( float u, float v );
	EXPORT float cellnoisef ( xrt::Tuple &p );
	EXPORT float cellnoisef ( xrt::Tuple &p, float t );
	EXPORT xrt::Tuple cellnoisep ( float u ); 
	EXPORT xrt::Tuple cellnoisep ( float u, float v ); 
	EXPORT xrt::Tuple cellnoisep ( xrt::Tuple &p ); 
	EXPORT xrt::Tuple cellnoisep ( xrt::Tuple &p, float t ); 
	EXPORT xrt::Color cellnoisec ( float u ); 
	EXPORT xrt::Color cellnoisec ( float u, float v ); 
	EXPORT xrt::Color cellnoisec ( xrt::Tuple &p );
	EXPORT xrt::Color cellnoisec ( xrt::Tuple &p, float t ); 
	xrt::Tuple cellnoisev ( float ), cellnoisev ( float , float ), cellnoisev ( xrt::Tuple& ), cellnoisev ( xrt::Tuple&, float );

	// Geometric Functions

	inline float xcomp( const xrt::Tuple& p ) { return p.x; };
	inline float ycomp( const xrt::Tuple& p ) { return p.y; };
	inline float zcomp( const xrt::Tuple& p ) { return p.z; };
	inline void setxcomp( xrt::Tuple& p, const float f ) { p.x = f; };
	inline void setycomp( xrt::Tuple& p, const float f ) { p.y = f; };
	inline void setzcomp( xrt::Tuple& p, const float f ) { p.z = f; };
	inline float comp ( const xrt::Tuple &p, float index ) { return p[(int)index]; }
	inline void setcomp ( xrt::Tuple &p, float index, float value ) { p[(int)index] = value; }
	inline const float length( xrt::Tuple& v ) { return v.length(); }
	inline xrt::Tuple normalize( xrt::Tuple& v ) { return v.hat(); }
	inline float distance( const xrt::Tuple& a, const xrt::Tuple& b ) { return length( b - a ); }
	EXPORT float ptlined( const xrt::Tuple&, const xrt::Tuple&, const xrt::Tuple& );
	EXPORT xrt::Tuple rotate( xrt::Tuple&, float, xrt::Tuple&, xrt::Tuple& );
	EXPORT float area( xrt::ShaderEnvironment& sv, xrt::Tuple& p);  
	inline xrt::Tuple faceforward( xrt::Tuple& N, xrt::Tuple& I, xrt::Tuple& Nref) { return sign( - dot( I, Nref ) ) * N; }
	EXPORT inline xrt::Tuple faceforward( xrt::ShaderEnvironment& sv, xrt::Tuple& N, xrt::Tuple& I );
	inline xrt::Tuple reflect( xrt::Tuple& I, xrt::Tuple& N) { return I - 2 * dot( I, N ) * N; }
	inline xrt::Tuple refract( xrt::Tuple& I, xrt::Tuple& N, float eta )
	{
		float IdotN = dot( I, N );
		float k = 1 - eta * eta * ( 1 - IdotN * IdotN );
		return k < 0 ? ( 0.0, 0.0, 0.0 ) : eta * I - ( eta * IdotN + sqrt( k ) ) * N;
	}
	EXPORT void fresnel( xrt::Tuple&, xrt::Tuple&, float, float &, float& );
	EXPORT void fresnel( xrt::Tuple&, xrt::Tuple&, float, float &, float&, xrt::Tuple&, xrt::Tuple& );
	EXPORT xrt::Tuple transform( xrt::ShaderEnvironment&, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple transform( xrt::ShaderEnvironment&, ustring, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple transform( xrt::Matrix&, xrt::Tuple& );
	xrt::Tuple transform( xrt::ShaderEnvironment&, ustring, xrt::Matrix&, xrt::Tuple& );
	EXPORT xrt::Tuple vtransform( xrt::ShaderEnvironment&, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple vtransform( xrt::ShaderEnvironment&, ustring, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple vtransform( xrt::Matrix&, xrt::Tuple& );
	xrt::Tuple vtransform( xrt::ShaderEnvironment&, ustring, xrt::Matrix&, xrt::Tuple& );
	EXPORT xrt::Tuple ntransform( xrt::ShaderEnvironment&, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple ntransform( xrt::ShaderEnvironment&, ustring, ustring, xrt::Tuple& );
	EXPORT xrt::Tuple ntransform( xrt::Matrix&, xrt::Tuple& );
	xrt::Tuple ntransform( xrt::ShaderEnvironment&, ustring, xrt::Matrix&, xrt::Tuple& );
	EXPORT float depth( xrt::ShaderEnvironment&, xrt::Tuple& );
	EXPORT xrt::Tuple calculatenormal( xrt::ShaderEnvironment& sv, xrt::Tuple& p );

	// Color functions

	inline float comp ( xrt::Tuple &c, float index ) { return c[(int)index]; }
	inline float comp ( xrt::Color &c, float index ) { return c[(int)index]; }
	inline void setcomp ( xrt::Color &c, float index, float value ) { c[(int)index] = value; }
	EXPORT xrt::Color ctransform ( ustring tospace, xrt::Color& c );
	EXPORT xrt::Color ctransform ( ustring fromspace, ustring tospace, xrt::Color &c );
	inline float luminance( xrt::Color &c ) { return 0.114 * c.r + 0.587 * c.g + 0.299 * c.b; }

	// Matrix functions

	inline float comp( const xrt::Matrix& m, float row, float column ) { return m.m[(int)row][(int)column]; }
	inline void setcomp( xrt::Matrix& m, float row, float column, float value ) { m.m[(int)row][(int)column] = value; }
	inline float determinant( const xrt::Matrix& m ) { return m.determinant(); }
	EXPORT xrt::Matrix translate ( const xrt::Matrix& m, const xrt::Tuple& t );
	inline xrt::Matrix rotate ( const xrt::Matrix& m, const float angle, const xrt::Tuple& axis ) { return m * xrt::Matrix( angle, axis ); }
	EXPORT xrt::Matrix scale ( const xrt::Matrix &m, const xrt::Tuple& s );
	EXPORT xrt::Matrix mtransform( xrt::ShaderEnvironment&, ustring, xrt::Matrix& );
	EXPORT xrt::Matrix mtransform( xrt::ShaderEnvironment&, ustring, ustring, xrt::Matrix& );
	EXPORT xrt::Matrix mtransform( xrt::Matrix&, xrt::Matrix& );
	xrt::Matrix mtransform( xrt::ShaderEnvironment&, ustring, xrt::Matrix&, xrt::Matrix& );

	// Array functions
	
	template <typename T> float arraylength( T ) { return -1; }
	template <typename T> float arraylength( const std::vector<T> &array ) { return array.size(); }
		
	// String functions

	#if 0
	string concat ( string a, b, ... )
	#endif
	EXPORT void printf ( xrt::ShaderEnvironment&, ustring pattern, ... );
	EXPORT ustring format ( ustring pattern, ... );
	#if 0
	float match ( string pattern, subject )
	#endif

	// Shading and lighting functions

	void startIlluminance( xrt::ShaderEnvironment& );
	EXPORT void startIlluminance( xrt::ShaderEnvironment&, xrt::Tuple& );
	EXPORT void startIlluminance( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, float );
	bool illuminance( xrt::ShaderEnvironment&, ustring , xrt::Tuple& );
	EXPORT bool illuminance0( xrt::ShaderEnvironment& );
	EXPORT bool illuminance1( xrt::ShaderEnvironment& );
	EXPORT bool illuminance3( xrt::ShaderEnvironment& );
	bool illuminance( xrt::ShaderEnvironment&, ustring, xrt::Tuple&, xrt::Tuple&, float );
	EXPORT bool illuminate( xrt::ShaderEnvironment&, xrt::Tuple& );
	EXPORT bool illuminate( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, float );
	EXPORT bool solar( xrt::ShaderEnvironment& );
	EXPORT bool solar( xrt::ShaderEnvironment&, xrt::Tuple&, float );

	EXPORT xrt::Color ambient( xrt::ShaderEnvironment& );
	EXPORT xrt::Color diffuse( xrt::ShaderEnvironment&, xrt::Tuple& );
	EXPORT xrt::Color specular( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, float );
	EXPORT xrt::Color specularbrdf ( xrt::ShaderEnvironment&, xrt::Tuple&,  xrt::Tuple&, xrt::Tuple&, float );
	EXPORT xrt::Color phong( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, float );
	EXPORT float tracef( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, ... );
	EXPORT xrt::Color tracec( xrt::ShaderEnvironment&, xrt::Tuple&, xrt::Tuple&, int ... );
	EXPORT xrt::Color transmission( xrt::ShaderEnvironment&, const xrt::Tuple&, const xrt::Tuple&, int, ... );
	EXPORT float occlusion3( xrt::ShaderEnvironment&, const xrt::Tuple&, const xrt::Tuple&, float, int, ... );
	EXPORT float occlusion2( xrt::ShaderEnvironment&, const xrt::Tuple&, const xrt::Tuple&, int, ... );
	EXPORT xrt::Color indirectdiffuse3( xrt::ShaderEnvironment&, const xrt::Tuple&, const xrt::Tuple&, float, int, ... );
	EXPORT xrt::Color indirectdiffuse2( xrt::ShaderEnvironment&, const xrt::Tuple&, const xrt::Tuple&, int, ... );

	// Texture mapping functions

	EXPORT float texturef ( xrt::ShaderEnvironment&, ustring, int, int, ... );
	EXPORT float texture2f ( xrt::ShaderEnvironment&, ustring, int, float, float, int, ... );
	EXPORT float texture8f ( xrt::ShaderEnvironment&, ustring, int, float, float, float, float, float, float, float, float, int, ... );
	EXPORT xrt::Color texturec ( xrt::ShaderEnvironment&, ustring, int, int, ... );
	EXPORT xrt::Color texture2c ( xrt::ShaderEnvironment&, ustring, int, float, float, int, ... );
	EXPORT xrt::Color texture8c ( xrt::ShaderEnvironment&, ustring, int, float, float, float, float, float, float, float, float, int, ... );
	float environmentf ( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, int, ... );
	float environmentf ( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, int, ... );
	EXPORT xrt::Color environmentc ( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, int, ... );
	EXPORT xrt::Color environmentc ( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, int, ... );
	EXPORT float shadowf( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, int, ... );
	EXPORT xrt::Color shadowc( xrt::ShaderEnvironment&, ustring, int, xrt::Tuple &, int, ... );
	//float shadow( xrt::ShaderEnvironment&, const char*, int, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, xrt::Tuple &, ... );
	EXPORT float textureinfo ( xrt::ShaderEnvironment&, ustring, ustring, float& );
	EXPORT float textureinfo ( xrt::ShaderEnvironment&, ustring, ustring, std::vector<float>& );
	float textureinfo ( ustring, ustring, xrt::Color& );
	float textureinfo ( ustring, ustring, xrt::Tuple& );
	float textureinfo ( ustring, ustring, xrt::Matrix& );
	float textureinfo ( ustring, ustring, ustring& );

	EXPORT float surface( xrt::ShaderEnvironment&, ustring paramname, float &variable );
	EXPORT float surface( xrt::ShaderEnvironment&, ustring paramname, xrt::Color &variable );
	EXPORT float lightsource( xrt::ShaderEnvironment&, ustring paramname, float &variable );
	EXPORT float lightsource( xrt::ShaderEnvironment&, ustring paramname, xrt::Color &variable );
	EXPORT float displacement( xrt::ShaderEnvironment&, ustring paramname, float &variable );
	EXPORT float displacement( xrt::ShaderEnvironment&, ustring paramname, xrt::Color &variable );
	#if 0
	// Message passing and information functions

	float atmosphere( string paramname; output type variable )
	float incident( string paramname; output type variable )
	float opposite( string paramname; output type variable )
	float attribute ( string name; output type variable )
	string shadername ( )
	string shadername ( string shadertype )
	#endif
	EXPORT float attribute( xrt::ShaderEnvironment&, ustring, float& );
	EXPORT float attribute( xrt::ShaderEnvironment&, ustring, xrt::Tuple& );
	EXPORT float attribute( xrt::ShaderEnvironment&, ustring, ustring& );
	EXPORT float rayinfo( xrt::ShaderEnvironment&, ustring, float& );
	EXPORT float rayinfo( xrt::ShaderEnvironment&, ustring, xrt::Tuple& );
	EXPORT float rayinfo( xrt::ShaderEnvironment&, ustring, ustring& );
};

#endif //__LIBSL_H__
