/* solid.c: routines for manipulating BREP_SOLID structs */

#include "brep.h"
#include "private.h"
#include "pools.h"

#ifndef NOPOOLS
static POOL *solidPool = (POOL *)NULL;
#define NEWSOLID()  	(BREP_SOLID *)NewPoolCell(sizeof(BREP_SOLID), 0, "brep solids", &solidPool)
#define DISPOSESOLID(ptr) Dispose((unsigned char *)(ptr), &solidPool)
#else /* NOPOOLS */
#define NEWSOLID()	(BREP_SOLID *)Alloc(sizeof(BREP_SOLID))
#define DISPOSESOLID(ptr) Free((char *)ptr, sizeof(BREP_SOLID))
#endif /* NOPOOLS */

/* callback functions for manipulating BREP_SOLIDs */
static BREP_CALLBACK_FUNC brep_close_solid_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_destroy_solid_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_create_solid_callback = (BREP_CALLBACK_FUNC)NULL;

/* set the CreateSolid callback routine */
BREP_CALLBACK_FUNC BrepSetCreateSolidCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_create_solid_callback;
  brep_create_solid_callback = func;
  return oldfunc;
}

/* set the CloseSolid callback routine */
BREP_CALLBACK_FUNC BrepSetCloseSolidCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_close_solid_callback;
  brep_close_solid_callback = func;
  return oldfunc;
}

/* set the DestroySolid callback routine */
BREP_CALLBACK_FUNC BrepSetDestroySolidCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_destroy_solid_callback;
  brep_destroy_solid_callback = func;
  return oldfunc;
}

/* creates an empty solid */
BREP_SOLID *BrepCreateSolid(void *client_data)
{
  BREP_SOLID *solid;

  solid = NEWSOLID();
  solid->vertices = BrepCreateVertexOctree();
  solid->shells = (BREP_SHELL *)NULL;

  /* notify the user that the solid is created */
  solid->client_data = client_data;
  if (brep_create_solid_callback)
    solid->client_data = brep_create_solid_callback(solid);

  return solid;
}

/* performs various actions to be done when the boundary representation of
 * the solid has been completely specified. */
void BrepCloseSolid(BREP_SOLID *solid)
{
  /* notify the user that the shells are complete */
  BrepSolidIterateShells(solid, BrepCloseShell);

  /* notify the user that the solid is complete */
  if (brep_close_solid_callback)
    solid->client_data = brep_close_solid_callback(solid);
}

/* execute func for every shell in the solid */
void BrepSolidIterateShells(BREP_SOLID *solid, void (*func)(BREP_SHELL *))
{
  BrepIterate((BREP_RING *)solid->shells, (void (*)(BREP_RING *))func);
}

void BrepSolidIterateShells1A(BREP_SOLID *solid, void (*func)(BREP_SHELL *, void *), void *parm)
{
  BrepIterate1A((BREP_RING *)solid->shells, (void (*)(BREP_RING *, void *))func, parm);
}

void BrepSolidIterateShells2A(BREP_SOLID *solid, void (*func)(BREP_SHELL *, void *, void *), void *parm1, void *parm2)
{
  BrepIterate2A((BREP_RING *)solid->shells, (void (*)(BREP_RING *, void *, void *))func, parm1, parm2);
}

/* destroys the shells in a solid */
static void BrepSolidDestroyShells(BREP_SHELL *first)
{
  BREP_SHELL *shell, *prev;

  if (first) {
    for (shell = first->prev; shell != first; shell = prev) {
      prev = shell->prev;
      BrepDestroyShell(shell);
    }
    BrepDestroyShell(first);
  }
}

#ifdef NEVER
/* destroys all the vertices in a solid, this does not happen automatically when 
 * destroying a solid. Or is it? */
static void BrepSolidDestroyVertices(BREP_SOLID *solid)
{
  BrepIterateVertices(solid->vertices, BrepDestroyVertex);
}
#endif /*NEVER*/

/* releases all memory associated with the solid */
void BrepDestroySolid(BREP_SOLID *solid)
{
  /* inverse operations of BrepCreateSolid, and in reverse order */

  /* first let the user dispose of the client data */
  if (brep_destroy_solid_callback) 
    brep_destroy_solid_callback(solid);

  /* next dispose of the shells in the solid */
  BrepSolidDestroyShells(solid->shells);

  /* next dispose of the vertex octree */
  BrepDestroyVertices(solid->vertices);

  /* finally dispose of the BREP_SOLID structure itself */
  DISPOSESOLID(solid);
}

