/* hrefine.c: hierarchical refinemaent */

#include "hrefine.h"
#include "globals.h"
#include "radiance.h"
#include "scene.h"
#include "render.h"
#include "formfactor.h"
#include "error.h"
#include "debug.h"
#include "Memory.h"
#include "render.h"

/* #define DEBUG */
#include "monitor.h"

/* defined in rad.c */
extern void CheckForEvents(void);

#include "shaftculling.h"
static GEOMLIST *candidateList;		/* voor shaftculling */

/* return FALSE if the interaction has to be refined further and TRUE if radiance
 * transport is will be computed accurate enough. There is a separate such "oracle"
 * function for gathering and shooting radiosity methods (defined in gauss_seidel.c and
 * southwell.c). The one to use is set by SetOracle(). */
static int (*AccurateEnough)(INTERACTION *);

void SetOracle(int (*oracle)(INTERACTION *))
{
	AccurateEnough = oracle;
}

/* Links the two patches, the new interaction is added to P's interaction list ---
 * links are not symmetrical: if P is linked with Q, Q does not have to be linked with
 * P. The fact that a lot a refinement is needed to compute light transport from 
 * toplevel(Q) to toplevel(P) accurately doesn't imply that so much refinement will 
 * be needed to compute the light transport from toplevel(P) to toplevel(Q) accurately! 
 * toplevel(Q) might eg be a very bright patch with low reflectivity. */
INTERACTION *Link(PATCH *P, PATCH *Q)
{
	static Float K[BASISMAXSIZE][BASISMAXSIZE];
	INTERACTION *PQ;
	Float deltaK;
	clock_t t;
	int vis;
#ifdef DEBUG
if (monitored)
fprintf(stderr, "Link(%d, %d): ", P->id, Q->id);
#endif

#ifdef NEVER	/* can't happen (or at least, it shouldn't ...) */
	if ((PQ = InteractionLookup(P->interactions, Q)) != (INTERACTION *)NULL) {
#ifdef DEBUG 
fprintf(stderr, " already linked: ");
InteractionPrint(stderr, PQ);
#endif 
		return PQ;
		
	} else 
#endif /*NEVER*/

	if (!Facing(P, Q)) {
#ifdef DEBUG
if (monitored)
fprintf(stderr, " not facing\n");
#endif
		return (INTERACTION *)NULL;

	} else {
		t = clock();
		deltaK = AreaToAreaFormFactor(P, Q, candidateList, K, &vis);
		usecs_ff_computation += clock()-t;
		if (vis) {
			PQ = InteractionCreate(P, Q, K, deltaK);
			P->interactions = InteractionListAdd(P->interactions, PQ);
#ifdef DEBUG
if (monitored) {
fprintf(stderr, " new:\n");
InteractionPrint(stderr, PQ);
}
#endif
			return PQ;
		}
#ifdef DEBUG
if (monitored)
fprintf(stderr, " don't interact (vis=%d).\n", vis);
#endif
	}

	return (INTERACTION *)NULL;
}

static void Unlink(INTERACTION *PQ)
{
	PATCH *P = PQ->P;
#ifdef DEBUG
if (monitored)
fprintf(stderr, "Unlink(%d -> %d).\n", PQ->P->id, PQ->Q->id);
#endif
	P->interactions = InteractionListRemove(P->interactions, PQ);
	InteractionDestroy(PQ);
}

static INTERACTIONLIST *CreateSubInteractionsP(PATCHLIST *Psubpatches, PATCH *Q)
{
	INTERACTIONLIST *subinteractions;
	PATCH *Psub;

	subinteractions = InteractionListCreate();
	while ((Psub = PatchListNext(&Psubpatches))) {
		subinteractions = InteractionListAdd(subinteractions, Link(Psub, Q));
		if (demo) RenderElementOutline(Psub);
	}

	return subinteractions;
}

static INTERACTIONLIST *CreateSubInteractionsQ(PATCH *P, PATCHLIST *Qsubpatches)
{
	INTERACTIONLIST *subinteractions;
	PATCH *Qsub;

	subinteractions = InteractionListCreate();
	while ((Qsub = PatchListNext(&Qsubpatches))) {
		subinteractions = InteractionListAdd(subinteractions, Link(P, Qsub));
		if (demo) RenderElementOutline(Qsub);
	}

	return subinteractions;
}

static INTERACTIONLIST *InteractionSubdivide(INTERACTION *PQ)
{
	PATCH *P = PQ->P, *Q = PQ->Q;  
	INTERACTIONLIST *subinteractions = (INTERACTIONLIST *)NULL;

/* we always subdivide the largest of the two patches ... until we have a better
 * criterion. */
	if (P->area > Q->area) 
		subinteractions = CreateSubInteractionsP(PatchRefine(P), Q);
	else
		subinteractions = CreateSubInteractionsQ(P, PatchRefine(Q));

	return subinteractions;
}

static void InteractionRefineRecursive(INTERACTION *PQ)
{
	INTERACTIONLIST *subinteractions;
	SHAFT shaft;
	GEOMLIST *oldCandidateList = (GEOMLIST *)NULL;
	BOUNDINGBOX Pbounds, Qbounds;
	clock_t t;
	
#ifdef DEBUG
if (monitored)
fprintf(stderr, "InteractionRefineRecursive: %d -> %d\n", PQ->P->id, PQ->Q->id);
#endif
	if (!AccurateEnough(PQ)) {
		if (shaftcullmode == SHAFTCULL_REFINE ||
		    shaftcullmode == SHAFTCULL_ALWAYS) {
			t = clock();
			ConstructShaft(PatchBounds(PQ->P, Pbounds), PatchBounds(PQ->Q, Qbounds), &shaft);
			oldCandidateList = candidateList;
			candidateList = DoShaftCulling(oldCandidateList, &shaft, GeomListCreate());
			usecs_shaftculling += clock()-t;
		} 

		if ((subinteractions = InteractionSubdivide(PQ))) {
			Unlink(PQ);	
			InteractionListIterate(subinteractions, InteractionRefineRecursive);
			InteractionListDestroy(subinteractions);
		}

		if (shaftcullmode == SHAFTCULL_REFINE ||
		    shaftcullmode == SHAFTCULL_ALWAYS) {
			t = clock();
			FreeCandidateList(candidateList);
			candidateList = oldCandidateList;
			usecs_shaftculling += clock()-t;
		}
	}
}

static void InteractionRefine(INTERACTION *PQ)
{
#ifdef DEBUG
if (monitored)
fprintf(stderr, "InteractionRefine: %d -> %d\n", PQ->P->id, PQ->Q->id);
#endif /*DEBUG*/
        candidateList = World;
	InteractionRefineRecursive(PQ);

	CheckForEvents();	/* for better feedback to the user of the program */
}

void PatchRefineInteractions(PATCH *P)
{
/* interactions will only be replaced by lower level interactions. We try refinement 
 * beginning at the lowest levels in the hierarchy and working upwards 1) to save time
 * 2) to prevent already refined interactions to be refined again, but I think the code
 * would do it right anyways ... I haven't tried it, seems pretty useless. */
#ifdef DEBUG
if (monitored)
fprintf(stderr, "PatchRefineInteractions: patch %d \n", P->id);
#endif /*DEBUG*/

	PatchListIterate(P->subpatches, PatchRefineInteractions);
#ifdef DEBUG
if (monitored)
fprintf(stderr, "Net voor InteractionListIterate... \n");
#endif
	InteractionListIterate(P->interactions, InteractionRefine);
}
