/*                                                         */
/* prepobj.c by Gerhard.Hippmann@dlr.de                    */
/* prepobj.b <filename>.obj [eps] prepares Wavefront-File: */
/* - removes duplicate vertices [with snap distance eps]   */
/* - removes superfluous vertices                          */
/* - transforms quadrangles to triangles                   */
/*                                                         */
/* gcc -o prepobj.b prepobj.c                              */
/*                                                         */
/* 08-10-21 fixed reading of triangles                     */
/* 08-10-23 added elimination of duplicate vertices        */
/* 09-03-23 improved Wavefront compability                 */
/*                                                         */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>


int main( int argc, char *argv[] );


int main( int narg, char *arg[] )
{
   int  found;
   int  ix;
   int  idv;
   int  overt;
   int  nvert;
   int  nface;
   int  ntri;
   int  nquad;
   int  nvd;
   int  first;
   int  form;
   int  idum;
   int  v1, v2, v3, v4;
   char  c;
   char  d;
   char  ch;
   char  objfile[128];
   char  line[128];
   float  x, y, z;
   float  eps;
   float  epsqr;
   float  disqr;
   FILE  *file;

   struct  tvert
   {
      float  x;
      float  y;
      float  z;
      struct tvert  *next;
   };
   struct  tface
   {
      int  v1;
      int  v2;
      int  v3;
      struct tface  *next;
   };

   struct tvert  *pvert;
   struct tvert  *pv;
   struct tvert  *pvn;
   struct tvert  *pvx;
   struct tvert  *pvd;
   struct tvert  *pvdp;
   struct tface  *pface;
   struct tface  *pf;
   struct tface  *pfn;
   struct tface  *pfc;

   if ( narg == 2 )
   {
      strcpy( objfile, arg[1] );
      eps = 0.0e0;
   }
   else if ( narg == 3 )
   {
      strcpy( objfile, arg[1] );
      narg = sscanf( arg[2], "%e", &eps );
   }
   else
   {
      printf( "ERROR: Wrong Number of Arguments narg = %d\n", narg );
      return 1;
   }

   strcpy( objfile, arg[1] );

   file = fopen( objfile, "rt" );

   if ( file == NULL )
   {
      printf( "** ERROR: Cannot open File: %s\n", objfile );
      return 1;
   }

   printf( "\n" );
   printf( " * Read Input File: %s\n", objfile );

   nvert = 0;
   nface = 0;
   ntri = 0;
   nquad = 0;
   form = 0;

   pvert = NULL;
   pface = NULL;

   ix = 0;
   do
   {
      /* Read next Character */
      ch = getc( file );

      if ( ch == '\n' || ch == EOF || ix > 128 )
      {
         /* Process Line */
         line[ix] = '\0';
         narg = sscanf( line, "%c%c", &c, &d );
         if ( narg == 2 )
         {
            if ( c=='v' && d==' ' )
            {
               narg = sscanf( line, "%c %e %e %e", &c, &x, &y, &z );
               if ( narg == 4 )
               {
                  nvert++;
                  pvn = malloc( sizeof( struct tvert ) );
                  pvn->x = x;
                  pvn->y = y;
                  pvn->z = z;
                  pvn->next = NULL;
                  if ( pvert == NULL )
                     pvert = pvn;
                  else
                     pv->next = pvn;
                  pv = pvn;
               }
            }
            else if ( c=='f' && d==' ' )
            {
               if ( form == 0 )
               {
                  narg = sscanf( line, "%c %d/%d/%d" );
                  if ( narg == 4 )
                     form = 4;
                  else
                  {
                     narg = sscanf( line, "%c %d//%d" );
                     if ( narg == 3 )
                        form = 3;
                     else
                     {
                        narg = sscanf( line, "%c %d/%d" );
                        if ( narg == 3 )
                           form = 2;
                        else
                           form = 1;
                     }
                  }
               }
               if ( form == 4 )
               {
                  narg = sscanf( line, "%c %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", &c, &v1,&idum,&idum, &v2,&idum,&idum, &v3,&idum,&idum, &v4,&idum,&idum );
                  if ( narg == 10 )
                     narg = 4;
                  else if ( narg == 13 )
                     narg = 5;
                  else
                     narg = 0;
               }
               else if ( form == 3 )
               {
                  narg = sscanf( line, "%c %d//%d %d//%d %d//%d %d//%d", &c, &v1,&idum, &v2,&idum, &v3,&idum, &v4,&idum );
                  if ( narg == 7 )
                     narg = 4;
                  else if ( narg == 9 )
                     narg = 5;
                  else
                     narg = 0;
               }
               else if ( form == 2 )
               {
                  narg = sscanf( line, "%c %d/%d %d/%d %d/%d %d/%d", &c, &v1,&idum, &v2,&idum, &v3,&idum, &v4,&idum );
                  if ( narg == 7 )
                     narg = 4;
                  else if ( narg == 9 )
                     narg = 5;
                  else
                     narg = 0;
               }
               else
                  narg = sscanf( line, "%c %d %d %d %d", &c, &v1, &v2, &v3, &v4 );
               if ( narg == 4 )
                  ntri++;
               else if ( narg == 5 )
                  nquad++;
               if ( narg == 4 || narg == 5 )
               {
                  nface++;
                  pfn = malloc( sizeof( struct tface ) );
                  pfn->v1 = v1;
                  pfn->v2 = v2;
                  pfn->v3 = v3;
                  pfn->next = NULL;
                  if ( pface == NULL )
                     pface = pfn;
                  else
                     pf->next = pfn;
                  pf = pfn;
               }
               if ( narg == 5 )
               {
                  nface++;
                  pfn = malloc( sizeof( struct tface ) );
                  pfn->v1 = v1;
                  pfn->v2 = v3;
                  pfn->v3 = v4;
                  pfn->next = NULL;
                  if ( pface == NULL )
                     pface = pfn;
                  else
                     pf->next = pfn;
                  pf = pfn;
               }
            }
         }
         ix = 0;
      }
      else
         /* Apend next Character to line */
         line[ix++] = ch;
   }
   while ( ch != EOF );

   fclose( file );

   printf( "   Number of Vertices:    %d\n", nvert );
   printf( "   Number of Triangles:   %d\n", ntri );
   printf( "   Number of Quadrangles: %d\n", nquad );
   printf( "\n" );

   printf( " * Delete non-referenced Vertices\n" );

   overt = 0;
   nvert = 0;
   pv = pvert;
   pvert = NULL;
   while ( pv != NULL )
   {
      pvn = pv->next;
      ntri = 0;
      overt++;
      first = 1;
      for ( pf = pface ; pf != NULL ; pf = pf->next )
      {
         ntri++;
         if ( pf->v1 == overt || pf->v2 == overt || pf->v3 == overt )
         {
            if ( first )
            {
               if ( pvert == NULL )
                  pvert = pv;
               else
                  pvx->next = pv;
               pv->next = NULL;
               pvx = pv;
               nvert++;
               first = 0;
            }
            if ( pf->v1 == overt )
               pf->v1 = nvert;
            if ( pf->v2 == overt )
               pf->v2 = nvert;
            if ( pf->v3 == overt )
               pf->v3 = nvert;
         }
      }
      if ( first )
         free( pv );
      pv = pvn;
   }

   printf( "   Number of Vertices:    %d\n", nvert );
   printf( "   Number of Triangles:   %d\n", ntri );
   printf( "\n" );

   printf( " * Remove duplicate Vertices with eps = %g\n", eps );

   nvert = 0;
   epsqr = eps*eps;
   for ( pv = pvert ; pv != NULL ; pv = pv->next )
   {
      nvert++;
      nvd = nvert;
      pvdp = pv;
      for ( pvd = pv->next ; pvd != NULL ; pvd = pvd->next )
      {
         nvd++;
         disqr = (pv->x - pvd->x)*(pv->x - pvd->x) + (pv->y - pvd->y)*(pv->y - pvd->y) + (pv->z - pvd->z)*(pv->z - pvd->z);
         if ( disqr <= epsqr )
         {
            if ( pvd->next == NULL )
               pvdp->next = NULL;
            else
               pvdp->next = pvd->next;
            free( pvd );
            pvd = pvdp;
            for ( pf = pface ; pf != NULL ; pf = pf->next )
            {
               if ( pf->v1 == nvd )
                  pf->v1 = nvert;
               else if ( pf->v1 > nvd )
                  pf->v1 = pf->v1 - 1;
               if ( pf->v2 == nvd )
                  pf->v2 = nvert;
               else if ( pf->v2 > nvd )
                  pf->v2 = pf->v2 - 1;
               if ( pf->v3 == nvd )
                  pf->v3 = nvert;
               else if ( pf->v3 > nvd )
                  pf->v3 = pf->v3 - 1;
            }
            nvd--;
         }
         else
            pvdp = pvd;
      }
   }

   printf( "   Number of Vertices:    %d\n", nvert );
   printf( "   Number of Triangles:   %d\n", ntri );
   printf( "\n" );

   printf( " * Delete duplicate Faces\n" );

   ntri = 0;
   for ( pf = pface ; pf != NULL ; pf = pf->next )
   {
      for ( pfc = pf ; pfc->next != NULL ; pfc = pfc->next )
      {
         pfn = pfc->next;
         if ( pfn->v1 == pf->v1 && pfn->v2 == pf->v2 && pfn->v3 == pf->v3 ||
              pfn->v1 == pf->v2 && pfn->v2 == pf->v3 && pfn->v3 == pf->v1 ||
              pfn->v1 == pf->v3 && pfn->v2 == pf->v1 && pfn->v3 == pf->v2 )
         {
            pfc->next = pfn->next;
            free( pfn );
         }
      }
      ntri++;
   }

   printf( "   Number of Vertices:    %d\n", nvert );
   printf( "   Number of Triangles:   %d\n", ntri );
   printf( "\n" );

   strcpy( line, "mv " );
   strcat( line, objfile );
   strcat( line, " " );
   strcat( line, objfile );
   strcat( line, ".bak" );

   printf(" * %s\n",&line);

   system( line );

   file = fopen( objfile, "wt" );

   if ( file == NULL )
   {
      printf( "** ERROR: Cannot open Output File: %s\n", objfile );
      return 1;
   }

   printf( "\n" );
   printf( " * Write Output File: %s\n", objfile );

   /* Write Header */
   fprintf( file, "# Number of Vertices = %d\n", nvert );
   fprintf( file, "# Number of Faces    = %d\n", ntri );
   /* Write Vertices */
   for ( pv=pvert; pv!=NULL; pv=pv->next )
      fprintf( file, "v %13e %13e %13e\n", pv->x, pv->y, pv->z );
   /* Write Faces */
   for ( pf=pface; pf!=NULL; pf=pf->next )
      fprintf( file, "f %d %d %d\n", pf->v1, pf->v2, pf->v3 );

   fclose( file );

   printf( "\n" );

   return 0;
}
