//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------		
#include <Render/WhittedIntegrator.hpp>
#include <Render/Scene.hpp>
#include <algorithm>


namespace Render
{
	//-----------------------------------------------------------------
	// C/D-Tor
	//-----------------------------------------------------------------
	WhittedIntegrator::WhittedIntegrator(int _maxDepth) : 
	SurfaceIntegrator(),
	maxDepth(_maxDepth)
	{

	}
	//-----------------------------------------------------------------
	WhittedIntegrator::~WhittedIntegrator()
	{

	}
	//-----------------------------------------------------------------
	Math::vec4 WhittedIntegrator::Li(const Scene& _scene, const Ray& _ray)
	{
		return ReccursiveRaytrace(_scene, _ray, maxDepth);
	}
	//------------------------------------------------------------------------
	Math::vec4 WhittedIntegrator::ReccursiveRaytrace(const Scene& _scene, const Ray& _ray, int _currentDepth) const
	{
		if(_currentDepth-- == 0)
			return Math::vec4(0.f);

		Intersection intersection;
		if(_scene.Intersect(_ray, intersection))
		{
			const BSDF& bsdf = _scene.GetMaterial(intersection.MaterialID);
			const Math::vec3 n(0.,0.,1.);

			// Transform vector in tagent space
			Math::mat3 TBN 	  = intersection.ComputeLocalToWorld();
			Math::mat3 invTBN = intersection.ComputeWorldToLocal();
			Math::vec3 wo     = invTBN * (_ray.Direction * -1.f);

			Math::vec3 finalL(0.f);
			const int nLights = _scene.lights.size();
			for(int i=0;i<nLights;++i)
			{
				// 
				int nSamples = _scene.lights[i]->nSamples;
				for(int s=0;s<nSamples;++s)
				{	
					// Compute light sample direction
					float lightSamplePdf;
					Math::vec3 wiW;
					VisibilityTester tester;
					Math::vec3 Li = _scene.lights[i]->SampleL(intersection.Position, wiW, lightSamplePdf, tester);

					// Check light direction visibility
					if(_scene.Unoccluded(tester))
					{
						// Get BRDF value
						Math::vec3 wi = invTBN * wiW;
						Math::vec3 fr = bsdf.Fr(wo, wi, BSDF_DIFFUSE);

						// Compute final radiance
						Math::vec3 L;
						float dotL = std::max(Math::dot(n,wi),0.f);
						L.x =  fr.x * Li.x;
						L.y =  fr.y * Li.y;
						L.z =  fr.z * Li.z;
						finalL += L * dotL;
					}
				}
			}

			// Compute specular ray 
			float reflectPdf;
			Math::vec3 wi;
			Math::vec3 reflectFr = bsdf.SampleFr(wo, wi, reflectPdf, BxDFType(BSDF_SPECULAR)); // or BSDF_SPECULAR | BSDF_REFLECTION ?
			if(!Math::isNull(reflectFr))
			{
				Math::vec3 wiW 		   = TBN * wi;

				Ray reflectedRay;
				reflectedRay.Origin    = intersection.Position;
				reflectedRay.Direction = wiW;

// printf("Wi : %s\n", Math::ToString(wi).c_str());
//				float dotL = std::max(Math::dot(n,wi),0.f);
//				Math::vec4 reflectRad = ReccursiveRaytrace(_scene,reflectedRay,_currentDepth);
//				Math::vec3 L;
//				L.x += reflectRad.x * reflectFr.x;
//				L.y += reflectRad.y * reflectFr.y;
//				L.z += reflectRad.z * reflectFr.z;
//				finalL += L;

//finalL += reflectFr;
finalL += reflectFr;
				 printf("Fr : %s\n", Math::ToString(reflectFr).c_str());
			}

//			Math::vec3 wiW    = Math::normalize(Math::vec3(0.,0.,3.) - intersection.Position);

			return Math::vec4(finalL.toGLMVec3(), 1.);

//			return Math::vec4(intersection.Normal,1.);

//			Math::vec3 pack = (intersection.Normal + Math::vec3(1.f)) * 0.5f;
//			return Math::vec4(pack,1.);
//			return Math::vec4(Math::vec3(intersection.T),1.);

/*			if(intersection.Position.z<0)
				return Math::vec4(Math::vec3(radiance),1.);
			else
				return Math::vec4(Math::vec3(1.,0.,0.),1.);*/
		}
		return Math::vec4(0.f);		
	}
}
