/*
 * $Id$
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2005 Blender Foundation.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file blender/nodes/intern/SHD_util.c
 *  \ingroup nodes
 */


#include "SHD_util.h"





/* ****** */

void nodestack_get_vec(float *in, short type_in, bNodeStack *ns)
{
	float *from= ns->vec;
		
	if(type_in==SOCK_VALUE) {
		if(ns->sockettype==SOCK_VALUE)
			*in= *from;
		else 
			*in= 0.333333f*(from[0]+from[1]+from[2]);
	}
	else if(type_in==SOCK_VECTOR) {
		if(ns->sockettype==SOCK_VALUE) {
			in[0]= from[0];
			in[1]= from[0];
			in[2]= from[0];
		}
		else {
			VECCOPY(in, from);
		}
	}
	else { /* type_in==SOCK_RGBA */
		if(ns->sockettype==SOCK_RGBA) {
			QUATCOPY(in, from);
		}
		else if(ns->sockettype==SOCK_VALUE) {
			in[0]= from[0];
			in[1]= from[0];
			in[2]= from[0];
			in[3]= 1.0f;
		}
		else {
			VECCOPY(in, from);
			in[3]= 1.0f;
		}
	}
}


/* ******************* execute and parse ************ */

void ntreeShaderExecTree(bNodeTree *ntree, ShadeInput *shi, ShadeResult *shr)
{
	ShaderCallData scd;
	float co[3] = {0.0f, 0.0f, 0.0f};
	/*
	 @note: preserve material from ShadeInput for material id, nodetree execs change it
	 fix for bug "[#28012] Mat ID messy with shader nodes"
	 */
	Material *mat = shi->mat;

	/* convert caller data to struct */
	memset(&scd, 0, sizeof(scd));
	scd.shi= shi;
	scd.shr= shr;
	scd.co= co;
	
	/* each material node has own local shaderesult, with optional copying */
	memset(shr, 0, sizeof(ShadeResult));
		   
	ntreeExecTree(ntree, &scd, shi->thread);	/* threads */
	// @note: set material back to preserved material
	shi->mat = mat;
	/* better not allow negative for now */
	if(shr->combined[0]<0.0f) shr->combined[0]= 0.0f;
	if(shr->combined[1]<0.0f) shr->combined[1]= 0.0f;
	if(shr->combined[2]<0.0f) shr->combined[2]= 0.0f;
	
}

/* go over all used Geometry and Texture nodes, and return a texco flag */
/* no group inside needed, this function is called for groups too */
void ntreeShaderGetTexcoMode(bNodeTree *ntree, int r_mode, short *texco, int *mode)
{
	bNode *node;
	bNodeSocket *sock;
	int a;
	
	ntreeSocketUseFlags(ntree);

	for(node= ntree->nodes.first; node; node= node->next) {
		if(node->type==SH_NODE_TEXTURE) {
			if((r_mode & R_OSA) && node->id) {
				Tex *tex= (Tex *)node->id;
				if ELEM3(tex->type, TEX_IMAGE, TEX_PLUGIN, TEX_ENVMAP) 
					*texco |= TEXCO_OSA|NEED_UV;
			}
			/* usability exception... without input we still give the node orcos */
			sock= node->inputs.first;
			if(sock==NULL || sock->link==NULL)
				*texco |= TEXCO_ORCO|NEED_UV;
		}
		else if(node->type==SH_NODE_GEOMETRY) {
			/* note; sockets always exist for the given type! */
			for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) {
				if(sock->flag & SOCK_IN_USE) {
					switch(a) {
						case GEOM_OUT_GLOB: 
							*texco |= TEXCO_GLOB|NEED_UV; break;
						case GEOM_OUT_VIEW: 
							*texco |= TEXCO_VIEW|NEED_UV; break;
						case GEOM_OUT_ORCO: 
							*texco |= TEXCO_ORCO|NEED_UV; break;
						case GEOM_OUT_UV: 
							*texco |= TEXCO_UV|NEED_UV; break;
						case GEOM_OUT_NORMAL: 
							*texco |= TEXCO_NORM|NEED_UV; break;
						case GEOM_OUT_VCOL:
							*texco |= NEED_UV; *mode |= MA_VERTEXCOL; break;
					}
				}
			}
		}
	}
}

/* nodes that use ID data get synced with local data */
void nodeShaderSynchronizeID(bNode *node, int copyto)
{
	if(node->id==NULL) return;
	
	if(ELEM(node->type, SH_NODE_MATERIAL, SH_NODE_MATERIAL_EXT)) {
		bNodeSocket *sock;
		Material *ma= (Material *)node->id;
		int a;
		
		/* hrmf, case in loop isnt super fast, but we dont edit 100s of material at same time either! */
		for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) {
			if(!(sock->flag & SOCK_HIDDEN)) {
				if(copyto) {
					switch(a) {
						case MAT_IN_COLOR:
							VECCOPY(&ma->r, sock->ns.vec); break;
						case MAT_IN_SPEC:
							VECCOPY(&ma->specr, sock->ns.vec); break;
						case MAT_IN_REFL:
							ma->ref= sock->ns.vec[0]; break;
						case MAT_IN_MIR:
							VECCOPY(&ma->mirr, sock->ns.vec); break;
						case MAT_IN_AMB:
							ma->amb= sock->ns.vec[0]; break;
						case MAT_IN_EMIT:
							ma->emit= sock->ns.vec[0]; break;
						case MAT_IN_SPECTRA:
							ma->spectra= sock->ns.vec[0]; break;
						case MAT_IN_RAY_MIRROR:
							ma->ray_mirror= sock->ns.vec[0]; break;
						case MAT_IN_ALPHA:
							ma->alpha= sock->ns.vec[0]; break;
						case MAT_IN_TRANSLUCENCY:
							ma->translucency= sock->ns.vec[0]; break;
					}
				}
				else {
					switch(a) {
						case MAT_IN_COLOR:
							VECCOPY(sock->ns.vec, &ma->r); break;
						case MAT_IN_SPEC:
							VECCOPY(sock->ns.vec, &ma->specr); break;
						case MAT_IN_REFL:
							sock->ns.vec[0]= ma->ref; break;
						case MAT_IN_MIR:
							VECCOPY(sock->ns.vec, &ma->mirr); break;
						case MAT_IN_AMB:
							sock->ns.vec[0]= ma->amb; break;
						case MAT_IN_EMIT:
							sock->ns.vec[0]= ma->emit; break;
						case MAT_IN_SPECTRA:
							sock->ns.vec[0]= ma->spectra; break;
						case MAT_IN_RAY_MIRROR:
							sock->ns.vec[0]= ma->ray_mirror; break;
						case MAT_IN_ALPHA:
							sock->ns.vec[0]= ma->alpha; break;
						case MAT_IN_TRANSLUCENCY:
							sock->ns.vec[0]= ma->translucency; break;
					}
				}
			}
		}
	}
	
}
