/* initiallinking.c */

#include "galerkinP.h"
#include "formfactor.h"
#include "shaftculling.h"
#include "geom.h"
#include "scene.h"
#include "patch.h"
#include "ui.h"
#include "error.h"
#include "basis.h"

static ELEMENT *the_element;	/* the element for which initial links are to be created */
static ROLE the_role;		/* the role of that element: SOURCE or RECEIVER */
static PATCH *the_patch;	/* the patch for the element is the toplevel element */
static BOUNDINGBOX the_patch_bounds; /* bounding box for that patch */
static GEOMLIST *the_candlist;	/* candidate list for shaft culling */

void CreateInitialLink(PATCH *patch)
{
  ELEMENT *rcv=(ELEMENT *)NULL, *src=(ELEMENT *)NULL;
  GEOMLIST *oldcandlist = the_candlist;
  INTERACTION link;
  float ff[MAXBASISSIZE*MAXBASISSIZE];
  link.K.p = ff;
  /* link.deltaK.p = */

  if (!Facing(patch, the_patch))
    return;

  switch (the_role) {
  case SOURCE:
    rcv=TOPLEVEL_ELEMENT(patch); src=the_element;
    break;
  case RECEIVER:
    rcv=the_element; src=TOPLEVEL_ELEMENT(patch);
    break;
  default:
    Fatal(2, "CreateInitialLink", "Impossible element role");
  }

  if ((gal.exact_visibility || gal.shaftcullmode == ALWAYS_DO_SHAFTCULLING) && oldcandlist) {
    SHAFT shaft, *the_shaft;

    if (gal.exact_visibility) {
      POLYGON rcvpoly, srcpoly;
      the_shaft = ConstructPolygonToPolygonShaft(ElementPolygon(rcv, &rcvpoly),
						 ElementPolygon(src, &srcpoly),
						 &shaft);
    } else {
      BOUNDINGBOX bbox;
      the_shaft = ConstructShaft(the_patch_bounds, PatchBounds(patch, bbox), &shaft);
    }

    if (the_shaft) {
      ShaftOmit(&shaft, (GEOM *)the_patch);
      ShaftOmit(&shaft, (GEOM *)patch);
      the_candlist = DoShaftCulling(oldcandlist, the_shaft, (GEOMLIST *)NULL);

      if (the_shaft->cut == TRUE) {	/* one patch causes full occlusion. */
	FreeCandidateList(the_candlist);
	the_candlist = oldcandlist;
	return;
      }
    } else {
      /* should never happen though */
      Warning("CreateInitialLinks", "Unable to construct a shaft for shaft culling");
    }
  }

  link.rcv = rcv;
  link.src = src;
  link.nrcv = rcv->basis_size;
  link.nsrc = src->basis_size;
  AreaToAreaFormFactor(&link, the_candlist);

  if (gal.exact_visibility || gal.shaftcullmode == ALWAYS_DO_SHAFTCULLING) {
    if (oldcandlist != the_candlist)
      FreeCandidateList(the_candlist);
    the_candlist = oldcandlist;
  }

  if (link.vis > 0) {
    /* store interactions with the source patch for the progressive radiosity method
     * and with the receiving patch for gathering mathods. */
    if (gal.iteration_method == SOUTHWELL)
      src->interactions = InteractionListAdd(src->interactions, InteractionDuplicate(&link));
    else
      rcv->interactions = InteractionListAdd(rcv->interactions, InteractionDuplicate(&link));
  }

  if (gal.wake_up) {
    ProcessWaitingEvents();
    gal.wake_up = FALSE;
  }
}

/* yes ... we exploit the hierarchical structure of the scene during initial linking */
static void GeomLink(GEOM *geom)
{
  SHAFT shaft;
  GEOMLIST *oldCandList = the_candlist;

  /* immediately return if the GEOM is bounded and behind the plane of the patch 
   * for which itneractions are created ... */
  if (geom->bounded && BoundsBehindPlane(GeomBounds(geom), &the_patch->normal, the_patch->plane_constant)) 
    return;

  /* if the geometry is bounded, do shaft culling, reducing the candidate list
   * which contains the possible occluders between a pair of patches for which
   * an initial link will need to be created. */
  if (geom->bounded && oldCandList) {
    ConstructShaft(the_patch_bounds, GeomBounds(geom), &shaft);
    ShaftOmit(&shaft, (GEOM *)the_patch);
    the_candlist = DoShaftCulling(oldCandList, &shaft, GeomListCreate());
  }

  /* if the GEOM is an aggregate, test each of it's childer GEOMs, if it
  * is a primitive, create an initial link with each patch it consists of. */
  if (GeomIsAggregate(geom)) {
    GEOMLIST *geoml = GeomPrimList(geom);
    GeomListIterate(geoml, GeomLink);
  } else {
    PATCHLIST *patchl = GeomPatchList(geom);
    PatchListIterate(patchl, CreateInitialLink);
  }

  if (geom->bounded && oldCandList)
    FreeCandidateList(the_candlist);
  the_candlist = oldCandList;
}

/* Creates the initial interactions for a toplevel element which is
 * considered to be a SOURCE or RECEIVER according to 'role'. Interactions
 * are stored at the receiver element when doing gathering and at the
 * source element when doing shooting. */
void CreateInitialLinks(ELEMENT *top, ROLE role)
{
  if (IsCluster(top))
    Fatal(-1, "CreateInitialLinks", "cannot use this routine for cluster elements");

  the_element = top;
  the_role = role;
  the_patch = top->pog.patch;
  PatchBounds(the_patch, the_patch_bounds);
  the_candlist = ClusteredWorld;
  GeomListIterate(World, GeomLink);
}

/* Creates an initial link between the given element and the top cluster. */
void CreateInitialLinkWithTopCluster(ELEMENT *elem, ROLE role)
{
  ELEMENT *rcv=(ELEMENT *)NULL, *src=(ELEMENT *)NULL;
  INTERACTION *link;
  FloatOrPointer K, deltaK;
  float ff[MAXBASISSIZE*MAXBASISSIZE];
  int i;
  /* #ident <<< WMP */
#ifdef WMP_WEIGHTS
  float FK[MAXBASISSIZE];
#endif
  /* #ident >>> WMP */


  switch (role) {
  case RECEIVER:
    rcv=elem; src=gal.top_cluster;
    break;
  case SOURCE:
    src=elem; rcv=gal.top_cluster;
    break;
  default:
    Fatal(-1, "CreateInitialLinkWithTopCluster", "Invalid role");
  }

  /* assume no light transport (overlapping receiver and source) */
  if (rcv->basis_size * src->basis_size == 1)
    K.f = 0.;		
  else {
    K.p = ff;
    for (i=0; i<rcv->basis_size * src->basis_size; i++) K.p[i] = 0.;
  }
  deltaK.f = HUGE;	/* HUGE error on the form factor */

  /* #ident <<< WMP */
#ifdef WMP_WEIGHTS
  for(i = 0; i < rcv->basis_size; i++) FK[i] = 0.0;
#endif
  /* #ident >>> WMP */

  link = InteractionCreate(rcv, src,
			   K, deltaK, 
			   rcv->basis_size /*nrcv*/, 
			   src->basis_size /*nsrc*/, 
			   /*crcv*/ 1, /*vis*/ 128
			   /* #ident <<< WMP */
#ifdef WMP_WEIGHTS
			   ,FK
#endif
			   /* #ident >>> WMP */
    );

  /* store interactions with the source patch for the progressive radiosity method
   * and with the receiving patch for gathering mathods. */
  if (gal.iteration_method == SOUTHWELL)
    src->interactions = InteractionListAdd(src->interactions, link);
  else
    rcv->interactions = InteractionListAdd(rcv->interactions, link);
}


