/* shell.c: routines for manipulating BREP_SHELLs */

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

#ifndef NOPOOLS
static POOL *shellPool = (POOL *)NULL;
#define NEWSHELL()  	(BREP_SHELL *)NewPoolCell(sizeof(BREP_SHELL), 0, "brep shells", &shellPool)
#define DISPOSESHELL(ptr) Dispose((unsigned char *)(ptr), &shellPool)
#else /* NOPOOLS */
#define NEWSHELL()	(BREP_SHELL *)Alloc(sizeof(BREP_SHELL))
#define DISPOSESHELL(ptr) Free((char *)ptr, sizeof(BREP_SHELL))
#endif /* NOPOOLS */

/* callback functions for manipulating BREP_SHELLs */
static BREP_CALLBACK_FUNC brep_close_shell_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_destroy_shell_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_create_shell_callback = (BREP_CALLBACK_FUNC)NULL;

/* set the CreateShell callback routine */
BREP_CALLBACK_FUNC BrepSetCreateShellCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_create_shell_callback;
  brep_create_shell_callback = func;
  return oldfunc;
}

/* set the CloseShell callback routine */
BREP_CALLBACK_FUNC BrepSetCloseShellCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_close_shell_callback;
  brep_close_shell_callback = func;
  return oldfunc;
}

/* set the DestroyShell callback routine */
BREP_CALLBACK_FUNC BrepSetDestroyShellCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_destroy_shell_callback;
  brep_destroy_shell_callback = func;
  return oldfunc;
}

/* connects the shell to the solid */
void BrepConnectShellToSolid(BREP_SHELL *shell, BREP_SOLID *solid)
{
  shell->solid = solid;
  if (!solid)
    return;

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

/* creates a new empty shell within the solid */
BREP_SHELL *BrepCreateShell(BREP_SOLID *solid, void *client_data)
{
  BREP_SHELL *shell;

  shell = NEWSHELL();
  shell->faces = (BREP_FACE *)NULL;
  shell->client_data = client_data;

  BrepConnectShellToSolid(shell, solid);

  /* if a CreateShell callback has been specified, call it */
  shell->client_data = client_data;
  if (brep_create_shell_callback)
    shell->client_data = brep_create_shell_callback(shell);

  return shell;
}

/* perform various actions to be performed when a shell has been
 * completely specified */
void BrepCloseShell(BREP_SHELL *shell)
{
  /* notify the user that the faces of the shell have been completely 
   * specified */
  BrepShellIterateFaces(shell, BrepCloseFace);

  /* if a CloseShell callback has been specified, call it */
  if (brep_close_shell_callback)
    shell->client_data = brep_close_shell_callback(shell);
}

/* execute func for every face in the shell */
void BrepShellIterateFaces(BREP_SHELL *shell, void (*func)(BREP_FACE *))
{
  BrepIterate((BREP_RING *)shell->faces, (void (*)(BREP_RING *))func);
}

void BrepShellIterateFaces1A(BREP_SHELL *shell, void (*func)(BREP_FACE *, void *), void *parm)
{
  BrepIterate1A((BREP_RING *)shell->faces, (void (*)(BREP_RING *, void *))func, parm);
}

void BrepShellIterateFaces2A(BREP_SHELL *shell, void (*func)(BREP_FACE *, void *, void *), void *parm1, void *parm2)
{
  BrepIterate2A((BREP_RING *)shell->faces, (void (*)(BREP_RING *, void *, void *))func, parm1, parm2);
}

/* disconnect the shell from the solid */
void BrepDisconnectShellFromSolid(BREP_SHELL *shell)
{
  BREP_SOLID *solid = shell->solid;
  
  if (!solid)			/* not connected to a solid */
    return;

  if (solid->shells == shell) {	/* the shell is the first shell 
				 * in the solid */
    if (shell->next == shell)	/* it is the only shell */
      solid->shells = (BREP_SHELL *)NULL;

    else			/* make the next shell the 
				 * first shell of the solid */
      solid->shells = shell->next;
  } 

  shell->next->prev = shell->prev;
  shell->prev->next = shell->next;

  shell->solid = (BREP_SOLID *)NULL;
}

/* destroys the faces in a shell */
static void BrepShellDestroyFaces(BREP_FACE *first)
{
  BREP_FACE *face, *prev;

  if (first) {
    for (face = first->prev; face != first; face = prev) {
      prev = face->prev;
      BrepDestroyFace(face);
    }
    BrepDestroyFace(first);
  }
}

/* release all memory associated to a shell and its contained faces */
void BrepDestroyShell(BREP_SHELL *shell)
{
  /* inverse actions performed in BrepCreateShell() in reverse order */

  /* disconnect the shell from the containing solid */
  BrepDisconnectShellFromSolid(shell);
  
  /* notify the user that the client_data can be disposed of */
  if (brep_destroy_shell_callback)
    brep_destroy_shell_callback(shell);

  /* destroy its faces */
  BrepShellDestroyFaces(shell->faces);

  /* dispose of the BREP_SHELL structure itself */
  DISPOSESHELL(shell);
}
