/* southwell.c: progressive refinement radiosity */

#include "scene.h"
#include "globals.h"
#include "radiance.h"
#include "importance.h"
#include "hrefine.h"
#include "render.h"
#include "camera.h"
#include "error.h"
#include "coefficients.h"
#include "initiallinking.h"

#include "monitor.h"

/* returns TRUE if the link should be further refined and FALSE if our estimate of
 * the approximation error over the link is small enough */
static int ShootingAccurateEnough(INTERACTION *PQ)
{
	PATCH *P = PQ->P, *Q = PQ->Q;
	float deltaBF;
extern int TotalElements;

/* sanity check */
	if (P->id < 1 || P->id > TotalElements+1 ||
	    Q->id < 1 || Q->id > TotalElements+1) {
		fprintf(stderr, "ShootingAccurateEnough() got garbage: P->id = %d, Q->id = %d\n",
			P->id, Q->id);
		return TRUE;
	}

	if (P->area < Aeps && Q->area < Aeps) {
		return TRUE;
	} else {
		COLOR deltaB, max_unshot_rad;

		MAXCOEFFICIENT(P->unshot_radiance, max_unshot_rad, P->basis->size);
		COLORPROD(Q->surface->material->Kd, max_unshot_rad, deltaB);
		deltaBF = PQ->deltaK * COLORSUMABSCOMPONENTS(deltaB);
		deltaBF *= M_PI * Q->area;

		return deltaBF < BFeps;
	}
}

/* ******************** propagation of radiance ************************* */
static PATCH *NextMostRadianceContributingPatch(void)
{
	PATCH *P, *next_most_contributing;
	PATCHLIST *patches = Patches;
	float unshot_power, max_unshot_power;  
	COLOR upow;

	max_unshot_power = Beps;
	next_most_contributing = (PATCH *)NULL;

	while ((P = PatchListNext(&patches))) {
		upow = P->basis->GetUnshotPower(P);

		if (!ImportanceDriven)
			unshot_power = COLORSUMABSCOMPONENTS(upow);
		else {
			COLOR imp = P->basis->GetImportance(P);
			COLORABS(upow, upow);
			unshot_power = COLORSCALARPRODUCT(upow, imp)/P->area;
		}

		if (unshot_power > max_unshot_power) {
			next_most_contributing = P;
			max_unshot_power = unshot_power;
		}
	}

	return next_most_contributing;
}

static void InteractionShootRadiance(INTERACTION *PQ)
{
	PATCH *P = PQ->P, *Q = PQ->Q, *toplevelQ = ToplevelPatch(Q);
	Float *Kab;
	COLOR delta_rad;
	int a, b;

	for (b=0; b<Q->basis->size; b++) {
		COLORCLEAR(delta_rad);
		for (a=0, Kab=PQ->K+b; a<P->basis->size; a++, Kab+=Q->basis->size) 
			COLORADDSCALED(delta_rad, (*Kab), P->unshot_radiance[a], delta_rad);
		COLORPROD(Q->surface->material->Kd, delta_rad, delta_rad);
		COLORADD(Q->received_radiance[b], delta_rad, Q->received_radiance[b]);
	}

	toplevelQ->updated = TRUE;
}

static void PushPullRadianceIfUpdated(PATCH *patch)
{
	if (patch->updated) {
		patch->basis->ShootPushPullRadiance(patch);
		if (!render_ambient)
			PatchComputeVertexColors(patch);
	}
	patch->updated = FALSE;
}

static void PatchShootRadiance(PATCH *P)
{
	PatchListIterate(P->subpatches, PatchShootRadiance);
	InteractionListIterate(P->interactions, InteractionShootRadiance);
	CLEARCOEFFICIENTS(P->unshot_radiance, P->basis->size);
}

static int PropagateRadiance(void)
{
	PATCH *shootingpatch;
	clock_t t;
	COLOR dark;

	t = clock() - usecs_ff_computation - usecs_shaftculling;
	if ((shootingpatch = NextMostRadianceContributingPatch()) == (PATCH *)NULL) 
		return TRUE;	/* no patches with unshot radiance ---> finished */

#ifdef DEBUG
if (monitored) {
	COLOR unshot_power = shootingpatch->basis->GetUnshotPower(shootingpatch);
fprintf(stderr, "radiance shooting patch = %d, unshot power = ", shootingpatch->id);
COLORPrint(stderr, unshot_power);
fprintf(stderr, "\n"); 
}
#endif /*DEBUG*/

	if (!shootingpatch->interactionscreated)
		CreateToplevelInteractions(shootingpatch);

        if (HierarchicalRefinement) {
		SetOracle(ShootingAccurateEnough);
		PatchRefineInteractions(shootingpatch);
	/*	if (demo) PatchRenderLinks(shootingpatch); */
	}

        PatchShootRadiance(shootingpatch);

	if (!render_ambient) {
		COLORCLEAR(dark);
		SetAmbientRadiance(&dark);
	}

/* also computes new vertex colors for Gouraud shading if !render_ambient */	
        SetRadianceRescaleFactor(radiance_rescale);
        PatchListIterate(Patches, PushPullRadianceIfUpdated);	

	if (render_ambient) {
		ComputeAmbientRadiance();
		PatchListIterate(Patches, PatchComputeVertexColors);	
	}

        usecs_last_radiance_propagation = clock() - t - usecs_ff_computation - usecs_shaftculling;

	return FALSE;	/* not yet finished, more computation is needed */
}

/* ******************** propagation of importance ************************* */
static PATCH *NextMostImportanceContributingPatch(void)
{
	PATCH *P, *next_most_contributing;
	PATCHLIST *patches = Patches;
	float unshot_importance, max_unshot_importance;  

	max_unshot_importance = Ieps;
	next_most_contributing = (PATCH *)NULL;

	while ((P = PatchListNext(&patches))) {
		COLOR uimp = P->basis->GetUnshotImportance(P);
		unshot_importance = COLORSUMABSCOMPONENTS(uimp);
		if (unshot_importance > max_unshot_importance) {
			next_most_contributing = P;
			max_unshot_importance = unshot_importance;
		}
	}

	return next_most_contributing;
}

static void InteractionShootImportance(INTERACTION *PQ)
{
	PATCH *P = PQ->P, *Q = PQ->Q, *toplevelQ = ToplevelPatch(Q);
	Float *Kab;
	COLOR delta_imp;
	int a, b;

	for (b=0; b<Q->basis->size; b++) {
		COLORCLEAR(delta_imp);
		for (a=0, Kab=PQ->K+b; a<P->basis->size; a++, Kab+=Q->basis->size) 
			COLORADDSCALED(delta_imp, *Kab, P->unshot_importance[a], delta_imp);
		COLORPROD(P->surface->material->Kd, delta_imp, delta_imp);
		COLORADD(Q->received_importance[b], delta_imp, Q->received_importance[b]);
	}

	toplevelQ->updated = TRUE;
}

static void PatchShootImportance(PATCH *P)
{
	PatchListIterate(P->subpatches, PatchShootImportance);
	InteractionListIterate(P->interactions, InteractionShootImportance);
	CLEARCOEFFICIENTS(P->unshot_importance, P->basis->size);
}

static void PushPullImportanceIfUpdated(PATCH *patch)
{
	if (patch->updated) 
		patch->basis->ShootPushPullImportance(patch);
	patch->updated = FALSE;
#ifdef DEBUG
if (monitored) {
fprintf(stderr, "patch %d: importance = ", patch->id); 
COLORPrint(stderr, patch->importance[0]);
fprintf(stderr, ", unshot importance = ");
COLORPrint(stderr, patch->unshot_importance[0]);
fprintf(stderr, "\n");
}
#endif /*DEBUG*/
}

/* see importance.h */
static void PatchUpdateDirectImportance(PATCH *P, float *newimp)
{
	float new_direct_importance, delta_importance;
	COLOR tmp;

	PatchListIterate1A(P->subpatches, PatchUpdateDirectImportance, newimp);

	new_direct_importance = newimp[P->id]/P->area;
	delta_importance = new_direct_importance - P->direct_importance;
	P->direct_importance = new_direct_importance;

/* P->basis->ShootPushPullImportance() also divides P->received_importance by P->area */
	COLORSETMONOCHROME(tmp, P->area * delta_importance);
	P->basis->AddConstant(P->received_importance, &tmp);

	ToplevelPatch(P)->updated = TRUE;
#ifdef DEBUG
if (monitored)
fprintf(stderr, "Patch %d: new direct importance = %g (delta importance = %g).\n", 
	P->id, new_direct_importance, delta_importance);
#endif /*DEBUG*/
}

static void ComputeNewDirectImportance(void)
{
	char rendermode = RenderGetMode();

	SetPatchDirectImportanceUpdateRoutine(PatchUpdateDirectImportance);
	UpdateDirectImportance();
	PatchListIterate(Patches, PushPullImportanceIfUpdated);

	if (rendermode == RENDER_IMPORTANCE || 
	    rendermode == RENDER_UNSHOT_IMPORTANCE)
		RenderScene();
}

static void PropagateImportance(void)
{
	PATCH *shootingpatch;
	char rendermode = RenderGetMode();
	clock_t t;

	t = clock() - usecs_ff_computation - usecs_shaftculling;
	if ((shootingpatch = NextMostImportanceContributingPatch()) != (PATCH *)NULL) {
#ifdef DEBUG
if (monitored) {
fprintf(stderr, "importance shooting patch = %d: unshot importance = ", shootingpatch->id);
PRINTCOEFFICIENTS(stderr, shootingpatch->unshot_importance);
fprintf(stderr, "\n");
}
#endif /*DEBUG*/
		if (!shootingpatch->interactionscreated) 
			CreateToplevelInteractions(shootingpatch);

/* no hierarchical refinement based on importance, we do refinement based on radiance only */
		PatchShootImportance(shootingpatch);
		PatchListIterate(Patches, PushPullImportanceIfUpdated);	

		if (rendermode == RENDER_IMPORTANCE || 
		    rendermode == RENDER_UNSHOT_IMPORTANCE)
			RenderScene();
	}

	usecs_last_importance_propagation = clock() - t - usecs_ff_computation - usecs_shaftculling;
}

/* ******************* one step in the radiance computation *********************** */
int DoSouthwellOneStep(void)
{
	iterationr ++;

	usecs_last_importance_propagation = 0;
	usecs_last_radiance_propagation = 0;

	if (ImportanceDriven) {
		if (Camera.changed) {
			ComputeNewDirectImportance();
			Camera.changed = FALSE;
		}

		PropagateImportance();
	}

	return PropagateRadiance();
}


