/* element.h */

#ifndef _ELEMENT_H_
#define _ELEMENT_H_

#ifdef __cplusplus
extern "C" {
#endif

/* Data associated with each PATCH: */
#include "elementtype.h"

/* close these macros with EndForAll */
#define ForAllRegularSubelements(p, parent) {	\
if ((parent)->regular_subelements) { 		\
  int i; 					\
  for (i=0; i<4; i++) {				\
    ELEMENT *p = (parent)->regular_subelements[i];

#define ForAllIrregularSubelements(p, parent) { \
if ((parent)->irregular_subelements) {		\
  struct ELEMENTLIST *_el_;			\
  for (_el_ = (parent)->irregular_subelements; _el_; _el_ = _el_->next) { \
    ELEMENT *p = _el_->element;

#define ForAllVerticesOfElement(vert, elem) { 	\
  int _i_;					\
  for (_i_=0; _i_<(elem)->nrvertices; _i_++) {	\
    VERTEX *(vert) = (elem)->vertex[_i_]; {

extern ELEMENT *CreateToplevelSurfaceElement(PATCH *patch);
extern void McrDestroyToplevelSurfaceElement(ELEMENT *elem);
extern ELEMENT *McrCreateClusterHierarchy(GEOM *world);
extern void McrDestroyClusterHierarchy(ELEMENT *top);
extern void PrintElement(FILE *out, ELEMENT *elem);

extern void ForAllLeafElements(ELEMENT *top, void (*func)(ELEMENT *));
extern void McrForAllSurfaceLeafs(ELEMENT *top, void (*func)(ELEMENT *));
extern void ForAllClusterSurfaces(ELEMENT *top, void (*func)(ELEMENT *));

/* returns TRUE if top has children and returns FALSE if top is a leaf element */
extern int McrForAllChildrenElements(ELEMENT *top, void (*func)(ELEMENT *));

/* returns TRUE if elem is a leaf element */
extern int ElementIsLeaf(ELEMENT *elem);

extern void ElementRange(ELEMENT *elem, int *nbits, niedindex *msb1, niedindex *rmsb2);

extern float *ElementBounds(ELEMENT *elem, float *bounds);
extern int ElementVertices(ELEMENT *elem, POINT *p);

extern ELEMENT *ClusterChildContainingElement(ELEMENT *parent, ELEMENT *descendant);
extern ELEMENT **McrRegularSubdivideElement(ELEMENT *element);
extern ELEMENT *McrRegularSubelementAtPoint(ELEMENT *parent, double *u, double *v);
extern ELEMENT *RegularLeafElementAtPoint(ELEMENT *top, double *u, double *v);

extern VERTEX *McrEdgeMidpointVertex(ELEMENT *elem, int edgenr);

extern TRANSFORM2D quadupxfm[4], triupxfm[4];

/* only for surface elements!! */
extern int ElementIsTextured(ELEMENT *elem);

/* uses elem->Rd for surface elements */
extern float ElementScalarReflectance(ELEMENT *elem);

/* implemented pushpull.c and basis.c */
extern void PushRadiance(ELEMENT *parent, ELEMENT *child, COLOR *parent_rad, COLOR *child_rad);
extern void PushImportance(ELEMENT *parent, ELEMENT *child, float *parent_imp, float *child_imp);
extern void PullRadiance(ELEMENT *parent, ELEMENT *child, COLOR *parent_rad, COLOR *child_rad);
extern void PullImportance(ELEMENT *parent, ELEMENT *child, float *parent_imp, float *child_imp);

/* implemented in render.c */
extern COLOR ElementDisplayRadiance(ELEMENT *elem);
extern COLOR ElementDisplayRadianceAtPoint(ELEMENT *elem, double u, double v);
extern void RenderElement(ELEMENT *elem);
extern void RenderElementOutline(ELEMENT *elem);
extern void ElementComputeNewVertexColors(ELEMENT *elem);
extern void ElementAdjustTVertexColors(ELEMENT *elem);
extern RGB ElementColor(ELEMENT *elem);
extern COLOR VertexReflectance(VERTEX *v);

extern void ElementTVertexElimination(ELEMENT *elem,
				      void (*do_triangle)(VERTEX *, VERTEX *, VERTEX *),
				      void (*do_quadrilateral)(VERTEX *, VERTEX *, VERTEX *, VERTEX *));

/* don't overuse the macros below. There are often elegant alternatives. */

/* maximum element hierarchy depth: Fatal errors in the macros below if
 * the element hierarchy is deeper than this, but that is surely not a
 * normal situation anyways! */
#define MAX_HIERARCHY_DEPTH 100

#include "stackmac.h"

/* iterates over all toplevel surface element in the element hierarchy
 * with the cluster element 'top' on top.
 *
 * Usage:
 * 
 *   ELEMENT *cluster = (some cluster element)
 *   REC_ForAllClusterSurfaces(surf, cluster) {
 *      do something with ELEMENT 'surf' 
 *   } REC_EndForAllClusterSurfaces;    <---- don't forget!!!
 */
#define REC_ForAllClusterSurfaces(surface, top) { 			\
  int _did_recurse = FALSE;						\
  ELEMENTLIST *_subelp = (ELEMENTLIST*)NULL;			\
  STACK_DECL(ELEMENTLIST*, _selstack, MAX_HIERARCHY_DEPTH, _selp);	\
  ELEMENT *_curel = (top);						\
  _begin_recurse_CS:							\
  if (_curel->iscluster) {						\
    _did_recurse = TRUE;						\
    STACK_SAVE(_subelp, _selstack, MAX_HIERARCHY_DEPTH, _selp);		\
    _subelp = _curel->irregular_subelements; 				\
    while (_subelp) {							\
      _curel = _subelp->element;					\
      goto _begin_recurse_CS;						\
    _end_recurse_CS: 							\
      _subelp = _subelp->next;						\
    }									\
    STACK_RESTORE_NOCHECK(_subelp, _selstack, _selp);			\
    if (_subelp)	/* not back at top */				\
      goto _end_recurse_CS;						\
  } else {								\
    ELEMENT *surface = _curel;

  /* do something with 'surface' */

#define REC_EndForAllClusterSurfaces					\
    if (_did_recurse) 							\
      goto _end_recurse_CS; 						\
  }									\
}

/* iterates over all leaf elements in the surface element hierarchy with
 * 'top' (a surface element) on top */
#define REC_ForAllSurfaceLeafs(leaf, top) {				\
  int _i_ = -1, _did_recurse = FALSE;					\
  STACK_DECL(int, _isave, MAX_HIERARCHY_DEPTH, _isaveptr);		\
  ELEMENT *_curel = (top);						\
  _begin_recurse_SL:							\
  if (_curel->regular_subelements) { /* not a leaf */			\
    _did_recurse = TRUE;						\
    STACK_SAVE(_i_, _isave, MAX_HIERARCHY_DEPTH, _isaveptr);		\
    for (_i_=0; _i_<4; _i_++) {						\
      _curel = _curel->regular_subelements[_i_];			\
      goto _begin_recurse_SL;						\
    _end_recurse_SL:							\
      _curel = _curel->parent;						\
    }									\
    STACK_RESTORE_NOCHECK(_i_, _isave, _isaveptr);			\
    if (_curel != (top))	/* not back at top */			\
      goto _end_recurse_SL;						\
  } else {								\
    ELEMENT *leaf = _curel;

  /* do something with 'leaf' */

#define REC_EndForAllSurfaceLeafs					\
    if (_did_recurse) 							\
      goto _end_recurse_SL; 						\
  }									\
}

#define ForAllElementsSharingVertex(elem, v) ForAllElements(elem, (v)->radiance_data)

#ifdef __cplusplus
}
#endif

#endif /*_ELEMENT_H_*/
