/* vertex.c: routines for manipulating BREP_VERTEXes */

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

#ifndef NOPOOLS

static POOL *vertexPool = (POOL *)NULL;
#define NEWVERTEX()  	(BREP_VERTEX *)NewPoolCell(sizeof(BREP_VERTEX), 0, "brep vertices", &vertexPool)
#define DISPOSEVERTEX(ptr) Dispose((unsigned char *)(ptr), &vertexPool)

static POOL *ringelPool = (POOL *)NULL;
#define NEWRINGEL()  	(BREP_WING_RING *)NewPoolCell(sizeof(BREP_WING_RING), 0, "brep vertex wing rings", &ringelPool)
#define DISPOSERINGEL(ptr) Dispose((unsigned char *)(ptr), &ringelPool)

#else /* NOPOOLS */

#define NEWVERTEX()	(BREP_VERTEX *)Alloc(sizeof(BREP_VERTEX))
#define DISPOSEVERTEX(ptr) Free((char *)ptr, sizeof(BREP_VERTEX))

#define NEWRINGEL()	(BREP_WING_RING *)Alloc(sizeof(BREP_WING_RING))
#define DISPOSERINGEL(ptr) Free((char *)ptr, sizeof(BREP_WING_RING))

#endif /* NOPOOLS */

/* callback functions for manipulating BREP_VERTEXs. */
static BREP_CALLBACK_FUNC brep_create_vertex_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_close_vertex_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_destroy_vertex_callback = (BREP_CALLBACK_FUNC)NULL;

/* set the CreateVertex callback routine */
BREP_CALLBACK_FUNC BrepSetCreateVertexCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_create_vertex_callback;
  brep_create_vertex_callback = func;
  return oldfunc;
}

/* set the CloseVertex callback routine */
BREP_CALLBACK_FUNC BrepSetCloseVertexCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_close_vertex_callback;
  brep_close_vertex_callback = func;
  return oldfunc;
}

/* set the DestroyVertex callback routine */
BREP_CALLBACK_FUNC BrepSetDestroyVertexCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_destroy_vertex_callback;
  brep_destroy_vertex_callback = func;
  return oldfunc;
}

/* creates a new vertex - no edges in which the vertex is used are specified, 
 * this is done when creating an edge. */
BREP_VERTEX *BrepCreateVertex(void *client_data)
{
  BREP_VERTEX *vertex;

  vertex = NEWVERTEX();
  vertex->wing_ring = (BREP_WING_RING *)NULL;

  vertex->client_data = client_data;  
  if (brep_create_vertex_callback)
    vertex->client_data = brep_create_vertex_callback(vertex);

  /* a vertex is completely specified by its 3D coordinates */
  if (brep_close_vertex_callback)
    vertex->client_data = brep_close_vertex_callback(vertex);

  return vertex;
}

/* connect the wing ring element 'ringel' to the wing ring of the vertex */
static void BrepConnectWingRingToVertex(BREP_WING_RING *ringel, BREP_VERTEX *vertex)
{
  if (!vertex->wing_ring) {	/* first wing in contour */
    ringel->next = ringel->prev = ringel;
    vertex->wing_ring = ringel;
  } else {			/* not first wing */  
    ringel->next = vertex->wing_ring;
    ringel->prev = vertex->wing_ring->prev;
    ringel->next->prev = ringel->prev->next = ringel;
  }
}

/* Connect the wing to its starting vertex. The wing must have been connected to its
 * contour before. */
void BrepConnectWingToVertex(BREP_WING *wing)
{
  BREP_VERTEX *vertex = wing->vertex;
  BREP_WING_RING *ringel;

  ringel = NEWRINGEL();
  ringel->wing = wing;

  BrepConnectWingRingToVertex(ringel, vertex);
}

/* looks whether the wing is referenced in the wing ring of its
 * starting vertex. Returns a pointer to the BREP_WING_RING element if
 * it is found and NULL if not */
BREP_WING_RING *BrepFindWingLeavingVertex(BREP_WING *wing)
{
  BREP_VERTEX *vertex = wing->vertex;
  BREP_WING_RING *ringel, *next;
  
  if (vertex->wing_ring) {
    next = vertex->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (ringel->wing == wing)
	return ringel;
    } while (next && next != vertex->wing_ring);
  }

  return (BREP_WING_RING *)NULL;
}

/* disconnect the wing ring element from the wing ring it belongs to */
static void BrepDisconnectWingRingFromVertex(BREP_WING_RING *ringel)
{
  BREP_VERTEX *vertex = ringel->wing->vertex;

  if (vertex->wing_ring == ringel) {
    if (ringel->next == ringel)		/* it's the only wing leaving the 
					 * vertex */
      vertex->wing_ring = (BREP_WING_RING *)NULL;
    else
      vertex->wing_ring = ringel->next;
  }

  ringel->next->prev = ringel->prev;
  ringel->prev->next = ringel->next;
}

/* Disconnect the wing from its starting vertex. The wing must be properly connected to
 * a contour. */
void BrepDisconnectWingFromVertex(BREP_WING *wing)
{
  BREP_WING_RING *ringel;

  /* find the wing in the wing ring */
  ringel = BrepFindWingLeavingVertex(wing);
  if (!ringel) {
    BrepError(wing->edge->client_data, "BrepDisconnectWingFromVertex", "wing not connected to the vertex");
    return;
  }

  /* disconnect the wing ring element from the vertex */
  BrepDisconnectWingRingFromVertex(ringel);

  /* dispose the wing ring element */
  DISPOSERINGEL(ringel);
}

/* Looks whether an edge connecting the two vertices already
 * exists, Returns it if it exists. Returns NULL if not. */
BREP_EDGE *BrepFindEdge(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2)
{
  BREP_WING_RING *ringel, *next;

  /* there is an edge if there is a wing from vertex2 to vertex1 ... */
  if (vertex2->wing_ring) {
    next = vertex2->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (BrepEdgeOtherWing(ringel->wing)->vertex == vertex1)
	return ringel->wing->edge;
    } while (next && next != vertex2->wing_ring);
  } 

  /* ... or from vertex1 to vertex2 */
  if (vertex1->wing_ring) {
    next = vertex1->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (BrepEdgeOtherWing(ringel->wing)->vertex == vertex2)
	return ringel->wing->edge;
    } while (next && next != vertex1->wing_ring);
  } 

  /* no edge connecting the two vertices was found */
  return (BREP_EDGE *)NULL;
}

/* Iterator over all wings (included in a contour) connecting the two 
 * given vertices */
void BrepIterateWingsWithVertex(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2, 
				void (*func)(BREP_WING *))
{
  BREP_WING_RING *ringel, *next;

  if (vertex2->wing_ring) {
    next = vertex2->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (ringel->wing->contour && ringel->wing->next->vertex == vertex1)
	func(ringel->wing);
    } while (next && next != vertex2->wing_ring);
  }

  if (vertex1->wing_ring) {
    next = vertex1->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (ringel->wing->contour && ringel->wing->next->vertex == vertex2)
	func(ringel->wing);
    } while (next && next != vertex1->wing_ring);
  }
}

/* Iterator over all wings (included in a contour) connecting the two 
 * given vertices */
void BrepIterateWingsWithVertex1A(BREP_VERTEX *vertex1, BREP_VERTEX *vertex2, 
				  void (*func)(BREP_WING *, void *parm), 
				  void *parm)
{
  BREP_WING_RING *ringel, *next;

  if (vertex2->wing_ring) {
    next = vertex2->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (ringel->wing->contour && ringel->wing->next->vertex == vertex1)
	func(ringel->wing, parm);
    } while (next && next != vertex2->wing_ring);
  }

  if (vertex1->wing_ring) {
    next = vertex1->wing_ring;
    do {
      ringel = next;
      next = ringel->next;
      if (ringel->wing->contour && ringel->wing->next->vertex == vertex2)
	func(ringel->wing, parm);
    } while (next && next != vertex1->wing_ring);
  }
}

/* returns the next wing with same starting vertex as wing. The order is 
 * counterclockwise if the vertex is used in outer contours only and 
 * clockwise if used in inner contours only. Returns a null pointer
 * if the current wing is not properly connected to possible
 * other wings sharing the vertex, or the boundary of a partial
 * shell was reached. */
BREP_WING *BrepNextWingLeavingVertex(BREP_WING *wing)
{
  BREP_WING *next_wing;

  if (!wing->prev)  
    return (BREP_WING *)NULL;

  next_wing = BrepEdgeOtherWing(wing->prev);

  if (next_wing->contour)
    return next_wing;
  else
    return (BREP_WING *)NULL;
}

/* iterate over the wings leaving at the vertex, in arbitrary order. */
static void do_ringel_wing(BREP_WING_RING *ringel, void (*func)(BREP_WING *))
{
  func(ringel->wing);
}

void BrepVertexIterateWings(BREP_VERTEX *vertex, void (*func)(BREP_WING *))
{
  BrepIterate1A((BREP_RING *)vertex->wing_ring, (void (*)(BREP_RING *, void *))do_ringel_wing, (void *)func);
}

#ifdef NEVER
static void do_ringel_wing_parm(BREP_WING_RING *ringel, void (*func)(BREP_WING *, void *), void *parm)
{
  func(ringel->wing, parm);
}
#endif

void BrepVertexIterateWings1A(BREP_VERTEX *vertex, void (*func)(BREP_WING *, void *), void *parm)
{
  BrepIterate2A((BREP_RING *)vertex->wing_ring, (void (*)(BREP_RING *, void *, void *))do_ringel_wing, (void *)func, parm);
}

/* release all storage associated with the vertex. The vertex is supposed not
 * to be used anymore in any edge. Disconnecting vertices and edges happens
 * automatically when destroying edges. */
void BrepDestroyVertex(BREP_VERTEX *vertex)
{
  /* check whether the vertex is still being used in edges, if so, refuse to
   * destroy the vertex */
  if (vertex->wing_ring) {
    BrepInfo(vertex->client_data, "BrepDestroyVertex", "vertex still being used in edges, will not be destroyed"); 
    return;
  }

  /* notify the user that the vertex is being destroyed */
  if (brep_destroy_vertex_callback)
    brep_destroy_vertex_callback(vertex);

  /* dispose of the BREP_VERTEX structure itself */
  DISPOSEVERTEX(vertex);
}

/* moves the wing ring element 'ringel' to the wing ring of the
 * vertex 'newvertex' */
void BrepMoveWingRingToVertex(BREP_WING_RING *ringel, BREP_VERTEX *newvertex)
{
  /* disconnect the wing ring element from its vertex */
  BrepDisconnectWingRingFromVertex(ringel);

  /* connect the wing ring element to the wing ring of the new vertex */
  BrepConnectWingRingToVertex(ringel, newvertex);
}

