/* face.c: routines for manipulating BREP_FACEs */

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

#ifndef NOPOOLS
static POOL *facePool = (POOL *)NULL;
#define NEWFACE()  	(BREP_FACE *)NewPoolCell(sizeof(BREP_FACE), 0, "brep faces", &facePool)
#define DISPOSEFACE(ptr) Dispose((unsigned char *)(ptr), &facePool)
#else /* NOPOOLS */
#define NEWFACE()	(BREP_FACE *)Alloc(sizeof(BREP_FACE))
#define DISPOSEFACE(ptr) Free((char *)ptr, sizeof(BREP_FACE))
#endif /* NOPOOLS */

/* callback functions for manipulating BREP_FACEs */
static BREP_CALLBACK_FUNC brep_close_face_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_destroy_face_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_create_face_callback = (BREP_CALLBACK_FUNC)NULL;

/* set the CreateFace callback routine */
BREP_CALLBACK_FUNC BrepSetCreateFaceCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_create_face_callback;
  brep_create_face_callback = func;
  return oldfunc;
}

/* set the CloseFace callback routine */
BREP_CALLBACK_FUNC BrepSetCloseFaceCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_close_face_callback;
  brep_close_face_callback = func;
  return oldfunc;
}

/* set the DestroyFace callback routine */
BREP_CALLBACK_FUNC BrepSetDestroyFaceCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_destroy_face_callback;
  brep_destroy_face_callback = func;
  return oldfunc;
}

/* connects the face to the shell */
void BrepConnectFaceToShell(BREP_FACE *face, BREP_SHELL *shell)
{
  face->shell = shell;
  if (!shell)
    return;

  if (!shell->faces) { /* this is the first face in the shell */
    shell->faces = face;
    face->next = face->prev = face;
  } else {		/* not the first face in the shell */
    face->next = shell->faces;
    face->prev = shell->faces->prev;
    face->next->prev = face->prev->next = face;
  }
}

/* creates a new empty face within the shell */
BREP_FACE *BrepCreateFace(BREP_SHELL *shell, void *client_data)
{
  BREP_FACE *face;

  face = NEWFACE();
  face->outer_contour = (BREP_CONTOUR *)NULL;

  BrepConnectFaceToShell(face, shell);

  /* if a CreateFace callback has been specified, call it */
  face->client_data = client_data;
  if (brep_create_face_callback)
    face->client_data = brep_create_face_callback(face);

  return face;
}

/* various actions to be performed when a face is specified completely */
void BrepCloseFace(BREP_FACE *face)
{
  /* notify the user that the contours of the face have been completely 
   * specified */
  BrepFaceIterateContours(face, BrepCloseContour);

  /* if a CloseFace callback has been specified, call it */
  if (brep_close_face_callback)
    face->client_data = brep_close_face_callback(face);
}

/* execute func for every contour in the face */
void BrepFaceIterateContours(BREP_FACE *face, void (*func)(BREP_CONTOUR *))
{
  BrepIterate((BREP_RING *)face->outer_contour, (void (*)(BREP_RING *))func);
}

void BrepFaceIterateContours1A(BREP_FACE *face, void (*func)(BREP_CONTOUR *, void *), void *parm)
{
  BrepIterate1A((BREP_RING *)face->outer_contour, (void (*)(BREP_RING *, void *))func, parm);
}

void BrepFaceIterateContours2A(BREP_FACE *face, void (*func)(BREP_CONTOUR *, void *, void *), void *parm1, void *parm2)
{
  BrepIterate2A((BREP_RING *)face->outer_contour, (void (*)(BREP_RING *, void *, void *))func, parm1, parm2);
}

/* disconnect the face from its containing shell */
void BrepDisconnectFaceFromShell(BREP_FACE *face)
{
  BREP_SHELL *shell = face->shell;

  if (!shell)			/* not connected to a shell */
    return;

  if (shell->faces == face) {	/* the face is the first face 
				 * in the shell */
    if (face->next == face)	/* it is the only face */
      shell->faces = (BREP_FACE *)NULL;

    else			/* make the next face the 
				 * first face of the shell */
      shell->faces = face->next;
  } 

  face->next->prev = face->prev;
  face->prev->next = face->next;

  face->shell = (BREP_SHELL *)NULL;
}

/* destroys the contours in a face */
static void BrepFaceDestroyContours(BREP_CONTOUR *first)
{
  BREP_CONTOUR *contour, *prev;

  if (first) {
    for (contour = first->prev; contour != first; contour = prev) {
      prev = contour->prev;
      BrepDestroyContour(contour);
    }
    BrepDestroyContour(first);
  }
}

/* release all storage associated with a face and its contours, including
 * edges and vertices if not used in other faces as well */
void BrepDestroyFace(BREP_FACE *face)
{
  /* inverse actions performed in BrepCreateFace() in reverse order */

  /* disconnect the face from the containing shell */
  BrepDisconnectFaceFromShell(face);
  
  /* notify the user that the client_data can be disposed of */
  if (brep_destroy_face_callback)
    brep_destroy_face_callback(face);

  /* destroy its contours */
  BrepFaceDestroyContours(face->outer_contour);

  /* dispose of the BREP_FACE structure itself */
  DISPOSEFACE(face);
}
