/**
* $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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Contributors: Matt Ebb
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include <stdio.h>
#include <math.h>
#include <string.h>

#include "BLI_arithb.h"

#include "IMB_imbuf_types.h"

#include "DNA_image_types.h"
#include "DNA_texture_types.h"
#include "DNA_world_types.h"

#include "BKE_image.h"
#include "BKE_global.h"

/* local include */
#include "render_types.h"

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
/* only to be used here in this file, it's for speed */
extern struct Render R;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

float sinc(float x) {
	if (fabs(x) < 1.0e-4) return 1.0 ;
	else return(sin(x)/x) ;
}
 
/* *** Diffuse irradiance: Spherical Harmonics  *** */
/*
* Based on the paper and sample code of:
* "An Efficient Representation for Irradiance Environment Maps"
* by Ravi Ramamoorthi and Pat Hanrahan
* http://graphics.stanford.edu/papers/envmap/ 
*
* Also thanks to some example code by Francesco Banterle
* http://www.banterle.com/francesco/
*
* This calculates RGB values for lighting coefficients L_{lm} with 
* 0 <= l <= 2 and -l <= m <= l.  There are 9 coefficients in all.
*/

float dsh_coeffs[9][3];

float			IBL_l_R[9];
float			IBL_l_G[9];
float			IBL_l_B[9];



void StampaCoefficienti(void)
{
	printf("\n");
	printf("I coefficienti delle armominche sferiche:\n");

	//L0 0
	printf("L0  0: R=%f G=%f B=%f \n", IBL_l_R[0], IBL_l_G[0], IBL_l_B[0]);
	//L0 -1
	printf("L1 -1: R=%f G=%f B=%f \n", IBL_l_R[1], IBL_l_G[1], IBL_l_B[1]);
	//L1 0
	printf("L1  0: R=%f G=%f B=%f \n", IBL_l_R[2], IBL_l_G[2], IBL_l_B[2]);
	//L1 1
	printf("L1  1: R=%f G=%f B=%f \n", IBL_l_R[3], IBL_l_G[3], IBL_l_B[3]);
	//L2 -2
	printf("L2 -2: R=%f G=%f B=%f \n", IBL_l_R[4], IBL_l_G[4], IBL_l_B[4]);
	//L2 -1
	printf("L2 -1: R=%f G=%f B=%f \n", IBL_l_R[5], IBL_l_G[5], IBL_l_B[5]);
	//L2 0
	printf("L2  0: R=%f G=%f B=%f \n", IBL_l_R[6], IBL_l_G[6], IBL_l_B[6]);
	//L2 1
	printf("L2  1: R=%f G=%f B=%f \n", IBL_l_R[7], IBL_l_G[7], IBL_l_B[7]);
	//L2 2
	printf("L2  2: R=%f G=%f B=%f \n", IBL_l_R[8], IBL_l_G[8], IBL_l_B[8]);

	printf("\n");
};

void ibl_diffusesh_prefilter(struct Render *re)
{
	float x,y,z;
	float theta,phi,u,v,radius;
	float c;

	float iR,iG,iB;
	int index;
	float dtheta_dphi, sinctheta;
	int w, h, i, j, k;
	float half_h,half_w;

	ImBuf *ibuf;
	Image *ima;
	float *rect_float;
	float pix[3];
	int col;

	ima = re->wrld.ibl_image;
		
	if (!ima) return;
	
	ibuf = BKE_image_get_ibuf(ima, NULL);
	
	w = ibuf->x;
	h = ibuf->y;
	if (w != h) {
		printf("only square angmaps supported for now\n");
		return;
	}
	
	rect_float = ibuf->rect_float;

	half_h=(float)h*0.5f;
	half_w=(float)w*0.5f;

	dtheta_dphi= (M_PI/(float)h) * (M_PI_2/(float)w);

	/* todo: replace with memset: 0, for dynamic coeffs mem */
	for(k=0;k<9;k++)
	{
		re->wrld.dsh_coeffs[k][0] = 0.0f;
		re->wrld.dsh_coeffs[k][1] = 0.0f;
		re->wrld.dsh_coeffs[k][2] = 0.0f;
	}

	for(i=0;i<w;i++)
	{
		/* coordinate in image space, in [-1, 1] */
		u= (half_w - (float)i) / half_w;

		for(j=0;j<h;j++)
		{
			/* coordinate in image space, in [-1, 1] */
			v= ((float)j - half_h)/half_h;

			/* for angmap, only consider points inside the unit circle */
			radius = sqrtf(u*u + v*v);
			if(radius>1.0f) continue;

			theta = M_PI*radius;
			phi = atan2(v,u);

			/* transform u,v to cartesian components */
			x = sin(theta)*cos(phi);
			y = sin(theta)*sin(phi);
			z = cos(theta);
			sinctheta=sinc(theta);

			index = i*h+j;
			pix[0] = rect_float[4 * index + 0];
			pix[1] = rect_float[4 * index + 1];
			pix[2] = rect_float[4 * index + 2];

			/* now we add the light per pixel to the coefficients per term,
			 * based on a constant, the differential solid angle and 
			 * cartesian components of surface normal x,y,z */			
			for (col = 0 ; col < 3 ; col++) {
			
				/* L_{00}.  Note that Y_{00} = 0.282095 */
				c=0.282095f*sinctheta;
				re->wrld.dsh_coeffs[0][col] += pix[col]*c;
				
				/* L_{1m}. -1 <= m <= 1.  The linear terms */
				c=0.488603f*sinctheta;
				re->wrld.dsh_coeffs[1][col]+=pix[col]*c*y;
				re->wrld.dsh_coeffs[2][col]+=pix[col]*c*z;
				re->wrld.dsh_coeffs[3][col]+=pix[col]*c*x;
			
				/* The Quadratic terms, L_{2m} -2 <= m <= 2 */
				/* First, L_{2-2}, L_{2-1}, L_{21} corresponding to xy,yz,xz */
				c=1.092548f*sinctheta;
				re->wrld.dsh_coeffs[4][col]+=pix[col]*c*x*y;		
				re->wrld.dsh_coeffs[5][col]+=pix[col]*c*y*z;
				re->wrld.dsh_coeffs[7][col]+=pix[col]*c*x*z;
			
				/* L_{20}.  Note that Y_{20} = 0.315392 (3z^2 - 1) */
				c=0.315392f*sinctheta*(3.0f*z*z-1.0f);
				re->wrld.dsh_coeffs[6][col]+=pix[col]*c;
				
				c=0.546274f*sinctheta*(x*x-y*y);
				re->wrld.dsh_coeffs[8][col]+=pix[col]*c;
			}
		}
    }

	for(i=0;i<9;i++)
	{
		re->wrld.dsh_coeffs[i][0] *= dtheta_dphi;
		re->wrld.dsh_coeffs[i][1] *= dtheta_dphi;
		re->wrld.dsh_coeffs[i][2] *= dtheta_dphi;
	}

	StampaCoefficienti();
};


void dsh_irradcoeffs ( float *col,
      float *L00, float *L1_1, float *L10, float *L11, 
      float *L2_2, float *L2_1, float *L20, float *L21, float *L22,
      float *n) 
{
  //------------------------------------------------------------------
  // These are variables to hold x,y,z and squares and products

	float x2 ;
	float  y2 ;
	float z2 ;
	float xy ;
	float  yz ;
	float  xz ;
	float x ;
	float y ;
	float z ;
  //------------------------------------------------------------------       
  // We now define the constants and assign values to x,y, and z 
	
	const float c1 = 0.429043 ;
	const float c2 = 0.511664 ;
	const float c3 = 0.743125 ;
	const float c4 = 0.886227 ;
	const float c5 = 0.247708 ;
	
    float a[3], b[3], c[3], d[3], e[3], f[3], g[3], h[3], i[3], j[3];
	float efg[3], hij[3];
	float mycol[3];

	/* previous line was: x = n[0] ; y = n[1] ; z = n[2] ; - now changed for blender coords (Z up) */
	x = -n[2] ; y = n[0] ; z = n[1] ;
  //------------------------------------------------------------------ 
  // We now compute the squares and products needed 

	x2 = x*x ; y2 = y*y ; z2 = z*z ;
	xy = x*y ; yz = y*z ; xz = x*z ;
  //------------------------------------------------------------------ 
  // Finally, we compute equation 13
  
 
 // evil construct, but tests that it's working for now
	VECCOPY(a, L22);
	VecMulf(a, c1*(x2-y2));
	
	VECCOPY(b, L20);
	VecMulf(b, c3*(z2));
	
	VECCOPY(c, L00);
	VecMulf(c, c4);
	
	VECCOPY(d, L20);
	VecMulf(d, c5);
	
	VECCOPY(e, L2_2);
	VecMulf(e, xy);
	
	VECCOPY(f, L21);
	VecMulf(f, xz);
	
	VECCOPY(g, L2_1);
	VecMulf(g, yz);
	
	VecAddf(efg, e, f);
	VecAddf(efg, efg, g);
	VecMulf(efg, 2*c1);
	
	VECCOPY(h, L11);
	VecMulf(h, x);
	
	VECCOPY(i, L1_1);
	VecMulf(i, y);
	
	VECCOPY(j, L10)
	VecMulf(j, z);
	
	VecAddf(hij, h, i);
	VecAddf(hij, hij, j);
	VecMulf(hij, 2*c2);
	
	VecAddf(mycol, a, b);
	VecAddf(mycol, mycol, c);
	VecSubf(mycol, mycol, d);
	VecAddf(mycol, mycol, efg);
	VecAddf(mycol, mycol, hij);
  
	VECCOPY(col, mycol);

/*	col = c1*L22*(x2-y2) + c3*L20*z2 + c4*L00 - c5*L20 
            + 2*c1*(L2_2*xy + L21*xz + L2_1*yz) 
            + 2*c2*(L11*x+L1_1*y+L10*z) ;
*/ 
}


void image_based_lighting(ShadeInput *shi, ShadeResult *shr)
{
	float iblcol[3];
	float nor[3];
	
	VECCOPY(nor, shi->worldnor);

	dsh_irradcoeffs(iblcol, R.wrld.dsh_coeffs[0], R.wrld.dsh_coeffs[1], R.wrld.dsh_coeffs[2], R.wrld.dsh_coeffs[3], 
				R.wrld.dsh_coeffs[4], R.wrld.dsh_coeffs[5], R.wrld.dsh_coeffs[6], R.wrld.dsh_coeffs[7], R.wrld.dsh_coeffs[8], nor);
	
	VecMulf(iblcol, R.wrld.ibl_multiplier);
	
	VecAddf(shr->diff, shr->diff, iblcol);
	VecAddf(shr->shad, shr->shad, iblcol);
}

