/********************************************************************/
/*   STRIPE: converting a polygonal model to triangle strips    
     Francine Evans, 1996.
     SUNY @ Stony Brook
     Advisors: Steven Skiena and Amitabh Varshney
*/
/********************************************************************/

/*---------------------------------------------------------------------*/
/*   STRIPE: add.c
     This file contains the procedure code that will add information
     to our data structures.
*/
/*---------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "global.h"
#include "queue.h"
#include "polverts.h"
#include "triangulate.h"
#include "ties.h"
#include "outputex.h"
#include "options.h"
#include "local.h"

BOOL new_vertex(double difference, int id1,int id2,
                struct vert_struct *n)
{
  /*   Is the difference between id1 and id2 (2 normal vertices that
       mapped to the same vertex) greater than the
       threshold that was specified?
  */
  struct vert_struct *pn1,*pn2;
  double dot_product;
  double distance1, distance2,distance;
  double rad;
  char arg1[100];
  char arg2[100];

  pn1 = n + id1;
  pn2 = n + id2;
 
  dot_product = ((pn1->x) * (pn2->x)) +
                ((pn1->y) * (pn2->y)) +
                ((pn1->z) * (pn2->z));
  /*   Get the absolute value */
  if (dot_product < 0)
    dot_product = dot_product * -1;

  distance1 = sqrt( (pn1->x * pn1->x) +
                    (pn1->y * pn1->y) +
                    (pn1->z * pn1->z) );
  distance2 = sqrt( (pn2->x * pn2->x) +
                    (pn2->y * pn2->y) +
                    (pn2->z * pn2->z) );
  distance = distance1 * distance2;

  rad = acos((double)dot_product/(double)distance);
  /* convert to degrees */
  rad = (180 * rad)/PI;
    
  if ( rad <= difference)
    return FALSE;
     
  /*   double checking because of imprecision with floating
       point acos function
  */
  sprintf( arg1,"%.5f", rad );
  sprintf( arg2,"%.5f", difference );
  if ( strcmp( arg1, arg2 ) <=0 )
    return( FALSE );
  if ( rad <= difference)
    return FALSE;
  else 
    return TRUE;
}

BOOL Check_VN(int vertex,int normal, struct vert_added *added)
{
  /*   Check to see if we already added this vertex and normal */
  register int x,n;

  n = (added+vertex)->num;
  for (x = 0; x < n; x++)
  {
    if (*((added+vertex)->normal+x) == normal)
      return TRUE;
  }
  return FALSE;
}

BOOL norm_array(int id, int vertex, double normal_difference,
                struct vert_struct *n, int num_vert)
{
  static int last;
  static struct vert_added *added;
  register int x;
  static BOOL first = TRUE;

  if (first)
  {
    /*   This is the first time that we are in here, so we will allocate
         a structure that will save the vertices that we added, so that we
         do not add the same thing twice
    */
    first = FALSE;
    added = (struct vert_added *) malloc (sizeof (struct vert_added ) * num_vert);
    /*   The number of vertices added for each vertex must be initialized to
         zero
    */
    for (x = 0; x < num_vert; x++)
      (added+x)->num = 0;
  }

  if (vertex)
    /*   Set the pointer to the vertex, we will be calling again with the
         normal to fill it with
    */
    last = id;
  else
  {    
    /*   Fill the pointer with the id of the normal */
    if (*(vert_norms + last) == 0)
      *(vert_norms + last) = id;
    else if ((*(vert_norms + last) != id) && ((int)normal_difference != 360))
    {
      /*   difference is big enough, we need to create a new vertex */
      if (new_vertex(normal_difference,id,*(vert_norms + last),n))
      {
        /*   First check to see if we added this vertex and normal already */
        if (Check_VN(last,id,added))
          return FALSE;
        /*   OK, create the new vertex, and have its id = the number of vertices
             and its normal what we have here
        */
        vert_norms = realloc(vert_norms, sizeof(int) * (num_vert + 1));
        if (!vert_norms)
        {
          printf("Allocation error - aborting\n");
          exit(1);
        }
        *(vert_norms + num_vert) = id;
        /*   We created a new vertex, now put it in our added structure so
             we do not add the same thing twice
        */
        (added+last)->num = (added+last)->num + 1;
        if ((added+last)->num == 1)
        {
          /*   First time */
          (added+last)->normal =  (int *) malloc (sizeof (int ) * 1);
          *((added+last)->normal) =  id;
        }
        else
        {
          /*   Not the first time, reallocate space */
          (added+last)->normal = realloc((added+last)->normal,sizeof(int) * (added+last)->num);
          *((added+last)->normal+((added+last)->num-1)) = id;
        }
        return TRUE;
      }
    }
  }
  return FALSE;
}

void add_texture(int id,BOOL vertex)
{
  /*   Save the texture with its vertex for future use when outputting */
  static int last;

  if (vertex)
    last = id;
  else
    *(vert_texture+last) = id;
}

int	add_vert_id(int id, int	index_count)
{
  register int x;

  /*   Test if degenerate, if so do not add degenerate vertex */
  for (x = 1; x < index_count ; x++)
  {
    if (ids[x] == id)
      return 0;
  }
  ids[index_count] = id;
  return 1;
}

void	add_norm_id(int id, int index_count)
{
	norms[index_count] = id;
}

void AddNewFace(int ids[STRIP_MAX], int vert_count, int face_id, 
		int norms[STRIP_MAX])
{
  PF_FACES pfNode;
  int	*pTempInt;
  int *pnorms;
  F_EDGES **pTempVertptr;
  int	*pTempmarked, *pTempwalked;
  register int	y,count = 0;
	
	/*   Add a new face into our face data structure */

  pfNode = (PF_FACES) malloc(sizeof(F_FACES) );
  if ( pfNode )
  {
    pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) );
    pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) );
    pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); 
		pfNode->marked  = (int*)malloc(sizeof(int) * (vert_count));
		pfNode->walked = (int*)malloc(sizeof(int) * (vert_count));
	}
	pTempInt =pfNode->pPolygon;
	pnorms = pfNode->pNorms;
  pTempmarked = pfNode->marked;
	pTempwalked = pfNode->walked;
	pTempVertptr = pfNode->VertandId;
	pfNode->nPolSize = vert_count;
	pfNode->seen = -1;
  pfNode->seen2 = -1;
	for (y=1;y<=vert_count;y++)
	{
		*(pTempInt + count) = ids[y];
		*(pnorms + count) = norms[y];
    *(pTempmarked + count) = FALSE;
	  *(pTempwalked + count) =  -1;
		*(pTempVertptr+count) = NULL;
		count++;
	}
	AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode);
}	

	
void CopyFace(int ids[STRIP_MAX], int vert_count, int face_id, 
	      int norms[STRIP_MAX])
{
  PF_FACES pfNode;
  int	*pTempInt;
  int *pnorms;
  F_EDGES **pTempVertptr;
  int	*pTempmarked, *pTempwalked;
  register int	y,count = 0;
	
	/*   Copy a face node into a new node, used after the global algorithm
       is run, so that we can save whatever is left into a new structure
  */
     
  pfNode = (PF_FACES) malloc(sizeof(F_FACES) );
  if ( pfNode )
  {
    pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) );
    pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) );
    pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); 
		pfNode->marked  = (int*)malloc(sizeof(int) * (vert_count));
		pfNode->walked = (int*)malloc(sizeof(int) * (vert_count));
	}
	pTempInt =pfNode->pPolygon;
	pnorms = pfNode->pNorms;
  pTempmarked = pfNode->marked;
	pTempwalked = pfNode->walked;
	pTempVertptr = pfNode->VertandId;
	pfNode->nPolSize = vert_count;
	pfNode->seen = -1;
  pfNode->seen2 = -1;
	for (y=0;y<vert_count;y++)
	{
		*(pTempInt + count) = ids[y];
		*(pnorms + count) = norms[y];
    *(pTempmarked + count) = FALSE;
		*(pTempwalked + count) =  -1;
		*(pTempVertptr+count) = NULL;
		count++;
	}
	AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode);
}	
	
void Add_Edge(int v1,int v2)
{
  PF_EDGES temp  = NULL;
  ListHead *pListHead;
  BOOL flag = TRUE;
  register int t,count = 0;
	
	/*   Add a new edge into the edge data structure */
  if (v1 > v2)
	{
		t  = v1;
		v1 = v2;
		v2 = t;
	}
	
  pListHead = PolEdges[v1];
	temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count);
	if (temp == NULL)
  {
    printf("Have the wrong edge \n:");
    exit(1);
  }
	
	while (flag)
	{
		if (v2 == temp->edge[0])
      return;
    else
    	temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,++count);
	}                	
}

void Add_AdjEdge(int v1,int v2,int fnum,int index1 )
{
  PF_EDGES temp  = NULL;
  PF_FACES temp2 = NULL;
  PF_EDGES pfNode;
  ListHead *pListHead;
  ListHead *pListFace;
  BOOL 	flag = TRUE;
  register int	count = 0;
  register int	t,v3 = -1;
	
	if (v1 > v2)
	{
		t  = v1;
		v1 = v2;
		v2 = t;
	}
	pListFace  = PolFaces[fnum];
	temp2 = (PF_FACES) PeekList(pListFace,LISTHEAD,0);
	pListHead = PolEdges[v1];
	temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count);
	if (temp == NULL)
		flag = FALSE;
	count++;
	while (flag)
	{
		if (v2 == temp->edge[0])
		{
      /*   If greater than 2 polygons adjacent to an edge, then we will
           only save the first 2 that we found. We will have a small performance
           hit, but this does not happen often.
      */
      if (temp->edge[2] == -1)
        temp->edge[2] = fnum;
      else
        v3 = temp->edge[2];
      flag = FALSE;
		}
		else
		{
			temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count);
			count++;
			if (temp == NULL)
				flag = FALSE;
		}
	}
                
	/*   Did not find it */
  if (temp == NULL)
	{
		pfNode = (PF_EDGES) malloc(sizeof(F_EDGES) );
    if ( pfNode )
    {
      pfNode->edge[0] = v2;
			pfNode->edge[1] = fnum;
	    pfNode->edge[2] =  v3;
			AddTail( PolEdges[v1], (PLISTINFO) pfNode );
    }
		else
    {
      printf("Out of memory!\n");
      exit(1);
    }
		
    *(temp2->VertandId+index1) = pfNode;
	}
	else
	  *(temp2->VertandId+index1) =  temp;
}