#ifndef __C4DTOOLS_H
#define __C4DTOOLS_H

#include "ge_vector.h"
#include "ge_matrix.h"
#include "operatingsystem.h"
#include "c4d_string.h"
#include "c4d_raytrace.h"
#include "c4d_shader.h"
#include "c4d_videopostplugin.h"

#define COLOR	         255.99 // converting from vectors to integers
#define PERCENT        100.00
#define THIRD          0.33333333333333
#define SIXTH          0.16666666666666

#define MAXRANGE	     10000000.0 // maximum value for points, vectors...
#define MAXELEMENTS	   100000000 // maximum number of points
#define MINSIZE        0.001	 // epsilon value

class Filename;

// Also called 'LERP'
inline Vector Mix(Vector v1, Vector v2, Real t)	// mixes v1 with v2 dependent on 0<= x <= 1.0
{
	return v1 + t*(v2-v1);
}

// Also called 'LERP'
inline Real Mix(Real v1, Real v2, Real t)
{
	return v1 + t*(v2-v1);
}

inline Real Step(Real a, Real x)
{
	if (x>=a)
		return 1.0;
	else
		return 0.0;
}

inline Real Pulse(Real a, Real b, Real x)
{
	if (x>=a && x<=b)
		return 1.0;
	else
		return 0.0;
}

// Attention: Order of parameters different to Peachey's definition
inline Real Clamp(Real a, Real b, Real x)
{
	return x<a?a:(x>b?b:x);
}

inline Real Boxstep(Real a, Real b, Real x)
{
	if (b==a) { if (x<a) return 0.0; else return 1.0; }
	x = (x-a)/(b-a);
	return x<0.0?0.0:(x>1.0?1.0:x);
}

inline Real Smoothstep(Real a, Real b, Real x)
{
	if (x< a) return 0.0;
	if (x>=b) return 1.0;

	x = (x-a)/(b-a); // normalize to [0,1]

	return x*x*(3.0-2.0*x);
}

inline Real Modulo(Real a, Real b)
{
	LONG n = (LONG) (a/b);

	a -= n*b;
	if (a<0.0) a+= b;

	return a;
}

inline Real Gammacorrect(Real gamma, Real x)
{
	return Pow(x,Real(1.0/gamma));
}

inline Real Bias(Real b, Real x)
{
  return Pow(x,Real(-Ln(b)/0.693147180559945));
}

inline Real Gain(Real g, Real x)
{
  if (x<0.5)
    return Bias(1.0-g,2.0*x)*0.5;
	else
    return 1.0-Bias(1.0-g,2.0-2.0*x)*0.5;
}

inline Vector CutColor(const Vector &v)
{
	Vector r;
	if (v.x>1.0) r.x=1.0; else if (v.x<0.0) r.x=0.0; else r.x = v.x;
	if (v.y>1.0) r.y=1.0; else if (v.y<0.0) r.y=0.0; else r.y = v.y;
	if (v.z>1.0) r.z=1.0; else if (v.z<0.0) r.z=0.0; else r.z = v.z;
	return r;
}

inline Real Truncate(Real x)
{
	if (x>=0.0)
		return Floor(x);
	else
		return Ceil(x);
}

inline Bool VectorEqual(const Vector &v1, const Vector &v2, Real epsilon=0.01)
{
	return (Abs(v1.x-v2.x)<epsilon && Abs(v1.y-v2.y)<epsilon && Abs(v1.z-v2.z)<epsilon);
}

inline Real VectorSum(const Vector &v)
{
	return v.x+v.y+v.z;
}

inline Real VectorGray(const Vector &v)
{
	return (v.x+v.y+v.z)*THIRD;
}

inline Real VectorAngle(const Vector &v1, const Vector &v2)
{
	Real l=Sqrt((v1*v1)*(v2*v2));
	if (l==0.0) return 0.0;
  return ACos(v1 * v2 / l);
}

inline Real VectorMin(const Vector &v)
{
	if (v.x<v.y)
	{
		if (v.z<v.x)
			return v.z;
		else
			return v.x;
	}
	else
	{
		if (v.z<v.y)
			return v.z;
		else
			return v.y;
	}
}

inline Real VectorMax(const Vector &v)
{
	if (v.x>v.y)
	{
		if (v.z>v.x)
			return v.z;
		else
			return v.x;
	}
	else
	{
		if (v.z>v.y)
			return v.z;
		else
			return v.y;
	}
}

Matrix MatrixMove  (const Vector &t);
Matrix MatrixScale (const Vector &s);
Matrix MatrixRotX  (Real w);
Matrix MatrixRotY  (Real w);
Matrix MatrixRotZ  (Real w);

Vector MatrixToHPB			(const Matrix &m);						// calculate euler angles from Matrix
Vector VectorToHPB			(const Vector &p);						// calculate euler angles from vector, bank always 0.0
Matrix HPBToMatrix			(const Vector &hpb);					// construct Matrix from euler angles
void   MatrixToRotAxis  (const Matrix &m, Vector *v, Real *w); 	// calculate rotation axis v and angle w from matrix m
Matrix RotAxisToMatrix  (const Vector &v, Real w);	    		// calculate matrix from rotation axis v and angle w
Vector GetOptimalAngle  (const Vector &hpb_old, const Vector &hpb_new);	// calculate optimal angles between two angle values
Vector PointLineDistance(const Vector &p0, const Vector &v, const Vector &p);
LVector ReflectRay			(LVector *v, Vector *n);

class MinMax
{
	private:
		Vector min,max;
		Bool   used;

	public:
		MinMax(void) { min=MAXREAL; max=MINREAL; used=FALSE; }
		inline MinMax(const Vector &v) { min=max=v; used=TRUE; }
		inline void Init(void) { min=MAXREAL; max=MINREAL; used=FALSE; } // reset min and max
		inline void AddPoint(const Vector &p) // add point and recalculate min and max
		{
			if (used)
			{
				if (p.x<min.x) min.x = p.x;
				if (p.y<min.y) min.y = p.y;
				if (p.z<min.z) min.z = p.z;
				if (p.x>max.x) max.x = p.x;
				if (p.y>max.y) max.y = p.y;
				if (p.z>max.z) max.z = p.z;
			}
			else
			{
				min.x = p.x;
				min.y = p.y;
				min.z = p.z;
				max.x = p.x;
				max.y = p.y;
				max.z = p.z;
				used = TRUE;
			}
		}

		inline Bool   Content(void)	const { return used; }
		inline Vector GetMin (void) const { if (used) return min; else return 0.0; }
		inline Vector GetMax (void) const { if (used) return max; else return 0.0; }
		inline Vector GetMp  (void) const { if (used) return (min+max)*0.5; else return 0.0; } // middle between min and max
		inline Vector GetRad (void) const { if (used) return (max-min)*0.5; else return 0.0; } // half distance between min and max
		inline void GetMpRad(Vector *mp, Vector *rad) const { if (used) {*mp = (min+max)*0.5; *rad = max - *mp; } else *mp=*rad=0.0; } // half distance between min and max
};

class Random
{
	private:
	 ULONG seed;
	 LONG iset;
	 Real gset;

	public:
		Random(void);

		void Init(ULONG s);

		Real Get01(void);	 // return random value between 0 and +1
		Real Get11(void);	 // return random value between -1 and +1

		Real GetG01(void);	 // return random value between 0 and +1 with gaussian distribution
		Real GetG11(void);	 // return random value between -1 and +1 with gaussian distribution

		LONG GetSeed(void) { return seed; }
};

inline Real	SNoise(const Vector &p) { return C4DOS.Sh->SNoise(p); }
inline Real	SNoise(const Vector &p, Real t) { return C4DOS.Sh->SNoiseT(p,t); }
inline Real	Noise(const Vector &p) { return C4DOS.Sh->Noise(p); }
inline Real	Noise(const Vector &p, Real t) { return C4DOS.Sh->NoiseT(p,t); }
inline Real	PNoise(const Vector &p, const Vector &d) { return C4DOS.Sh->PNoise(p,d); }
inline Real	PNoise(const Vector &p, Real t, const Vector &d, Real dt) { return C4DOS.Sh->PNoiseT(p,t,d,dt); }
inline Real	Turbulence(const Vector &p, Real oct, Bool abs) { return C4DOS.Sh->Turbulence(p,oct,abs); }
inline Real	Turbulence(const Vector &p, Real t, Real oct, Bool abs)  { return C4DOS.Sh->TurbulenceT(p,t,oct,abs); }
inline Real	WavyTurbulence(const Vector &p, Real t, Real oct, Real start)  { return C4DOS.Sh->WavyTurbulence(p,t,oct,start); }
inline void	InitFbm(Real *table, LONG max_octaves, Real lacunarity, Real h)  { C4DOS.Sh->InitFbm(table,max_octaves,lacunarity,h); }
inline Real	Fbm(Real *table, const Vector &p, Real oct) { return C4DOS.Sh->Fbm(table,p,oct); }
inline Real	Fbm(Real *table, const Vector &p, Real t, Real oct)  { return C4DOS.Sh->FbmT(table,p,t,oct); }
inline Real	RidgedMultifractal(Real *table, const Vector &p, Real oct, Real offset, Real gain)  { return C4DOS.Sh->RidgedMultifractal(table,p,oct,offset,gain); }
inline Real	CalcSpline(Real x, Real *knot, LONG nknots)  { return C4DOS.Sh->CalcSpline(x,knot,nknots); }
inline Vector	CalcSpline(Real x, Vector *knot, LONG nknots)  { return C4DOS.Sh->CalcSplineV(x,knot,nknots); }

Vector RGBToHSV(const Vector &col);
Vector HSVToRGB(const Vector &col);

// shadow options for Illuminate routines
#define ILLUMINATE_SHADOW_OFF							 0
#define ILLUMINATE_SHADOW_ON							 1
#define ILLUMINATE_SHADOW_NOENVIRONMENT		 2

struct VolumeData : public BaseVolumeData
{
	void	    Illuminance(Vector *diffuse, Vector *specular, IlluminationModel *model, void *data) { C4DOS.Sh->Illuminance(this,diffuse,specular,model,data); }
	void	    Illuminance1(Vector *diffuse, Vector *specular, Real exponent) { C4DOS.Sh->Illuminance1(this,diffuse,specular,exponent); }
	void	    Illuminance2(Vector *diffuse, Vector *specular, Real exponent, Vector *p, Vector *n, Vector *bumpn, Vector *rayv, LONG calc_shadow) { C4DOS.Sh->Illuminance2(this,diffuse,specular,exponent,p,n,bumpn,rayv,calc_shadow); }
	Vector    Illuminance3(Vector *p, LONG calc_shadow) { return C4DOS.Sh->Illuminance3(this,p,calc_shadow); }

	LONG	    GetCurrentCPU(void) { return C4DOS.Sh->GetCurrentCPU(this); }
	LONG	    GetCPUCount(void) { return C4DOS.Sh->GetCPUCount(this); }
	Vector    TraceColor(Ray *ray, Real maxdist, LONG last_hit, Vector *p, Vector *n, LONG *hit) { return C4DOS.Sh->TraceColor(this,ray,maxdist,last_hit,p,n,hit); }
	Vector    TraceColorDirect(Ray *ray, Real maxdist, LONG raydepth, LONG raybits, LONG last_hit, LVector *p, LVector *n, LONG *hit) { return C4DOS.Sh->TraceColorDirect(this,ray,maxdist,raydepth,raybits,last_hit,p,n,hit); }
	void      TraceGeometry(Ray *ray, Real maxdist, LONG last_hit, Vector *p, Vector *n, LONG *hit)  { C4DOS.Sh->TraceGeometry(this,ray,maxdist,last_hit,p,n,hit); }
	RayObject *ID_to_Obj(LONG global_id, LONG *local_id) { return C4DOS.Sh->ID_to_Obj(this,global_id,local_id); }
	LONG	    Obj_to_ID(RayObject *op) { return C4DOS.Sh->Obj_to_ID(this,op); }
	LONG			Obj_to_Num(RayObject *obj) { return C4DOS.Sh->Obj_to_Num(this,obj); }
	RayObject *GetObj(LONG index) { return C4DOS.Sh->GetObj(this,index); }
	LONG	    GetObjCount(void) { return C4DOS.Sh->GetObjCount(this); }
	void	    GetUVW(RayObject *op, LONG uvwind, LONG local_id, PolyVector *uvw) { C4DOS.Sh->GetUVW(this,op,uvwind,local_id,uvw); }
	void	    GetNormals(RayObject *op, LONG local_id, PolyVector *norm) { C4DOS.Sh->GetNormals(this,op,local_id,norm); }
	TexData   *GetTexData(RayObject *op, LONG index) { return C4DOS.Sh->GetTexData(this,op,index); }
	Vector	  GetNormal(RayObject *op, LONG local_id) { return C4DOS.Sh->GetNormal(this,op,local_id); }
	Vector	  GetSmoothedNormal(LONG global_id, const Vector &p) { return C4DOS.Sh->GetSmoothedNormal(this,global_id,p); }
	Bool      GetRS(LONG global_id, const Vector &p, Real *r, Real *s) { return C4DOS.Sh->GetRS(this,global_id,p,r,s); }
	RayLight  *GetLight(LONG index) { return C4DOS.Sh->GetLight(this,index); }
	LONG	    GetLightCount(void) { return C4DOS.Sh->GetLightCount(this); }
	LONG			Light_to_Num(RayLight *light) { return C4DOS.Sh->Light_to_Num(this,light); }
	Bool	    Illuminate(RayLight *rl, Vector *color, Vector *light_vector, const Vector &p, const Vector &n, LONG calc_shadow, LONG last_hit) { return C4DOS.Sh->IlluminateEx(this,rl,color,light_vector,p,n,calc_shadow,last_hit); }
	void      LightCalcSpecial(RayLight *rl, Real exponent, Bool nodiff, Bool nospec, const Vector &rayv, Real cosc, const Vector &p, const Vector &bumpn, Real &diffuse, Real &specular);
	void      *GetRayData(LONG i) { return C4DOS.Sh->GetRayData(this,i); }
	RayCamera *GetRayCamera(void) { return (RayCamera*)C4DOS.Sh->GetRayData(this,RAY_CAMERA); }
	RayParameter *GetRayParameter(void) { return (RayParameter*)C4DOS.Sh->GetRayData(this,RAY_PARAMETER); }
	RayEnvironment *GetRayEnvironment(void) { return (RayEnvironment*)C4DOS.Sh->GetRayData(this,RAY_ENVIRONMENT); }
	RayObject *GetRaySky(void) { return (RayObject*)C4DOS.Sh->GetRayData(this,RAY_SKY); }
	RayObject *GetRayForeground(void) { return (RayObject*)C4DOS.Sh->GetRayData(this,RAY_FOREGROUND); }
	RayObject *GetRayBackground(void) { return (RayObject*)C4DOS.Sh->GetRayData(this,RAY_BACKGROUND); }
	void	    GetRay(Real x, Real y, Ray *ray) { C4DOS.Sh->GetRay(this,x,y,ray); }
	Vector    ScreenToCamera(const Vector &p) { return C4DOS.Sh->ScreenToCamera(this,p); }
	Vector    CameraToScreen(const Vector &p) { return C4DOS.Sh->CameraToScreen(this,p); }
	Vector    CalcShadow(RayLight *l, const Vector &p, const Vector &n, Bool transparency, LONG last_hit) { return C4DOS.Sh->CalcShadow(this,l,p,n,transparency,last_hit); }
	Vector    CalcHardShadow(const Vector &p1, const Vector &p2, Bool transparency, LONG last_hit) { return C4DOS.Sh->CalcHardShadow(this,p1,p2,transparency,last_hit); }
	Vector		GetPointUVW(TexData *tdp, LONG global_id, const Vector &p) { return C4DOS.Sh->GetPointUVW(this,tdp,global_id,p); }
	Bool			ProjectPoint(TexData *tdp, LONG global_id, const Vector &p, const Vector &n, Vector *uv)	{ return C4DOS.Sh->ProjectPoint(this,tdp,global_id,p,n,uv); }
	void			StatusSetText(const String &str) { C4DOS.Sh->StatusSetText(this,&str); }
	void			StatusSetBar(Real percentage) { C4DOS.Sh->StatusSetBar(this,percentage); }
	Vector		CalcVisibleLight(Ray *ray, Real maxdist, Vector *trans) { return C4DOS.Sh->CalcVisibleLight(this,ray,maxdist,trans); }
	void			GetDUDV(TexData *tex, const Vector &p, const Vector &n, Vector *ddu, Vector *ddv) { C4DOS.Sh->GetDUDV(this,tex,p,n,ddu,ddv); }
	void			GetXY(LONG *x, LONG *y, LONG *scale) { C4DOS.Sh->GetXY(this,x,y,scale); }
	MinMax		GetSceneBoundaries(void);
	void			CopyTo(VolumeData *dst) { C4DOS.Sh->CopyVolumeData(this,dst); }
	void			OutOfMemory(void) { C4DOS.Sh->OutOfMemory(this); }
	void  		GetSurfaceData(SurfaceData *cd, Bool calc_illum, Bool calc_shadow, Bool calc_refl, Bool calc_trans, Ray *ray, const Vector &p, const Vector &n, LONG global_id) { C4DOS.Sh->GetSurfaceData(this,cd,calc_illum,calc_shadow,calc_refl,calc_trans,ray,p,n,global_id); }
	Real			GetLightFalloff(LONG falloff, Real inner, Real outer, Real dist) { return C4DOS.Sh->GetLightFalloff(falloff,inner,outer,dist); }
	Bool			TestBreak(void) { return C4DOS.Sh->TestBreak(this); }
	PluginVideoPost* GetVideoPost(LONG nth) { return C4DOS.Sh->FindVideoPost(this,nth,TRUE); }
	PluginVideoPost* FindVideoPost(LONG id) { return C4DOS.Sh->FindVideoPost(this,id,FALSE); }
	VPFragment	**GetFragments(LONG x, LONG y, LONG cnt, LONG flags) { return C4DOS.Sh->VPGetFragments(this,x,y,cnt,flags); }
	Bool			AddLensGlow(LensGlowStruct *lgs, Vector *lgs_pos, LONG lgs_cnt, Real intensity) { return C4DOS.Sh->AddLensGlow(this,lgs,lgs_pos,lgs_cnt,intensity); }
	Bool			SampleLensFX(VPBuffer *rgba, VPBuffer *fx, BaseThread *bt) { return C4DOS.Sh->SampleLensFX(this,rgba,fx,bt); }
	UWORD*		GetFragmentMask(void) { return C4DOS.Sh->GetFragmentMask(this); }
	LONG			TranslateObjIndex(LONG index, Bool old_to_new) { return C4DOS.Sh->TranslateObjIndex(this,index,old_to_new); }
	Bool			TranslatePolygon(RayObject *op, LONG local_index, Vector *previous_points) { return C4DOS.Sh->TranslatePolygon(this,op,local_index,previous_points); }
	Bool			SetRayTolerance(Real tolerance) { return C4DOS.Sh->SetRayTolerance(this,tolerance); }

	static VolumeData *Alloc(void); // should normally NOT be used
	static void Free(VolumeData *&vd);
};

RayLight *AllocRayLight(BaseObject *op);
void FreeRayLight(RayLight *&lgt);
Bool IlluminateRayLight(RayLight *rl, Vector *color, Vector *light_vector, const Vector &p, const Vector &n);

class RayFilter
{
	private:
		RayFilter(void);

	public:
		void AddLine(Real *line, LONG y);
		void EvaluateLine(Bool display, LONG y, LONG xcnt, BaseBitmap *bmp, LONG alpha);
		LONG GetYRadius(void);
		void SetRange(LONG t_top, LONG t_bottom);

	static RayFilter *Alloc(LONG xres, LONG yres, LONG components, LONG left, LONG right, LONG type, Real softness, Bool dithering);
	static void Free(RayFilter *&filter);
};

class VPBuffer
{
	private:
		VPBuffer(void);

	public:
		LONG GetInfo(LONG type);
		Bool GetLine(LONG x, LONG y, LONG cnt, void *data, LONG bitdepth, Bool dithering);
		Bool SetLine(LONG x, LONG y, LONG cnt, void *data, LONG bitdepth, Bool dithering);

		LONG GetBw(void) { return C4DOS.Sh->VPGetInfo(this,VPGETINFO_XRESOLUTION); }
		LONG GetBh(void) { return C4DOS.Sh->VPGetInfo(this,VPGETINFO_YRESOLUTION); }
		LONG GetBt(void) { return C4DOS.Sh->VPGetInfo(this,VPGETINFO_BITDEPTH); }
		LONG GetCpp(void) { return C4DOS.Sh->VPGetInfo(this,VPGETINFO_CPP); }
		Bool GetVisibleBit(void) { return C4DOS.Sh->VPGetInfo(this,VPGETINFO_VISIBLE); }
};

struct VideoPostStruct : public BaseVideoPostStruct
{
	private:
		VideoPostStruct(void);
};

class Render
{
	private:
		Render(void);
	public:
		Bool					AllocateBuffer(LONG id, LONG subid, LONG bitdepth, Bool visible);
		LONG					AllocateBufferFX(LONG id, const String &name, LONG bitdepth, Bool visible);
		VPBuffer			*GetBuffer(LONG id, LONG subid);
		BaseContainer GetRenderData(void);
		void					SetRenderData(const BaseContainer &bc);
		void					UpdateScreen(void);
		VolumeData		*GetInitialVolumeData(LONG cpu);
};

void CalcRestrictionInc(LightRestriction *lr, RayObject *op, Bool &nodif, Bool &nospec);

#endif
