//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	attributes.cpp
//  Classes				:	CAttributes
//  Description			:	Implementation
//
////////////////////////////////////////////////////////////////////////
#include <math.h>
#include <string.h>

#include "attributes.h"
#include "ri.h"
#include "renderer.h"
#include "stats.h"
#include "photonMap.h"
#include "irradiance.h"
#include "error.h"

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	CAttributes
// Description			:	The constructor, the default values for
//							attributes are given here
// Return Value			:	-
// Comments				:
// Date last edited		:	3/11/2003
CAttributes::CAttributes() {
	next					=	NULL;
	refCount				=	0;

	stats.numAttributes++;
	stats.attributesMemory	+=	sizeof(CAttributes);

	surface					=	NULL;
	displacement			=	NULL;
	atmosphere				=	NULL;
	interior				=	NULL;
	exterior				=	NULL;
	usedParameters			=	0;

	initv(surfaceColor,1,1,1);
	initv(surfaceOpacity,1,1,1);

	s[0]					=	0.0;
	s[1]					=	1.0;
	s[2]					=	0.0;
	s[3]					=	1.0;
	t[0]					=	0.0;
	t[1]					=	0.0;
	t[2]					=	1.0;
	t[3]					=	1.0;	

	initv(bmin,C_INFINITY,C_INFINITY,C_INFINITY);
	initv(bmax,-C_INFINITY,-C_INFINITY,-C_INFINITY);
	bexpand					=	0.00001f;	// Epsilon

	{
		int	i,j;

		for (i=0;i<4;i++)
			for (j=0;j<4;j++) {
				uBasis[element(i,j)]	=	RiBezierBasis[i][j];
				vBasis[element(i,j)]	=	RiBezierBasis[i][j];
			}
	}

	uStep						=	3;
	vStep						=	3;

	nSides						=	2;

	flags						=	0;
	flags						|=	ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
	flags						|=	ATTRIBUTES_FLAGS_SINGULARITYFIX;

	minSubdivision				=	2;
	maxSubdivision				=	5;

	flatness					=	0.5f;
	pointDeviation				=	C_INFINITY;
	normalDeviation				=	cosf(radians(45));

	maxDisplacement				=	0;
	maxDisplacementSpace		=	NULL;

	lightSources				=	NULL;

	shadingRate					=	1.0f;
	motionFactor				=	0.0f;

	name						=	NULL;

	numUProbes					=	4;
	numVProbes					=	4;
	minSplits					=	2;
	rasterExpand				=	0.5f;
	shadowBias					=	0.01f;

	transmission				=	'o';

	emit						=	-1;
	relativeEmit				=	1;

	shadingModel				=	SM_MATTE;
	
	globalMapName				=	NULL;
	causticMapName				=	NULL;
	globalMap					=	NULL;
	causticMap					=	NULL;
	irradianceHandle			=	NULL;
	irradianceHandleMode		=	NULL;
	irradianceMaxError			=	1.0f;
	irradianceMaxPixelDistance	=	20.0f;
	photonEstimator				=	100;
	photonIor					=	1.5;
	maxDiffuseDepth				=	1;
	maxSpecularDepth			=	2;

	shootStep					=	1000;		// Shoot 1000 rays at a time
	
	lodRange[0]					=	-C_INFINITY;
	lodRange[1]					=	-C_INFINITY;
	lodRange[2]					=	C_INFINITY;
	lodRange[3]					=	C_INFINITY;
	lodSize						=	0;
	lodImportance				=	1;
	

	defaultsReady				=	FALSE;
	defaults					=	NULL;
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	CAttributes
// Description			:	This functions creates an exact replica of 
//							another attribute set
// Return Value			:	-
// Comments				:
// Date last edited		:	3/13/2003
CAttributes::CAttributes(const CAttributes *a) {
	CActiveLight	*cLight,*nLight;

	stats.numAttributes++;
	stats.attributesMemory	+=	sizeof(CAttributes);

	this[0]						=	a[0];
	
	refCount					=	0;

	if (surface != NULL)			surface->attach();
	if (displacement != NULL)		displacement->attach();
	if (atmosphere != NULL)			atmosphere->attach();
	if (interior != NULL)			interior->attach();
	if (exterior != NULL)			exterior->attach();

	if (globalMapName != NULL)		globalMapName	=	strdup(a->globalMapName);
	if (causticMapName != NULL)		causticMapName	=	strdup(a->causticMapName);
	if (globalMap != NULL)			globalMap->attach();
	if (causticMap != NULL)			causticMap->attach();

	irradianceHandle			=	(a->irradianceHandle == NULL ?		NULL : strdup(a->irradianceHandle));
	irradianceHandleMode		=	(a->irradianceHandleMode == NULL ?	NULL : strdup(a->irradianceHandleMode));

	if (a->maxDisplacementSpace != NULL)	maxDisplacementSpace	=	strdup(a->maxDisplacementSpace);

	lightSources				=	NULL;
		
	for (cLight=a->lightSources;cLight!=NULL;cLight=cLight->next) {
		nLight			=	new CActiveLight;

		nLight->light		=	cLight->light;
		nLight->defaults	=	NULL;
		nLight->next		=	lightSources;
		lightSources		=	nLight;
	}

	name						=	(a->name != NULL ? strdup(a->name) : NULL);

	defaultsReady				=	FALSE;
	defaults					=	NULL;

	if (a->next != NULL) next	=	new CAttributes(a->next);
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	~CAttributes
// Description			:	Deallocate everything
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
CAttributes::~CAttributes(){
	CActiveLight	*cLight;
	CGlobalDefault	*cGlobal;

	stats.numAttributes--;
	stats.attributesMemory	-=	sizeof(CAttributes);

	if (surface != NULL)				surface->detach();
	if (displacement != NULL)			displacement->detach();
	if (atmosphere != NULL)				atmosphere->detach();
	if (interior != NULL)				interior->detach();
	if (exterior != NULL)				exterior->detach();

	if (globalMapName != NULL)			free(globalMapName);
	if (causticMapName != NULL)			free(causticMapName);
	if (globalMap != NULL)				globalMap->detach();
	if (causticMap != NULL)				causticMap->detach();
	if (irradianceHandle != NULL)		free(irradianceHandle);
	if (irradianceHandleMode != NULL)	free(irradianceHandleMode);

	if (name != NULL)					free(name);

	if (maxDisplacementSpace != NULL)	free(maxDisplacementSpace);

	while((cLight=lightSources) != NULL) {
		lightSources	=	cLight->next;
		while((cGlobal=(CGlobalDefault*)cLight->defaults) != NULL) {
			cLight->defaults	=	(CGlobalDefault*)cGlobal->next;
			delete cGlobal;
		}
		delete cLight;
	}

	while((cGlobal=defaults) != NULL) {
		defaults		=	cGlobal->next;
		delete cGlobal;
	}

	if (next != NULL)			delete next;
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	addLight
// Description			:	Obvious !
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
void	CAttributes::addLight(CShaderInstance *cLight) {
	CActiveLight	*nLight;

	for (nLight=lightSources;nLight!=NULL;nLight=nLight->next) {
		if (nLight->light == cLight)	return;
	}

	nLight				=	new CActiveLight;
	nLight->defaults	=	NULL;
	nLight->light		=	(CProgrammableShaderInstance*) cLight;
	nLight->next		=	lightSources;
	lightSources		=	nLight;
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	removeLight
// Description			:	This method must be used to remove a light
//							source from the active light source list
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
void	CAttributes::removeLight(CShaderInstance *light) {
	CActiveLight	*cLight,*pLight;
	CGlobalDefault	*cGlobal;

	for (pLight=NULL,cLight=lightSources;cLight!=NULL;pLight=cLight,cLight=cLight->next) {
		if (cLight->light == light) {
			if (pLight == NULL)	lightSources	=	cLight->next;
			else				pLight->next	=	cLight->next;

			while((cGlobal=(CGlobalDefault*)cLight->defaults) != NULL) {
				cLight->defaults	=	(CGlobalDefault*)cGlobal->next;
				delete cGlobal;
			}
			delete cLight;

			break;
		}
	}
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	lightParameterCheck
// Description			:	Check the light parameters
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
void	CAttributes::checkParameters() {
	CActiveLight	*cLight;

	usedParameters	=	0;

	if (displacement != NULL)	usedParameters	|=	displacement->requiredParameters();
	if (surface != NULL)		usedParameters	|=	surface->requiredParameters();
	if (atmosphere != NULL)		usedParameters	|=	atmosphere->requiredParameters();
	if (interior != NULL)		usedParameters	|=	interior->requiredParameters();
	if (exterior != NULL)		usedParameters	|=	exterior->requiredParameters();

	if (surface == NULL)		usedParameters	|=	PARAMETER_N;

	for (cLight=lightSources;cLight!=NULL;cLight=cLight->next) {
		usedParameters	|=	cLight->light->requiredParameters();
	}
}

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	initDefaults & initLightDefaults
// Description			:	Initialise the default global variables
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
// Init the default values

#define performInitCopy()																\
	for (;cDef!=NULL;cDef=cDef->next) {													\
		CVariable	*cVar	=	cDef->variable;											\
																						\
		i			=	((cVar->container == CONTAINER_UNIFORM) ? 1 : numVertices);		\
		numItems	=	cVar->numItems;													\
																						\
		switch(cVar->type) {															\
		case TYPE_FLOAT:																\
																						\
			srcFloat	=	(const float *) cDef->value;								\
			destFloat	=	variables[cVar->entry];										\
			for (;i>0;i--) {															\
				for (j=0;j<numItems;j++) {												\
					*destFloat++	=	srcFloat[j];									\
				}																		\
			}																			\
			break;																		\
																						\
		case TYPE_COLOR:																\
		case TYPE_VECTOR:																\
		case TYPE_NORMAL:																\
		case TYPE_POINT:																\
																						\
			destFloat	=	variables[cVar->entry];										\
			for (;i>0;i--) {															\
				srcFloat	=	(const float *) cDef->value;							\
				for (j=numItems;j>0;j--) {												\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
				}																		\
			}																			\
			break;																		\
		case TYPE_MATRIX:																\
																						\
			destFloat	=	variables[cVar->entry];										\
			for (;i>0;i--) {															\
				srcFloat	=	(const float *) cDef->value;							\
				for (j=numItems;j>0;j--) {												\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
				}																		\
			}																			\
			break;																		\
																						\
		case TYPE_QUAD:																	\
																						\
			destFloat	=	variables[cVar->entry];										\
			for (;i>0;i--) {															\
				srcFloat	=	(const float *) cDef->value;							\
				for (j=numItems;j>0;j--) {												\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
				}																		\
			}																			\
			break;																		\
																						\
		case TYPE_DOUBLE:																\
																						\
			destFloat	=	variables[cVar->entry];										\
			for (;i>0;i--) {															\
				srcFloat	=	(const float *) cDef->value;							\
				for (j=numItems;j>0;j--) {												\
					*destFloat++	=	*srcFloat++;									\
					*destFloat++	=	*srcFloat++;									\
				}																		\
			}																			\
			break;																		\
																						\
		case TYPE_STRING:																\
																						\
			destString	=	(const char **) variables[cVar->entry];						\
			for (;i>0;i--) {															\
				srcString	=	(const char **) cDef->value;							\
				for (j=numItems;j>0;j--) {												\
					*destString++	=	*srcString++;									\
				}																		\
			}																			\
			break;																		\
																						\
		case TYPE_INTEGER:																\
		case TYPE_BOOLEAN:																\
		default:																		\
			error(CODE_BUG,"Unexpected global variable type\n");						\
			break;																		\
		}																				\
	}
	
void	CAttributes::initDefaults(int numVertices,float **variables) {
	CGlobalDefault	*cDef;
	const float		*srcFloat;
	const char		**srcString;
	float			*destFloat;
	const char		**destString;
	int				i,j,numItems;

	// Are the variables that we need to init collected ?
	if (defaultsReady == FALSE) {
		CActiveLight	*cLight;

		// Ask every shader to register global parameter defaults
		if (surface != NULL)		surface->registerDefaults(this);
		if (displacement != NULL)	displacement->registerDefaults(this);
		if (atmosphere != NULL)		atmosphere->registerDefaults(this);
		if (interior != NULL)		interior->registerDefaults(this);
		if (exterior != NULL)		exterior->registerDefaults(this);

		for (cLight=lightSources;cLight!=NULL;cLight=cLight->next)	cLight->light->registerDefaults(this,cLight);

		defaultsReady	=	TRUE;
	}
	cDef = defaults;

	performInitCopy();
}

void	CAttributes::initLightDefaults(int numVertices,float **variables, CActiveLight *light) const {
	CGlobalDefault	*cDef;
	const float		*srcFloat;
	const char		**srcString;
	float			*destFloat;
	const char		**destString;
	int				i,j,numItems;

	// We are initializing defaults for a light, the defaults
	// must already be ready because lights can only be called from
	// within another shading context
	cDef = (CGlobalDefault*)light->defaults;
	performInitCopy();
}

#undef performInitCopy

///////////////////////////////////////////////////////////////////////
// Class				:	CAttributes
// Method				:	addDefault
// Description			:	Add a global variable that needs to be defaulted
// Return Value			:	-
// Comments				:
// Date last edited		:	3/3/2001
void	CAttributes::addDefault(CVariable *cVariable,void *cData,CActiveLight *light) {
	CGlobalDefault	*cDefault	=	new CGlobalDefault;

	if ( light != NULL ){
		// These are defaults for a light
		cDefault->variable	=	cVariable;
		cDefault->value		=	cData;
		cDefault->next		=	(CGlobalDefault*)light->defaults;
		light->defaults		=	cDefault;
	} else {
		cDefault->variable	=	cVariable;
		cDefault->value		=	cData;
		cDefault->next		=	defaults;
		defaults			=	cDefault;
	}
}
