/*****************************************************************************
*   Program to draw 3D object as wireframe after removing the hidden lines.  *
* This porgram works in object space, and if redirect stdout to a file, dump *
* the visible polylines into it instead of drawing them on current device.   *
* This may be used to display the results on any device (a plotter !?) later.*
*									     *
* Options:								     *
* 1. -b : Delete back facing polygons.					     *
* 2. -e #Edges : If the edges are order is specific way (like in DrawFn3D)   *
*         and only the k first edges of each polygons are to be displayed    *
*	  use this option as -e k.					     *
* 3. -i : Internal edges. IRIT solid modeller may generate edges, which one  *
*	  may not want to see (default). -i will draw all of them.	     *
* 4. -m : More flag, to print more imformation on input/errors.		     *
* 5. -o Objects : List of objects to be displayed from the input data files. *
*	  This option should not be last and Object list terminates with     *
*	  next option (- as first char). ALL objects are displayed otherwise.*
* 6. -v ViewFile : Optional view to start with.				     *
* 7. -z : Print current version, and some helpfull data.		     *
*									     *
* Note some of those options may be permanently set to a different default   *
* using the configuration file "Poly3D-H.cfg"				     *
*									     *
* Usage: Poly3D-H [-b] [-i] [m] [-e #Edges] [-o Objects] [-v ViewFile] [-z]  *
*								       Files *
*									     *
* Written by:  Gershon Elber				Ver 2.0, Sep. 1989   *
*****************************************************************************/

#ifdef __MSDOS__
#include <stdlib.h>
#include <conio.h>
#include <io.h>
#include <dos.h>
#include <alloc.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <math.h>
#include <time.h>
#include "program.h"
#include "getarg.h"
#include "genmat.h"
#include "parser.h"
#include "config.h"
#include "ctrl-brk.h"

#ifdef __MSDOS__
/* This huge stack is mainly from second phase - the segment intersections   */
/* which may cause recursive calls - a lot...				     */
extern unsigned int _stklen = 32766;
#endif /* __MSDOS__ */

#ifdef SYSV
static char *VersionStr =
	"Poly3D-H	version 2.0,	Gershon Elber,\n\
	(C) Copyright 1989 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Poly3D-H	version 2.0,	Gershon Elber,	"
	__DATE__ ",  " __TIME__ "\n"
	"(C) Copyright 1989 Gershon Elber, Non commercial use only.";
#endif /* SYSV */

static char *CtrlStr =
	"poly3d-h b%- m%- i%- e%-#Edges!d o%-Objects!*s v%-ViewFile!s z%- DFiles!*s";

int NumOfPolygons = 0;		      /* Total number of polygons to handle. */
MatrixType GlblViewMat;				  /* Current view of object. */

/* Data structures used by the hidden line modules: */
int EdgeCount = 0;
struct EdgeStruct *EdgeHashTable[EDGE_HASH_TABLE_SIZE];
struct PolygonStruct *PolyHashTable[POLY_HASH_TABLE_SIZE];

static long SaveTotalTime;

/* The following are setable variables (via configuration file poly3d-h.cfg).*/
int MoreFlag = FALSE, NumEdges = 0, ViewFlag = FALSE, BackFacingFlag = FALSE,
    InternalFlag;

static ConfigStruct SetUp[] =
{ { "Internal",		(VoidPtr) &InternalFlag,	SU_BOOLEAN_TYPE },
  { "More",		(VoidPtr) &MoreFlag,		SU_BOOLEAN_TYPE },
  { "NumOfEdges",	(VoidPtr) &NumEdges,		SU_INTEGER_TYPE } };

#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

static void MainGetViewFile(char *ViewFileName);
static FileDescription **MainGetDataFiles(char **DataFileNames,
							int NumOfDataFiles);
static void FreeBinTree(struct BinTree *Pbin);

/*****************************************************************************
* Main routine - Read Parameter	line and do what you need...		     *
*****************************************************************************/
void main(int argc, char **argv)
{
    int EdgesFlag = FALSE, ObjFlag = FALSE, NumObjs = 0, VerFlag = FALSE,
	NumFiles = FALSE, Error;
    char **Objects = NULL, *ViewName = NULL, **FileNames = NULL;
    FileDescription **FD;

    SaveTotalTime = time(NULL);			      /* Save starting time. */
#ifdef __MSDOS__
    ctrlbrk((int (*)()) MyExit);	       /* Kill this program if ^C... */
#endif /* __MSDOS_ */
    SetUpHardErr();	    /* Set up hardware error trap routine (int 24h). */

    Config("poly3d-h", SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    if ((Error = GAGetArgs (argc, argv, CtrlStr,
		&BackFacingFlag, &MoreFlag, &InternalFlag, &EdgesFlag,
		&NumEdges, &ObjFlag, &NumObjs, &Objects,
		&ViewFlag, &ViewName, &VerFlag,	&NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	MyExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	ConfigPrint(SetUp, NUM_SET_UP);
	MyExit(0);
    }

    if (!NumFiles) {
	fprintf(stderr, "No data file names were given, exit\n");
	GAPrintHowTo(CtrlStr);
	MyExit(1);
    }

    MainGetViewFile(ViewName);

    /* Get the data files: */
    FD = MainGetDataFiles(FileNames, NumFiles);

    /* Prepare data structures to be able to decide on visibility: */
    PrepareViewData(FD, NumObjs, Objects);

    OutVisibleEdges();		       /* Scan all sub-edges output visible. */

    MyExit(0);
}

/*****************************************************************************
* Main routine to read the view	description file, into GlblViewMat:	     *
*****************************************************************************/
static void MainGetViewFile(char *ViewFileName)
{
    FILE *f;

    if (ViewFlag && (f = fopen(ViewFileName, "rt")) == NULL) {
	fprintf(stderr, "Can't open view file %s\n", ViewFileName);
	MyExit(1);
    }

    /* Get view data if ViewFlag, otherwise set default view (isometric): */
    GetViewFile(f, ViewFlag);

    if (ViewFlag) fclose(f);				  /* Close the file. */
}

/*****************************************************************************
* Main routine to read the data	description files:			     *
* Returns pointer to pointers on FileDescription structures (one per file).  *
*****************************************************************************/
static FileDescription **MainGetDataFiles(char **DataFileNames,
							int NumOfDataFiles)
{
    int i;
    long SaveTime = time(NULL);
    FILE *f;
    FileDescription **FDbase, **FDinc;  /* Used to save memory pointers loc. */

    NumOfPolygons = 0;
    fprintf(stderr, "\nPass 1, Polys =      ");

    FDbase = FDinc = (FileDescription **)    /* Allocate the pointers block. */
	 MyMalloc((unsigned) sizeof(FileDescription *) * (NumOfDataFiles + 1));

    for	(i=0; i<NumOfDataFiles;	i++) {
	if (MoreFlag) {
	    fprintf(stderr, "\rReading data file %s", *DataFileNames);
	    fprintf(stderr, "\nPass 1, Polys =      ");
	}
	f = fopen(*DataFileNames, "rt");		   /* Open the file. */
	if (!f)
	{
	    fprintf(stderr, "Can't open data file %s\n", *DataFileNames);
	    MyExit(1);
	}

	*FDinc = GetDataFile(f);		       /* Get the data file. */
	FreeBinTree((*FDinc) ->	VertexPointer); /* We dont need these trees. */
	FreeBinTree((*FDinc) ->	PolygonPointer);/* - objects are all loaded. */
	FreeBinTree((*FDinc) ->	PolylinePointer);
	fprintf(stderr, " \b");		  /* Erase the FreeBinTree marker... */
	(*FDinc) -> VertexPointer =
	(*FDinc) -> PolygonPointer =
	(*FDinc) -> PolylinePointer = NULL;
	*FDinc++;

	fclose(f);					  /* Close the file. */

	DataFileNames++;			  /* Skip to next file name. */
    }

    *FDinc = NULL;

    fprintf(stderr, ",  %ld seconds.", time(NULL) - SaveTime);

    return FDbase;
}

/*****************************************************************************
* My Routine to	allocate dynamic memory. All program requests must call	     *
* this routine (no direct call to malloc). Dies if no memory.		     *
*****************************************************************************/
char *MyMalloc(unsigned size)
{
    char *p;

    if ((p = malloc(size)) != NULL) return p;

    fprintf(stderr, "\nNot enough memory, exit\n");
    MyExit(2);

    return NULL;				    /* Makes warning silent. */
}

/*****************************************************************************
* Routine to free a binary tree, which is not needed any more:		     *
*****************************************************************************/
static void FreeBinTree(BinTree *Pbin)
{
    static Flip = FALSE;

    if (Flip) {
	Flip = FALSE;
	fprintf(stderr, "m\b");
    }
    else {
	Flip = TRUE;
	fprintf(stderr, "M\b");
    }

    if (Pbin) {
	FreeBinTree(Pbin -> right);
	FreeBinTree(Pbin -> left);
	free((char *) Pbin);
    }
}

/*****************************************************************************
* MyExit routine. Note it might call to CloseGraph without calling	     *
* InitGraph(), or call MouseClose() without MouseInit(), or call	     *
* RestoreCtrlBrk() without SetUpCtrlBrk() and it is the responsibility	     *
* of the individual modules to do nothing in these cases.		     *
*****************************************************************************/
void MyExit(int ExitCode)
{
#ifdef __MSDOS__
    fprintf(stderr,
        "\nPoly3D-H: Total RealTime %ld seconds, Core left %ldk.\n",
	    time(NULL) - SaveTotalTime, coreleft() / 1024);
#else
    fprintf(stderr,
        "\nPoly3D-H: Total RealTime %ld seconds.\n",
	    time(NULL) - SaveTotalTime);
#endif /* __MSDOS__ */

    exit(ExitCode);
}
