/* stlmodelexporter.cc			letzte Aenderung: 9.1.2021 */
#define VERSION "Version 0.0"
/*

 Modell im STL-Format einlesen und als BMF-Datei speichern

History:
9.1.2021	Erstellung (RP)
*/

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

typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;

/************************* eigene Klassen *****************************/
class Dreieck {
public:
 float normalen[3];
 float vertex1[3];
 float vertex2[3];
 float vertex3[3];
 uint16 attribut;
};

/************************* Vordeklarationen ***************************/
void unterprog(FILE *fp1,FILE *fp2);
void save_bmf(FILE *fp,Dreieck *dreiecke,uint32 numDreiecke);

/**************** Routinen zur Parameterauswertung ********************/
#define MAXARG 2
static char argflag[128];
void setargflags(const char *s)
{
 int c;
 while((c= *s++)!=0)
  {if(c>='a' && c<='z')  c -= 'a'-'A';
   argflag[c&127]=1;
  }
}

/*************************** Kleinkram ********************************/
int getline(FILE *fp,char *s,int lim)
{		/* liest eine Textzeile oder maximal lim Zeichen */
		/* und ersetzt den Zeilentrenner durch 0         */
 int c=0;
 while(--lim && (c=getc(fp))!=EOF && c!='\n')
	*s++ = c;
 *s='\0';
 return (c!=EOF);	/* TRUE wenn erfolgreich, FALSE wenn Fileende */
}

/************************* Hauptprogramm ******************************/
int main(int argc,char *argv[])
{
 char quellname[80],zielname[80];
 FILE *fp1,*fp2;
 int i,j,c;
 quellname[0]=zielname[0]=0;
 if(argc<=0)
  {/* es wurde von WorkBench (GUI, Desktop, ...) gestartet */
   j=0;
  }
 else
  {/* es wurde von Shell (Terminal, Konsole, ...) gestartet */
   for(j=0,i=1;i<argc;i++)
	{if((c= *argv[i])=='-' || c=='?') setargflags(argv[i]);
	 else	{if(++j==1) strcpy(quellname,argv[i]);
		 else if(j==2) strcpy(zielname,argv[i]);
	}	}
  }
 if(argflag['?'] || j<1 || j>MAXARG)
	{printf("astart  %s\n",VERSION);
	 printf("Anwendung: astart Quelle.stl [Ziel.bmf]\n");
	 exit(0);
	}
 if(*zielname==0)
  {
   for(i=0; (c=quellname[i])!=0 && c!='.'; i++)
    {zielname[i]=quellname[i];}
   zielname[i++]='.';
   zielname[i++]='b';
   zielname[i++]='m';
   zielname[i++]='f';
   zielname[i]=0;
   printf("Zieldatei: \"%s\"\n",zielname);//test
  }
 if((fp1=fopen(quellname,"r"))==NULL)
   printf("Datei '%s' nicht vorhanden\n",quellname);
 if((fp2=fopen(zielname,"wb"))==NULL)
   printf("Konnte Datei \"%s\" nicht erstellen\n",zielname);
 if(fp1!=NULL && fp2!=NULL) unterprog(fp1,fp2);
 if(fp1) fclose(fp1);
 if(fp2) fclose(fp2);
 return 0;
}/* ende von main */

uint16 input_uint16(FILE *fp)
{
 uint16 c;
 c = getc(fp) & 0xFF;
 c += ((getc(fp)&0xFF)<<8);
 return c;
}

uint32 input_uint32(FILE *fp)
{
 uint32 c;
 c = getc(fp) & 0xFF;
 c += ((getc(fp)&0xFF)<<8);
 c += ((getc(fp)&0xFF)<<16);
 c += ((getc(fp)&0xFF)<<24);
 return c;
}

float input_float(FILE *fp)
{
 union {float f; uint32 c;} fc;
 fc.c = getc(fp) & 0xFF;
 fc.c += ((getc(fp)&0xFF)<<8);
 fc.c += ((getc(fp)&0xFF)<<16);
 fc.c += ((getc(fp)&0xFF)<<24);
 return fc.f;
}

void input_vektor(FILE *fp,float* vektor)
{
 for(int i=0;i<3;i++)
  vektor[i] = input_float(fp);
}

void unterprog(FILE *fp1,FILE *fp2)
{
 int c;
 uint32 i;
 char kopfdaten[80];
 printf("STL-Datei wird in BMF-Datei konvertiert....\n");//test
 for(i=0;i<5;i++) kopfdaten[i]=getc(fp1);
 bool asciiflag=(strncmp(kopfdaten,"solid",5)==0);
 Dreieck *dreiecke=NULL;
 uint32 numDreiecke=0;
 if(asciiflag)
  {
   //STL-Datei im ASCII-Format einlesen:
   for(;i<80-1 && (c=kopfdaten[i])!=0 && c!='\n'; i++) kopfdaten[i]=getc(fp1);
   kopfdaten[i]=0;
   char zeile[200], *pz;
   i=0;
   int vnr=0;
   while(getline(fp1,zeile,200))
    {
     float x,y,z;
     for(pz=zeile; *pz==' '; pz++) {} //Leerzeichen ueberlesen
     if(strncmp(pz,"endsolid",8)==0) break;
     if(strncmp(pz,"facet normal",12)==0)
      {
       sscanf(&pz[12],"%f %f %f",&x,&y,&z);
       dreiecke[i].normalen[0]=x;
       dreiecke[i].normalen[1]=y;
       dreiecke[i].normalen[2]=z;
      }
     else if(strncmp(pz,"endfacet",8)==0)
      {
       i++;
       vnr=0;
      }
     //else if(strncmp(pz,"outer loop",10)==0) {}
     else if(strncmp(pz,"endloop",7)==0)
      {
       //TODO: ueberpruefen ob ev. mehrere "outer loop" .. "endloop" vorkommen
       numDreiecke++;
       vnr=0;
      }
     else if(strncmp(pz,"vertex",6)==0)
      {
       sscanf(&pz[6],"%f %f %f",&x,&y,&z);
       if(++vnr==1)
	{
	 dreiecke[i].vertex1[0]=x;
	 dreiecke[i].vertex1[1]=y;
	 dreiecke[i].vertex1[2]=z;
	}
       else if(vnr==2)
	{
	 dreiecke[i].vertex2[0]=x;
	 dreiecke[i].vertex2[1]=y;
	 dreiecke[i].vertex2[2]=z;
	}
       else if(vnr==3)
	{
	 dreiecke[i].vertex3[0]=x;
	 dreiecke[i].vertex3[1]=y;
	 dreiecke[i].vertex3[2]=z;
	}
       else printf("Fehler: zu viele Vertexe pro Dreieck\n");//test
      }
    }
  }
 else
  {
   //Binaere STL-Datei einlesen:
   for(;i<80;i++) kopfdaten[i]=getc(fp1);
   numDreiecke = input_uint32(fp1);
   dreiecke = new Dreieck[numDreiecke];
   for(i=0;i<numDreiecke;i++)
    {
     input_vektor(fp1,dreiecke[i].normalen);
     input_vektor(fp1,dreiecke[i].vertex1);
     input_vektor(fp1,dreiecke[i].vertex2);
     input_vektor(fp1,dreiecke[i].vertex3);
     dreiecke[i].attribut = input_uint16(fp1);
    }
  }
 if(dreiecke==NULL) {printf("Fehler: keine Dreiecke eingelesen\n");}
 else
  {
   save_bmf(fp2,dreiecke,numDreiecke);
   delete[] dreiecke;
  }
}

/*********************** Neue Datei schreiben *************************/
struct Position {
 float x,y,z;
};

void output_uint32(FILE *fp, uint32 u)
{
 for(int i=0;i<4;i++)
  {
   fputc(u&0xFF, fp);
   u >>= 8;
  }
}

void output_uint64(FILE *fp, uint64 u)
{
 for(int i=0;i<8;i++)
  {
   fputc(u&0xFF, fp);
   u >>= 8;
  }
}

void output_float(FILE *fp, float fx)
{
 union {float f; uint32 c;} fc;
 fc.f = fx;
 fputc(fc.c&0xFF, fp);
 fputc((fc.c>>8)&0xFF, fp);
 fputc((fc.c>>16)&0xFF, fp);
 fputc((fc.c>>24)&0xFF, fp);
}

Position get_vertex(float *p)
{
 Position vertex;
 vertex.x = p[0];
 vertex.y = p[1];
 vertex.z = p[2];
 return vertex;
}

uint32 vertex_suchen(uint32 imax, std::vector<Position>& positions, Position& vertex)
{
 for(uint32 i=0;i<imax;i++)
  {
   if(positions[i].x == vertex.x
      && positions[i].y == vertex.y
      && positions[i].z == vertex.z)
    //TODO: Rundungsfehler ausgleichen
    return i+1;
  }
 return 0;
}

void save_bmf(FILE *fp,Dreieck *dreiecke,uint32 numDreiecke)
{
 uint32 i, index, index1, index2, index3;
 uint32 next_index=1;
 uint64 numVertices=0;
 uint64 numIndices=0;
 std::vector<Position> positions;
 std::vector<uint32> indices;
 Position vertex1,vertex2,vertex3;
 printf("numDreiecke=%d\n",numDreiecke);//test
 for(i=0;i<numDreiecke;i++)
  {
   vertex1 = get_vertex(dreiecke[i].vertex1);
   index1 = vertex_suchen(numVertices, positions, vertex1);
   if(index1==0)
    {
     positions.push_back(vertex1); numVertices++;
     index1 = next_index++;
    }
   indices.push_back(index1-1);
   vertex2 = get_vertex(dreiecke[i].vertex2);
   index2 = vertex_suchen(numVertices, positions, vertex2);
   if(index2==0)
    {
     positions.push_back(vertex2); numVertices++;
     index2 = next_index++;
    }
   indices.push_back(index2-1);
   vertex3 = get_vertex(dreiecke[i].vertex3);
   index3 = vertex_suchen(numVertices, positions, vertex3);
   if(index3==0)
    {
     positions.push_back(vertex3); numVertices++;
     index3 = next_index++;
    }
   indices.push_back(index3-1);
   numIndices += 3;
  }
 //numVertices = positions.size(); //waere etwas einfacher
 //numIndices = indices.size(); //waere etwas einfacher
 printf("numVertices=%ld\n",numVertices);//test
 printf("numIndices=%ld\n",numIndices);//test
 output_uint64(fp,numVertices);
 output_uint64(fp,numIndices);
 for(uint64 i=0; i<numVertices; i++)
  {
   output_float(fp, positions[i].x);
   output_float(fp, positions[i].y);
   output_float(fp, positions[i].z);
  }
 for(uint64 i=0; i<numIndices; i++)
  {
   output_uint32(fp, indices[i]);
  }
}
