/* shadowcaching.c: shadow caching routines */
#include "shadowcaching.h"
#include "patch.h"
#include "grid.h"
#include "scene.h"

/* for statistics */
int ShadowRays=0, ShadowCacheHits=0;

#define MAXCACHE 5	/* cache at most 5 blocking patches */
static PATCH *cache[MAXCACHE];
static int cachedpatches, ncached;

/* initialize/empty the shadow cache */
void InitShadowCache(void)
{
  int i;

  ncached = cachedpatches = 0;
  for (i=0; i<MAXCACHE; i++) cache[i] = NULL;
}

/* test ray against patches in the shadow cache. Returns NULL if the ray hits
 * no patches in the shadow cache, or a pointer to the first hit patch otherwise.  */
HITREC *CacheHit(RAY *ray, float *dist, HITREC *hitstore)
{
  int i; 
  HITREC *hit;
  
  for (i=0; i<ncached; i++) {
    if ((hit = PatchIntersect(cache[i], ray, EPSILON*(*dist), dist, HIT_FRONT|HIT_ANY, hitstore)))
      return hit;
  }
  return NULL;
}
				
/* replace least recently added patch */
void AddToShadowCache(PATCH *patch)
{
  cache[cachedpatches % MAXCACHE] = patch;
  cachedpatches++;
  if (ncached<MAXCACHE) ncached++;
}

/* Tests whether the ray intersects the discretisation of a GEOMetry in the list 
 * 'world'. Returns NULL if the ray hits no geometries. Returns an arbitrary hit 
 * patch if the ray does intersect one or more geometries. Intersections
 * further away than dist are ignored. Patches in the shadow cache are
 * checked first. */
HITREC *ShadowTestDiscretisation(RAY *ray, GEOMLIST *world, float dist, HITREC *hitstore)
{
  HITREC *hit=NULL;

  ShadowRays++;
  if ((hit = CacheHit(ray, &dist, hitstore)))
    ShadowCacheHits++;
  else {
    if (world != ClusteredWorld && world != World) 
      hit = GeomListDiscretisationIntersect(world, ray, EPSILON*dist, &dist, HIT_FRONT|HIT_ANY, hitstore);
    else
      hit = GridIntersect(WorldGrid, ray, EPSILON*dist, &dist, HIT_FRONT|HIT_ANY, hitstore);
    if (hit)
      AddToShadowCache(hit->patch);
  }

  return hit;
}

/* like ShadowTestDiscretisation, except that a list of PATCHes is tested
 * for intersection. */
HITREC *ShadowTestPatchlist(RAY *ray, PATCHLIST *ShadowList, float dist, HITREC *hitstore)
{
  HITREC *hit=NULL;

  ShadowRays++;
  if ((hit = CacheHit(ray, &dist, hitstore)))
    ShadowCacheHits++;
  else if ((hit = PatchListIntersect(ShadowList, ray, EPSILON*dist, &dist, HIT_FRONT|HIT_ANY, hitstore)))
    AddToShadowCache(hit->patch);
  
  return hit;
}

