/* contour.c: routines for manipulating BREP_CONTOURs */

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

#ifndef NOPOOLS
static POOL *contourPool = (POOL *)NULL;
#define NEWCONTOUR()  	(BREP_CONTOUR *)NewPoolCell(sizeof(BREP_CONTOUR), 0, "brep contours", &contourPool)
#define DISPOSECONTOUR(ptr) Dispose((unsigned char *)(ptr), &contourPool)
#else /* NOPOOLS */
#define NEWCONTOUR()	(BREP_CONTOUR *)Alloc(sizeof(BREP_CONTOUR))
#define DISPOSECONTOUR(ptr) Free((char *)ptr, sizeof(BREP_CONTOUR))
#endif /* NOPOOLS */

/* callback functions for manipulating BREP_CONTOURs */
static BREP_CALLBACK_FUNC brep_close_contour_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_destroy_contour_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_create_contour_callback = (BREP_CALLBACK_FUNC)NULL,
                          brep_update_contour_callback = (BREP_CALLBACK_FUNC)NULL;

/* set the CreateContour callback routine */
BREP_CALLBACK_FUNC BrepSetCreateContourCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_create_contour_callback;
  brep_create_contour_callback = func;
  return oldfunc;
}

/* set the CloseContour callback routine */
BREP_CALLBACK_FUNC BrepSetCloseContourCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_close_contour_callback;
  brep_close_contour_callback = func;
  return oldfunc;
}

/* set the UpdateContour callback routine */
BREP_CALLBACK_FUNC BrepSetUpdateContourCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_update_contour_callback;
  brep_update_contour_callback = func;
  return oldfunc;
}

/* set the DestroyContour callback routine */
BREP_CALLBACK_FUNC BrepSetDestroyContourCallback(BREP_CALLBACK_FUNC func)
{
  BREP_CALLBACK_FUNC oldfunc = brep_destroy_contour_callback;
  brep_destroy_contour_callback = func;
  return oldfunc;
}

/* connects a contour to the specified face. The first contour in a face is
 * supposed to be the outer contour. */
void BrepConnectContourToFace(BREP_CONTOUR *contour, BREP_FACE *face)
{
  contour->face = face;
  if (!face->outer_contour) { 	/* this is the first contour in the face */
    face->outer_contour = contour;
    contour->next = contour->prev = contour;
  } else {			/* not the first contour in the face */
    contour->next = face->outer_contour;
    contour->prev = face->outer_contour->prev;
    contour->next->prev = contour->prev->next = contour;
  }
}

/* creates a new empty contour within the face */
BREP_CONTOUR *BrepCreateContour(BREP_FACE *face, void *client_data)
{
  BREP_CONTOUR *contour;

  contour = NEWCONTOUR();
  contour->wings = (BREP_WING *)NULL;

  BrepConnectContourToFace(contour, face);

  /* if a CreateContour callback has been specified, call it */
  contour->client_data = client_data;
  if (brep_create_contour_callback)
    contour->client_data = brep_create_contour_callback(contour);

  return contour;
}

/* various actions to be performed when a contour is specified completely. There
 * is no iteration over the edges since the edges may occur in two contours. For
 * edges, there are two constructor callbacks: CreateEdge, which is called when first
 * inserting the edge in a contour, and CloseEdge callback, which is called when the
 * edge is inserted a second time in a contour. */
void BrepCloseContour(BREP_CONTOUR *contour)
{
  /* if a CloseContour callback has been specified, call it */
  if (brep_close_contour_callback)
    contour->client_data = brep_close_contour_callback(contour);
}

/* various actions to be performed when a contour has been updated */
void BrepUpdateContour(BREP_CONTOUR *contour)
{
  /* if a UpdateContour callback has been specified, call it */
  if (brep_update_contour_callback)
    contour->client_data = brep_update_contour_callback(contour);

  /* BrepUpdateFace(contour->face); */
}

/* execute func for every wing in the contour */
void BrepContourIterateWings(BREP_CONTOUR *contour, void (*func)(BREP_WING *))
{
  BrepIterate((BREP_RING *)contour->wings, (void (*)(BREP_RING *))func);
}

void BrepContourIterateWings1A(BREP_CONTOUR *contour, void (*func)(BREP_WING *, void *), void *parm)
{
  BrepIterate1A((BREP_RING *)contour->wings, (void (*)(BREP_RING *, void *))func, parm);
}

void BrepContourIterateWings2A(BREP_CONTOUR *contour, void (*func)(BREP_WING *, void *, void *), void *parm1, void *parm2)
{
  BrepIterate2A((BREP_RING *)contour->wings, (void (*)(BREP_RING *, void *, void *))func, parm1, parm2);
}

/* execute func for every vertex in the contour */
static void do_vertex_func(BREP_WING *wing, void (*func)(BREP_VERTEX *))
{
  func(wing->vertex);
}

void BrepContourIterateVertices(BREP_CONTOUR *contour, void (*func)(BREP_VERTEX *))
{
  BrepContourIterateWings1A(contour, (void (*)(BREP_WING *, void *))do_vertex_func, (void *)func);
}

static void do_vertex_func_parm(BREP_WING *wing, void (*func)(BREP_VERTEX *, void *), void *parm)
{
  func(wing->vertex, parm);
}

void BrepContourIterateVertices1A(BREP_CONTOUR *contour, void (*func)(BREP_VERTEX *, void *), void *parm)
{
  BrepContourIterateWings2A(contour, (void (*)(BREP_WING *, void *, void *))do_vertex_func_parm, (void *)func, parm);
}

/* Remove a vertex from a (closed) contour, the wings containing the vertex are
 * removed as well (and their edges if not used in other contours as
 * well). The vertex itself will also be removed when it is no longer 
 * used. If it is not the only vertex in the contour, a new wing connecting
 * the neighbooring vertices is created, so the contour remains
 * a loop. */
void BrepContourRemoveVertex(BREP_CONTOUR *contour, BREP_VERTEX *vertex)
{
  BREP_WING *wing;

  /* check for empty contour */
  if (!contour->wings) {
    BrepError(contour->client_data, "BrepContourRemoveVertex", "empty contour");
    return;
  }

  /* if vertex is the only vertex in the contour, remove its self-edge. */
  if (contour->wings->next == contour->wings) {
    if (contour->wings->vertex != vertex) {
      BrepError(contour->client_data, "BrepContourRemoveVertex", "vertex not belonging to contour or contour is not closed");
      return;
    } else
      BrepDestroyWing(contour->wings);
  }

  /* else, replace the wing arriving at and the wing leaving the last occurence
   * of the vertex in the contour by a single wing connecting the 
   * previous and next vertex. */

  /* look for the last occurence of the vertex in the contour */
  wing = contour->wings->prev;
  while (wing->vertex != vertex && wing != contour->wings)
    wing = wing->prev;
  
  if (wing->vertex != vertex) 
    BrepError(contour->client_data, "BrepContourRemoveVertex", "vertex doesn't occur in contour");
  else
    BrepJoinWings(wing->prev, wing);
}

/* disconnects a contour from its containing face */
void BrepDisconnectContourFromFace(BREP_CONTOUR *contour)
{
  BREP_FACE *face = contour->face;

  if (face->outer_contour == contour) {	/* the contour is the first contour 
					 * in the face */
    if (contour->next == contour)	/* it is the only contour */
      face->outer_contour = (BREP_CONTOUR *)NULL;

    else {				/* the faces looses its outer contour */
      /*      BrepInfo(face->client_data, "BrepDisconnectContourFromFace", "outer contour disconnected from face"); */
      face->outer_contour = contour->next;
    }
  } 

  contour->next->prev = contour->prev;
  contour->prev->next = contour->next;

  contour->face = (BREP_FACE *)NULL;
}

/* destroys the wings in a contour */
static void BrepContourDestroyWings(BREP_WING *first)
{
  BREP_WING *wing, *prev;

  if (first) {
    for (wing = first->prev; wing != first; wing = prev) {
      prev = wing->prev;
      BrepDestroyWing(wing);
    }
    BrepDestroyWing(first);
  }
}

/* release all storage associated with a contour and its contours, 
 * including edges and vertices if not used in other contours as well */
void BrepDestroyContour(BREP_CONTOUR *contour)
{
  /* inverse actions performed in BrepCreateContour() in reverse order */

  /* disconnect the contour from the containing face */
  BrepDisconnectContourFromFace(contour);
  
  /* notify the user that the client_data can be disposed of */
  if (brep_destroy_contour_callback)
    brep_destroy_contour_callback(contour);

  /* destroy its wings */
  BrepContourDestroyWings(contour->wings);

  /* dispose of the BREP_CONTOUR structure itself */
  DISPOSECONTOUR(contour);  
}

/* wing1 and wing2 should be two wings belonging to the same contour.
 * This routine creates a new edge connecting the start vertex of wing1 
 * and wing2, splitting the contour in two. A pointer to the newly created 
 * wing, connecting the start vertex of wing1 to the start vertex of wing2, 
 * is returned. */
BREP_WING *BrepMakeEdgeSplitContour(BREP_WING *wing1, BREP_WING *wing2,
				    void *edge_data, void *contour_data)
{
  BREP_EDGE *edge;
  BREP_WING *winga, *wingb, *wing;

  /* test whether the wings belong to the same contour */
  if (wing1->contour != wing2->contour) {
    BrepError(wing1->edge->client_data, "BrepMakeEdgeSplitContour", "wings must belong to the same contour");
    return (BREP_WING *)NULL;
  }

  /* create an edge connecting the two start vertices */
  edge = BrepCreateEdge(wing1->vertex, wing2->vertex, edge_data);
  winga = &(edge->wing[0]);
  wingb = &(edge->wing[1]);
  
  /* connect the wings of the newly created edge to the contour,
   * effectively breaking the contour in two loops: one starting
   * with wing1 and one starting with wing2. */
  winga->prev = wing1->prev;
  winga->next = wing2;
  wingb->prev = wing2->prev;
  wingb->next = wing1;
  winga->prev->next = winga->next->prev = winga;
  wingb->prev->next = wingb->next->prev = wingb;
  winga->contour = wingb->contour = wing1->contour;

  /* let the old contour point to the wing1 loop */
  wing1->contour->wings = wing1;

  /* create a new contour for the wing2 loop */
  wing2->contour = BrepCreateContour(wing1->contour->face, contour_data);
  wing2->contour->wings = wing2;

  /* let all wings in the wing2 loop point to the new contour */
  wing = wing2;
  do {
    wing->contour = wing2->contour;
    wing = wing->next;
  } while (wing != wing2);

  /* connect the newly created wings to their start vertices */
  BrepConnectWingToVertex(winga);
  BrepConnectWingToVertex(wingb);

  /* notify the user that the edge has been connected into two
   * contours. */
  BrepCloseEdge(edge);

  /* notify the user that two new contours have been completely specified */
  /*  BrepUpdateContour(wing1->contour); */
  BrepCloseContour(wing2->contour);
  BrepCloseContour(wing1->contour);

  /* return a pointer to the wing connecting wing1->vertex to wing2->vertex */
  return winga;
}

/* Wing1 and wing2 are two wings belonging to a different contour. This
 * routine creates a new edge connecting the starting vertices of 
 * wing1 and wing2 and merges to two contours to one. A pointer to
 * the newly created wing, connecting the start vertex of wing1 to the
 * start vertex of wing2, is returned. */
BREP_WING *BrepMakeEdgeJoinContours(BREP_WING *wing1, BREP_WING *wing2, void *edge_data)
{
  BREP_EDGE *edge;
  BREP_WING *winga, *wingb, *wing;

  /* test whether the wings belong to a different contour */
  if (wing1->contour == wing2->contour) {
    BrepError(wing1->edge->client_data, "BrepMakeEdgeJoinContours", "wings must belong to a different contour");
    return (BREP_WING *)NULL;
  }

  /* test whether the wings belong to the same face */
  if (wing1->contour->face != wing2->contour->face) {
    BrepWarning(wing1->edge->client_data, "BrepMakeEdgeJoinContours", "joining contours from different faces");
    /* it's not an error ... */
  }

  /* create an edge connecting the two start vertices */
  edge = BrepCreateEdge(wing1->vertex, wing2->vertex, edge_data);
  winga = &(edge->wing[0]);
  wingb = &(edge->wing[1]);
  
  /* connect the wings of the newly created edge to the contour to which
   * wing1 belongs. */
  winga->prev = wing1->prev;
  winga->next = wing2;
  wingb->prev = wing2->prev;
  wingb->next = wing1;
  winga->prev->next = winga->next->prev = winga;
  wingb->prev->next = wingb->next->prev = wingb;

  /* unless the contour of wing2 is the outer contour of its face, "move" its
   * wings to the contour of wing1, and destroy the contour */
  if (wing2->contour != wing2->contour->face->outer_contour) {
    winga->contour = wingb->contour = wing1->contour;

    /* disconnect wing2->contour from its wings and destroy it */
    wing2->contour->wings = (BREP_WING *)NULL;
    BrepDestroyContour(wing2->contour);

    /* let all wings in the former contour of wing2 point to the contour of wing1 */
    for (wing = wing2; wing->contour != wing1->contour; wing = wing->next)
      wing->contour = wing1->contour;
  } else {
    winga->contour = wingb->contour = wing2->contour;

    /* disconnect wing1->contour from its wings and destroy it */
    wing1->contour->wings = (BREP_WING *)NULL;
    BrepDestroyContour(wing1->contour);

    /* let all wings in the former contour of wing2 point to the contour of wing1 */
    for (wing = wing1; wing->contour != wing2->contour; wing = wing->next)
      wing->contour = wing2->contour;
  }

  /* connect the newly created wings to their start vertices */
  BrepConnectWingToVertex(winga);
  BrepConnectWingToVertex(wingb);

  /* notify the user that the edge has been connected into two
   * contours. */
  BrepCloseEdge(edge);

  /* notify the user that the contour has been updated. */
  /* BrepUpdateContour(wing1->contour); */
  BrepCloseContour(winga->contour);

  /* return a pointer to the wing connecting wing1->vertex to wing2->vertex */
  return winga;
}

/* wing is a wing of an edge that is used in two different contours. This routine
 * deletes the edge to which the wing belongs, inserting the reaminder of 
 * the "other" contour sharing the edge into the contour to which the wing 
 * belongs. The other contour is destroyed and the first is "closed" again 
 * in order to notify that it changed. Returns the wing from the first contour 
 * that comes before the first wing moved from the second to the first, or
 * NULL if the first contour was a selfloop. In that case, the empty contour 
 * is deleted. In case of errors, wing is returned. Warning: if the face to which
 * the other contour belongs, contains holes, these holes should probably be moved
 * to the face to which 'wing' belongs.   */
BREP_WING *BrepDeleteEdgeJoinContours(BREP_WING *wing)
{
  BREP_WING *owing = BrepEdgeOtherWing(wing);
  BREP_WING *prev_wing = wing->prev, *next_wing = wing->next;
  BREP_CONTOUR *contour = wing->contour, *ocontour = owing->contour;

  if (!ocontour || ocontour == wing->contour) {
    BrepWarning(wing->edge->client_data, "BrepDeleteEdgeJoinContours", "Edge not enclosed in two different contours");
    return wing;
  }

  if (!prev_wing || !next_wing || !contour) {
    BrepError(wing->edge->client_data, "BrepDeleteEdgeJoinContours", "wing not properly connected in contour");
    return wing;
  }

  if (wing->prev == wing) {
    /* remove the selfloop from the other contour */
    if (owing->next == owing)
      BrepDestroyContour(ocontour);
    else
      BrepDestroyWing(owing);

    /* Delete the first contour (that contained only this wing). */
    BrepDestroyContour(contour);

    return (BREP_WING *)NULL;
  }

  /* disconnect and delete the wing from its contour */
  BrepDestroyWing(wing);

  if (owing->next != owing) {
    /* move all the wings of ocontour to contour, insert after prev_wing and before
     * next_wing */
    BREP_WING *wing1 = owing->next, *wing2 = owing->prev;
    owing->next = owing->prev = owing;
    ocontour->wings = owing;	/* will be destroyed soon ... */

    prev_wing->next = wing1;
    wing1->prev = prev_wing;

    next_wing->prev = wing2;
    wing2->next = next_wing;

    for (wing=wing1; wing!=wing2; wing=wing->next)
      wing->contour = contour;
    wing2->contour = contour;
  }

  /* destroy the other contour */
  BrepDestroyContour(ocontour);

  /* notify that the first contour has changed. */
  BrepCloseContour(contour);

  /* last wing before the first inserted from the other contour. */
  return prev_wing;
}

/* 'wing' is a wing of an edge that is used two times (in opposite direction) 
 * in the same contour, i.o.w. a seam. This routine deletes this edge.
 * If the wings happen to be consequtive wings, the wings are just deleted.
 * If these are the only two wings in the contour, the contour is deleted as 
 * well and a NULL wing pointer is returned. If they are not consequtive, the 
 * contour is split in two contours. The wing before 'wing' will remain in 
 * the original contour and will be returned. */
BREP_WING *BrepDeleteEdgeSplitContour(BREP_WING *wing, void *contour_data)
{
  BREP_WING *owing;

  if (!wing || !wing->contour || BrepEdgeOtherWing(wing)->contour != wing->contour) {
    BrepWarning(wing->edge->client_data, "BrepDeleteEdgeSplitContour", "argument should be a seam");
    return wing;
  }

  owing = BrepEdgeOtherWing(wing);
  if (owing == wing->next || owing == wing->prev) {
    /* consequtive wings, the seam is a notch or a slit */
    if (wing->next->next == wing) {
      /* they are the only two wings in the contour */
      BrepDestroyContour(wing->contour);
      return (BREP_WING *)NULL;
    } else {
      /* simply destroy the two wings. No new contour is split off. */
      BREP_WING *prevwing = (owing->next == wing) ? owing->prev : wing->prev;
      BrepDestroyWing(wing);
      BrepDestroyWing(owing);
      BrepCloseContour(prevwing->contour);
      return prevwing;
    }
  } else {
    BREP_CONTOUR *contour = wing->contour, *split_contour;
    BREP_WING *first1 = owing->next, *last1 = wing->prev,
              *first2 = wing->next, *last2 = owing->prev;

    /* disconnect wing and owing from the contour + destroy */
    BrepDestroyWing(wing);
    BrepDestroyWing(owing);

    /* close the loop first1 ... last1 */
    first1->prev = last1;
    last1->next = first1;
    contour->wings = last1;
    BrepCloseContour(contour);

    /* close the second loop 'first2' ... 'last2' and create a separate contour */
    split_contour = BrepCreateContour(contour->face, contour_data);
    first2->prev = last2;
    last2->next = first2;
    split_contour->wings = first2;
    ForAllWingsInContour(w, split_contour) {
      w->contour = split_contour;
    } EndForAll;
    BrepCloseContour(split_contour);

    return last1;
  }

  return wing;	/* we never get here .... */
}

/* Creates a slit, connecting the two specified vertices, 
 * in the given face. A slit is a new contour containing
 * two wings: one from v1 to v2 and the second
 * from v2 back to v1. A pointer to the newly created
 * wing, connecting v1 to v2, is returned. */
BREP_WING *BrepMakeSlit(BREP_FACE *face, BREP_VERTEX *v1, BREP_VERTEX *v2,
			void *edge_data, void *contour_data)
{
  BREP_EDGE *edge;
  BREP_WING *winga, *wingb;
  BREP_CONTOUR *contour;

  /* create a new contour in the face */
  contour = BrepCreateContour(face, contour_data);

  /* create an edge connecting the two vertices */
  edge = BrepCreateEdge(v1, v2, edge_data);
  winga = &(edge->wing[0]);
  wingb = &(edge->wing[1]);
  
  /* connect the wings of the newly created edge to the newly created
   * contour and to each other */
  winga->prev = winga->next = wingb;
  wingb->prev = wingb->next = winga;
  winga->contour = wingb->contour = contour;
  contour->wings = winga;

  /* connect the newly created wings to their start vertices */
  BrepConnectWingToVertex(winga);
  BrepConnectWingToVertex(wingb);

  /* notify the user that the edge has been connected into two
   * contours. */
  BrepCloseEdge(edge);

  /* notify the user that the new contour has been completely specified */
  BrepCloseContour(contour);

  /* return the wing connecting wing->vertex to vertex */
  return winga;
}

/* Creates a notch, connecting the start vertex of the wing with
 * the specified vertex, ans inserts it into the contour to
 * which the wing belongs. A pointer to the newly created
 * wing, from the start vertex of the specified wing to the
 * specified vertex, is returned. */
BREP_WING *BrepMakeNotch(BREP_WING *wing, BREP_VERTEX *vertex, void *edge_data)
{  
  BREP_EDGE *edge;
  BREP_WING *winga, *wingb;

  /* create an edge connecting the start vertex of the wing to the
   * specified vertex */
  edge = BrepCreateEdge(wing->vertex, vertex, edge_data);
  winga = &(edge->wing[0]);
  wingb = &(edge->wing[1]);
  
  /* connect the wings of the newly created edge to the contour */
  winga->prev = wing->prev;
  winga->next = wingb;
  wingb->prev = winga;
  wingb->next = wing;
  winga->prev->next = winga->next->prev = winga;
  wingb->prev->next = wingb->next->prev = wingb;
  winga->contour = wingb->contour = wing->contour;

  /* connect the newly created wings to their start vertices */
  BrepConnectWingToVertex(winga);
  BrepConnectWingToVertex(wingb);

  /* notify the user that the edge has been connected into two
   * contours. */
  BrepCloseEdge(edge);

  /* notify the user that the contour has been updated. */
  /* BrepUpdateContour(wing->contour); */

  /* return the wing connecting wing->vertex to vertex */
  return winga;
}

