/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / Scene Graph sub-project
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */


#include <gpac/intern/m4_scenegraph_dev.h>
/*MPEG4 tags (for internal nodes)*/
#include <gpac/m4_mpeg4_nodes.h>

void format_sftime_string(Fixed _val, char *str)
{
	u32 h, m, s;
	Bool neg = 0;
	Float val = FIX2FLT(_val);
	if (val<0) {
		val = -val;
		neg = 1;
	}
	h = (u32) (val/3600);
	m = (u32) (val/60) - h*60;
	s = (u32) (val) - h*3600 - m*60;
	sprintf(str, "%s%02d:%02d:%02d", neg ? "-" : "", h, m, s);
}

void SetValuatorOutput(M_Valuator *p, SFVec4f *inSFField, GenMFField *inMFField, u32 inType)
{
	char str[500];
	u32 i;
	LPROUTE r;
	SFVec4f output, sf_out;
	u32 count, num_out;

	if (!p->sgprivate->NodeID && !p->sgprivate->scenegraph->pOwningProto) return;
	if (!p->sgprivate->routes) return;

	num_out = 1;

	if (!inMFField) {
		count = 1;
		output.x = m4_mulfix(p->Factor1, inSFField->x) + p->Offset1;
		output.y = m4_mulfix(p->Factor2, inSFField->y) + p->Offset2;
		output.z = m4_mulfix(p->Factor3, inSFField->z) + p->Offset3;
		output.q = m4_mulfix(p->Factor4, inSFField->q) + p->Offset4;

		if (p->Sum) {
			output.x = output.x + output.y + output.z + output.q;
			output.y = output.z = output.q = output.x;
		}

		switch (inType) {
		case FT_SFVec2f:
			num_out = 2;
			break;
		case FT_SFVec3f:
		case FT_SFColor:
			num_out = 3;
			break;
		case FT_SFVec4f:
		case FT_SFRotation:
			num_out = 4;
			break;
		}
	} else {
		count = inMFField->count;
	}
	/*reallocate all MF fields*/
	VRML_MF_Reset(&p->outMFColor, FT_MFColor);
	VRML_MF_Reset(&p->outMFFloat, FT_MFFloat);
	VRML_MF_Reset(&p->outMFInt32, FT_MFInt32);
	VRML_MF_Reset(&p->outMFRotation, FT_MFRotation);
	VRML_MF_Reset(&p->outMFString, FT_MFString);
	VRML_MF_Reset(&p->outMFVec2f, FT_MFVec2f);
	VRML_MF_Reset(&p->outMFVec3f, FT_MFVec3f);

	VRML_MF_Alloc(&p->outMFColor, FT_MFColor, count);
	VRML_MF_Alloc(&p->outMFFloat, FT_MFFloat, count);
	VRML_MF_Alloc(&p->outMFInt32, FT_MFInt32, count);
	VRML_MF_Alloc(&p->outMFRotation, FT_MFRotation, count);
	VRML_MF_Alloc(&p->outMFString, FT_MFString, count);
	VRML_MF_Alloc(&p->outMFVec2f, FT_MFVec2f, count);
	VRML_MF_Alloc(&p->outMFVec3f, FT_MFVec3f, count);

	/*set all MF outputs*/
	assert(count);
	for (i=0; i<count; i++) {
		if (inType) {
			switch (inType) {
			case FT_MFInt32:
			{
				SFInt32 sfi = ((MFInt32 *)inMFField)->vals[i];
				Fixed vi = INT2FIX(sfi);
				output.x = m4_mulfix(p->Factor1, vi) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, vi) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, vi) + p->Offset3;
				output.q = m4_mulfix(p->Factor4, vi) + p->Offset4;
			}
				break;
			case FT_MFFloat:
			{
				Fixed sff = ((MFFloat *)inMFField)->vals[i];
				output.x = m4_mulfix(p->Factor1, sff) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, sff) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, sff) + p->Offset3;
				output.q = m4_mulfix(p->Factor4, sff) + p->Offset4;
			}
				break;
			case FT_MFColor:
			{
				SFColor sfc = ((MFColor *)inMFField)->vals[i];
				output.x = m4_mulfix(p->Factor1, sfc.red) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, sfc.green) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, sfc.blue) + p->Offset3;
				output.q = p->Offset4;
				num_out = 3;
			}
				break;
			case FT_MFVec2f:
			{
				SFVec2f sfv = ((MFVec2f *)inMFField)->vals[i];
				output.x = m4_mulfix(p->Factor1, sfv.x) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, sfv.y) + p->Offset2;
				output.z = p->Offset3;
				output.q = p->Offset4;
				num_out = 2;
			}
				break;
			case FT_MFVec3f:
			{
				SFVec3f sfv = ((MFVec3f *)inMFField)->vals[i];
				output.x = m4_mulfix(p->Factor1, sfv.x) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, sfv.y) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, sfv.z) + p->Offset3;
				output.q = p->Offset4;
				num_out = 3;
			}
				break;
			case FT_MFVec4f:
			case FT_MFRotation:
			{
				SFVec4f sfv = ((MFVec4f *)inMFField)->vals[i];
				output.x = m4_mulfix(p->Factor1, sfv.x) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, sfv.y) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, sfv.z) + p->Offset3;
				output.q = m4_mulfix(p->Factor4, sfv.q) + p->Offset4;
				num_out = 4;
			}
				break;
			case FT_MFString:
				/*cf below*/
				output.x = output.y = output.z = output.q = 0;
				if (((MFString *)inMFField)->vals[i]) {
					if (!stricmp(((MFString *)inMFField)->vals[i], "true")) {
						output.x = output.y = output.z = output.q = FIX_ONE;
					} else if (!strstr(((MFString *)inMFField)->vals[i], ".")) {
						output.x = INT2FIX( atoi(((MFString *)inMFField)->vals[i]) );
						output.y = output.z = output.q = output.x;
					} else {
						output.x = FLT2FIX( atof(((MFString *)inMFField)->vals[i]) );
						output.y = output.z = output.q = output.x;
					}
				}

				output.x = m4_mulfix(p->Factor1, output.x) + p->Offset1;
				output.y = m4_mulfix(p->Factor2, output.y) + p->Offset2;
				output.z = m4_mulfix(p->Factor3, output.z) + p->Offset3;
				output.q = m4_mulfix(p->Factor4, output.q) + p->Offset4;
				break;

			}
			if (p->Sum) {
				output.x = output.x + output.y + output.z + output.q;
				output.y = output.z = output.q = output.x;
			}
		}
		
		p->outMFFloat.vals[i] = output.x;
		p->outMFInt32.vals[i] = FIX2INT(output.x);
		p->outMFColor.vals[i].red = output.x;
		p->outMFColor.vals[i].green = output.y;
		p->outMFColor.vals[i].blue = output.z;
		p->outMFVec2f.vals[i].x = output.x;
		p->outMFVec2f.vals[i].y = output.y;
		p->outMFVec3f.vals[i].x = output.x;
		p->outMFVec3f.vals[i].y = output.y;
		p->outMFVec3f.vals[i].z = output.z;
		p->outMFRotation.vals[i].x = output.x;
		p->outMFRotation.vals[i].y = output.y;
		p->outMFRotation.vals[i].z = output.z;
		p->outMFRotation.vals[i].q = output.q;

		if (num_out==1) {
			if (inType==FT_SFTime) {
				format_sftime_string(output.x, str);
			} else {
				sprintf(str, "%.6f", FIX2FLT(output.x));
			}
		} else if (num_out==2) {
			sprintf(str, "%.4f %.4f", FIX2FLT(output.x), FIX2FLT(output.y));
		} else if (num_out==3) {
			sprintf(str, "%.3f %.3f %.3f", FIX2FLT(output.x), FIX2FLT(output.y), FIX2FLT(output.z));
		} else if (num_out==4) {
			sprintf(str, "%.2f %.2f %.2f %.2f", FIX2FLT(output.x), FIX2FLT(output.y), FIX2FLT(output.z), FIX2FLT(output.q));
		}

		if (p->outMFString.vals[i]) free(p->outMFString.vals[i]);
		p->outMFString.vals[i] = strdup(str);
		
		if (!i) sf_out = output;

	}

	p->outSFBool = (Bool) (sf_out.x ? 1 : 0);
	p->outSFFloat = sf_out.x;
	p->outSFInt32 = FIX2INT(sf_out.x);
	p->outSFTime = (SFTime) FIX2FLT(sf_out.x);
	p->outSFRotation.x = sf_out.x;
	p->outSFRotation.y = sf_out.y;
	p->outSFRotation.z = sf_out.z;
	p->outSFRotation.q = sf_out.q;
	p->outSFColor.red = sf_out.x;
	p->outSFColor.green = sf_out.y;
	p->outSFColor.blue = sf_out.z;
	p->outSFVec2f.x = sf_out.x;
	p->outSFVec2f.y = sf_out.y;
	p->outSFVec3f.x = sf_out.x;
	p->outSFVec3f.y = sf_out.y;
	p->outSFVec3f.z = sf_out.z;

	if (num_out==1) {
		if (inType==FT_SFTime) {
			format_sftime_string(output.x, str);
		} else {
			sprintf(str, "%.6f", FIX2FLT(sf_out.x));
		}
	} else if (num_out==2) {
		sprintf(str, "%.4f %.4f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y));
	} else if (num_out==3) {
		sprintf(str, "%.3f %.3f %.3f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y), FIX2FLT(sf_out.z));
	} else if (num_out==4) {
		sprintf(str, "%.2f %.2f %.2f %.2f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y), FIX2FLT(sf_out.z), FIX2FLT(sf_out.q));
	}
	if (p->outSFString.buffer ) free(p->outSFString.buffer);
	p->outSFString.buffer = strdup(str);

	/*valuator is a special case, all routes are triggered*/
	for (i=0; i<ChainGetCount(p->sgprivate->routes); i++) {
		r = ChainGetEntry(p->sgprivate->routes, i);
		if (r->FromNode != (SFNode *)p) continue;

		if (r->IS_route) {
			ActivateRoute(r);
		} else {
			QueueRoute(p->sgprivate->scenegraph, r);
		}
	}
}


/*
valuator spec (9.4.2.116.2)
"In the special case of a scalar input type (e.g. SFBool, SFInt32) that is cast to a vectorial output type (e.g.
SFVec2f), for all components i of output.i, input.i shall take the value of the scalar input type, after appropriate type
conversion"
*/

void Valuator_SetInSFBool(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = val.y = val.z = val.q = _this->inSFBool ? FIX_ONE : 0;
	SetValuatorOutput(_this, &val, NULL, FT_SFBool);
}
void Valuator_SetInSFFloat(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = val.y = val.z = val.q = _this->inSFFloat;
	SetValuatorOutput(_this, &val, NULL, FT_SFFloat);
}
void Valuator_SetInSFInt32(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = val.y = val.z = val.q = INT2FIX(_this->inSFInt32);
	SetValuatorOutput(_this, &val, NULL, FT_SFInt32);
}
void Valuator_SetInSFTime(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = val.y = val.z = val.q = FLT2FIX(_this->inSFTime);
	SetValuatorOutput(_this, &val, NULL, FT_SFTime);
}
void Valuator_SetInSFColor(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = _this->inSFColor.red;
	val.y = _this->inSFColor.green;
	val.z = _this->inSFColor.blue;
	val.q = 0;
	SetValuatorOutput(_this, &val, NULL, FT_SFColor);
}
void Valuator_SetInSFVec2f(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = _this->inSFVec2f.x;
	val.y = _this->inSFVec2f.y;
	val.z = val.q = 0;
	SetValuatorOutput(_this, &val, NULL, FT_SFVec2f);
}
void Valuator_SetInSFVec3f(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = _this->inSFVec3f.x;
	val.y = _this->inSFVec3f.y;
	val.z = _this->inSFVec3f.z;
	val.q = 0;
	SetValuatorOutput(_this, &val, NULL, FT_SFVec3f);
}
void Valuator_SetInSFRotation(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val = _this->inSFRotation;
	SetValuatorOutput(_this, &val, NULL, FT_SFRotation);
}

/*
valuator spec (9.4.2.116.2)
Convert if the content of the string represents an int, float or
double value. ‘Boolean’ string values 'true' and 'false' are
converted to 1.0 and 0.0 respectively. Any other string is converted to 0.0
*/
void Valuator_SetInSFString(SFNode *n)
{
	SFVec4f val;
	M_Valuator *_this = (M_Valuator *) n;
	val.x = val.y = val.z = val.q = 0;
	if (! _this->inSFString.buffer) return;
	if (!stricmp(_this->inSFString.buffer, "true")) {
		val.x = val.y = val.z = val.q = FIX_ONE;
	} else if (!strstr(_this->inSFString.buffer, ".")) {
		val.x = INT2FIX( atoi(_this->inSFString.buffer) );
		val.y = val.z = val.q = val.x;
	} else {
		val.x = FLT2FIX( atof(_this->inSFString.buffer) );
		val.y = val.z = val.q = val.x;
	}
	SetValuatorOutput(_this, &val, NULL, FT_SFString);
}

void Valuator_SetInMFColor(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFColor, FT_MFColor);
}

void Valuator_SetInMFFloat(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFFloat, FT_MFFloat);
}
void Valuator_SetInMFInt32(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFInt32, FT_MFInt32);
} 
void Valuator_SetInMFVec2f(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFVec2f, FT_MFVec2f);
}
void Valuator_SetInMFVec3f(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFVec3f, FT_MFVec3f);
}
void Valuator_SetInMFRotation(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFRotation, FT_MFRotation);
}
void Valuator_SetInMFString(SFNode *n)
{
	M_Valuator *_this = (M_Valuator *) n;
	SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFString, FT_MFString);
}
