/* -------------------------------------------------------------------------------

   Spherical Harmonics (L2) for light grid

   9 coefficients per color channel, encoding a smooth directional lighting
   function over the sphere. This is the standard real-valued SH basis up to
   order 2 (l=0,1,2), widely used for irradiance environment maps.

   Basis functions (Cartesian, real-valued, orthonormal):
     Y0  = 0.282095                          (l=0, m=0)  -- constant / DC
     Y1  = 0.488603 * y                      (l=1, m=-1) -- linear
     Y2  = 0.488603 * z                      (l=1, m=0)
     Y3  = 0.488603 * x                      (l=1, m=1)
     Y4  = 1.092548 * x*y                    (l=2, m=-2) -- quadratic
     Y5  = 1.092548 * y*z                    (l=2, m=-1)
     Y6  = 0.315392 * (3*z*z - 1)            (l=2, m=0)
     Y7  = 1.092548 * x*z                    (l=2, m=1)
     Y8  = 0.546274 * (x*x - y*y)            (l=2, m=2)

   References:
     - "Stupid Spherical Harmonics (SH) Tricks", Peter-Pike Sloan
     - "An Efficient Representation for Irradiance Environment Maps",
       Ramamoorthi & Hanrahan, SIGGRAPH 2001

   ------------------------------------------------------------------------------- */

#pragma once

#include "math/vector.h"
#include <cmath>


/* number of L2 SH coefficients */
static constexpr int SH_NUM_COEFFS = 9;

/* SH basis function constants */
static constexpr float SH_C0 = 0.282094791773878f;   /* 1 / (2*sqrt(pi))           */
static constexpr float SH_C1 = 0.488602511902920f;   /* sqrt(3) / (2*sqrt(pi))     */
static constexpr float SH_C2 = 1.092548430592079f;   /* sqrt(15) / (2*sqrt(pi))    */
static constexpr float SH_C3 = 0.315391565252520f;   /* sqrt(5) / (4*sqrt(pi))     */
static constexpr float SH_C4 = 0.546274215296040f;   /* sqrt(15) / (4*sqrt(pi))    */


/*
   SH L2 coefficients for a single color channel (RGB stored as 3 of these),
   or for all 3 channels interleaved as Vector3.
*/
struct SHCoeffs3
{
	Vector3 c[SH_NUM_COEFFS];

	SHCoeffs3() {
		for ( int i = 0; i < SH_NUM_COEFFS; ++i )
			c[i].set( 0 );
	}
};


/*
   SH_evaluate()
   Evaluate the 9 L2 SH basis functions for a given direction.
   Direction must be normalized.
*/
inline void SH_evaluate( const Vector3& dir, float basis[SH_NUM_COEFFS] ){
	const float x = dir.x();
	const float y = dir.y();
	const float z = dir.z();

	/* l=0 */
	basis[0] = SH_C0;

	/* l=1 */
	basis[1] = SH_C1 * y;
	basis[2] = SH_C1 * z;
	basis[3] = SH_C1 * x;

	/* l=2 */
	basis[4] = SH_C2 * x * y;
	basis[5] = SH_C2 * y * z;
	basis[6] = SH_C3 * ( 3.0f * z * z - 1.0f );
	basis[7] = SH_C2 * x * z;
	basis[8] = SH_C4 * ( x * x - y * y );
}


/*
   SH_project_sample()
   Given a directional light sample (direction + RGB color), accumulate
   its contribution into the SH coefficients.

   This projects: color * SH_basis(direction) for each basis function.
   The direction must be normalized.
*/
inline void SH_project_sample( SHCoeffs3& sh, const Vector3& dir, const Vector3& color ){
	float basis[SH_NUM_COEFFS];
	SH_evaluate( dir, basis );

	for ( int i = 0; i < SH_NUM_COEFFS; ++i )
		sh.c[i] += color * basis[i];
}


/*
   SH_reconstruct()
   Reconstruct the lighting color for a given direction from SH coefficients.
   Direction must be normalized.
   Returns the RGB color (may contain negative values from ringing -- caller should clamp).
*/
inline Vector3 SH_reconstruct( const SHCoeffs3& sh, const Vector3& dir ){
	float basis[SH_NUM_COEFFS];
	SH_evaluate( dir, basis );

	Vector3 result( 0 );
	for ( int i = 0; i < SH_NUM_COEFFS; ++i )
		result += sh.c[i] * basis[i];

	return result;
}


/*
   SH_scale()
   Scale all SH coefficients by a scalar.
*/
inline void SH_scale( SHCoeffs3& sh, float s ){
	for ( int i = 0; i < SH_NUM_COEFFS; ++i )
		sh.c[i] *= s;
}


/*
   SH_add()
   Add src coefficients into dst.
*/
inline void SH_add( SHCoeffs3& dst, const SHCoeffs3& src ){
	for ( int i = 0; i < SH_NUM_COEFFS; ++i )
		dst.c[i] += src.c[i];
}
