/*
 * $Id$
 */

/*
 * 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.
 * 
 * Other licensing options are available on request; please
 * contact the author for more information.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2007 Sarah Kelley
 * All rights reserved.
 *
 */

/*
 *  Utility functions used throughout the sarahtexture suite.
 *
 *  Ideally these would live in one or more .c files, however,
 *  the Blender plugin build framework currently assumes that
 *  each plugin directory will have one .c file, and create
 *  one plugin, which makes it a little more challenging to share
 *  boilerplate code between plugins. I decided against trying
 *  to tweak the build system as that might break all sorts
 *  of things on other platforms.
 *
 *  And *that's* why this header file is full of C code, okay? :-)
 */



/*
 *  Copy local results into global result.
 *  (This is part of an attempt to deal with thread safety problems.)
 */
void set_final_result(float *final_result,Result *r) {
	final_result[RESULT_INTENSITY]=r->intensity;
	final_result[RESULT_RED]=r->red;
	final_result[RESULT_GREEN]=r->green;
	final_result[RESULT_BLUE]=r->blue;
	final_result[RESULT_ALPHA]=r->alpha;
	final_result[RESULT_XNORMAL]=r->xnormal;
	final_result[RESULT_YNORMAL]=r->ynormal;
	final_result[RESULT_ZNORMAL]=r->znormal;
}


/*
 *  Some vector functions I swiped from one of the
 *  plugins in the plugin suite.
 */
void vec_copy(float *va, float *vb)
{
        va[0] = vb[0];
        va[1] = vb[1];
        va[2] = vb[2];
}
float vec_len(float *v)
{
        return fsqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
void vec_add(float *v,float *va, float *vb)
{
        v[0] = va[0] + vb[0];
        v[1] = va[1] + vb[1];
        v[2] = va[2] + vb[2];
}
void vec_sub(float *v,float *va, float *vb)
{
        v[0] = va[0] - vb[0];
        v[1] = va[1] - vb[1];
        v[2] = va[2] - vb[2];
}

void crossp(float *v,float *va, float *vb)
{
        v[0] = va[1] * vb[2] - va[2] * vb[1];
        v[1] = va[2] * vb[0] - va[0] * vb[2];
        v[2] = va[0] * vb[1] - va[1] * vb[0];
}

void vec_normalize(float *v)
{
        float li;
        double l = vec_len(v);
        if (l < 0.0000000001)
                l = 0.0000000001;
        li = 1.0 / l;
        v[0] *= li;
        v[1] *= li;
        v[2] *= li;
}




/* Experimental extended scaling.
 * If the slider is in the range 0..0.5, we multiply by twice that,
 * which means we're multiplying by a value between 0 and 1, which
 * has the effect of scaling downward (making noise smaller).
 *
 * If the slider is beyond 0.5, we scale the remaining half of
 * the range to be a value of 1 to 1000 (making noise bigger).
 *
 * The intended effect is that we can both shrink and expand with
 * just a single slider.
 */
float extended_scaler(float value_to_scale,float scale_value) {
	if (scale_value<=0.5) {
		return value_to_scale*scale_value*2;
	} else {
		return value_to_scale*((scale_value-0.5)*2000.0);
	}
}



/*
 *  Calculate and return the intensity of the pixel at texvec.
 *  If dxt and dyt are non-null, they are used to return an
 *  anti-aliased value. The algorithm used to compute the
 *  intensity of the pixel is passed as a pointer to a
 *  function calc, which takes as parameters a pointer to the
 *  cast data, x,y,and z, and returns a float containing intensity.
 */
float calculate_intensity(float (*calc)(Cast *,float,float,float,Extra *),
		Cast *c,float *texvec,float *dxt,float *dyt,Extra *extra) {
	float x,y,z,intensity,offx,offy,offz;
	
	x=texvec[0]; y=texvec[1]; z=texvec[2];

	/* Compute intensity for pixel at x,y,z */
	intensity=(*calc)(c,x,y,z,extra);

	/* If dxt and dyt are provided, we need to anti-alias.
	 *  This code probably isn't correct yet...
	 */
	if ((dxt!=NULL)&&(dyt!=NULL)) {
		offx=fabs(dxt[0]-dyt[0])/3.0;
		offy=fabs(dxt[1]-dyt[1])/3.0;
		offz=fabs(dxt[2]-dyt[2])/3.0;
		
		intensity+=(*calc)(c,x-offx,y-offy,z-offz,extra);
		intensity+=(*calc)(c,x-offx,y+offy,z-offz,extra);
		intensity+=(*calc)(c,x+offx,y-offy,z-offz,extra);
		intensity+=(*calc)(c,x+offx,y+offy,z-offz,extra);
		
		intensity+=(*calc)(c,x-offx,y-offy,z+offz,extra);
		intensity+=(*calc)(c,x-offx,y+offy,z+offz,extra);
		intensity+=(*calc)(c,x+offx,y-offy,z+offz,extra);
		intensity+=(*calc)(c,x+offx,y+offy,z+offz,extra);

		/* Averate of original pixel plus extra samples. */
		intensity=intensity/9.0;
		
	}
	return intensity;
	
}



/**
 *  Attempt to calculate bump mapping for the texture at this point.
 *  This gives cosmetically reasonable results, but I'm not
 *  entirely sure I know what I'm doing here.
 */
float calculate_bumpmap(float (*calc)(Cast *,float,float,float,Extra *),
		Cast *c,float *texvec,float *dxt,float *dyt,Extra *extra,
		float nabla,float scale,Result *result) {
	float tmpvec[3];
	float ofs;
	
	/* If nabla is set to 0, don't do bump mapping.
	 * This is slightly faster, if you're not going to use bump mapping...
	 */
	if (nabla<0.001) {
		return 0;
	}
	
	ofs=nabla/scale;
	
	tmpvec[0]=texvec[0]+ofs; tmpvec[1]=texvec[1]; tmpvec[2]=texvec[2];
	result->xnormal=calculate_intensity(calc,c,tmpvec,dxt,dyt,extra)
		- result->intensity;

	tmpvec[0]=texvec[0]; tmpvec[1]=texvec[1]+ofs; tmpvec[2]=texvec[2];
	result->ynormal=calculate_intensity(calc,c,tmpvec,dxt,dyt,extra)
		- result->intensity;

	tmpvec[0]=texvec[0]; tmpvec[1]=texvec[1]; tmpvec[2]=texvec[2]+ofs;
	result->znormal=calculate_intensity(calc,c,tmpvec,dxt,dyt,extra)
		- result->intensity;
		
	return TEX_NOR;
}



/*
 *  Write a log file showing texvec,dxt,dyt into /tmp.
 *  Only logs when dxt and/or dyt are non-null
 *  (i.e. anti-aliased requests)
 *
 *  Plugins do NOT need to do this; I'm just trying to understand
 *  how the dxt and dyt parameters interact.
 *
 *  Enabling this will make rendering go VERY slowly.
 */
void debug_log_inputs(float *texvec, float *dxt, float *dyt) 
{
	FILE *f;
	char filename[80]="";
	char format[] = "%s = ( %6.4f %6.4f %6.4f) \n";
	
	/* only log anti-aliased stuff */
	if ((dxt==NULL)&&(dyt==NULL)) return;
	
	/* make a filename in /tmp based on the name of the texture */
	strncat(filename,"/tmp/",sizeof(filename)-strlen(filename)-1);
	strncat(filename,TEXTURE_NAME,sizeof(filename)-strlen(filename)-1);
	strncat(filename,".debug",sizeof(filename)-strlen(filename)-1);
	
	f=fopen(filename,"a");
	if (f==NULL) return;
	
	fprintf(f,format,"texvec",texvec[0],texvec[1],texvec[2]);
	
	if (dxt!=NULL) {
		fprintf(f,format,"dxt   ",dxt[0],dxt[1],dxt[2]);
	} else {
		fprintf (f,"dxt   = null\n");
	}
	
	if (dyt!=NULL) {
		fprintf(f,format,"dyt   ",dyt[0],dyt[1],dyt[2]);
	} else {
		fprintf (f,"dyt   = null\n");
	}
	
	fprintf(f,"\n");
	fflush(f);
	fclose(f);
}


/**
 *  To be used for a pthread mutex lock should the need arise.
 */
void begin_doit() {
}



/**
 *  To be used for a pthread mutex lock should the need arise.
 */
void end_doit() {
}

