/*****************************************************************************
*   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.*
*									     *
* Written by:  Gershon Elber				Ver 3.0, Aug. 1990   *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include "program.h"
#include "getarg.h"
#include "genmat.h"
#include "config.h"
#include "ip_cnvrt.h"

#ifdef NO_CONCAT_STR
static char *VersionStr =
	"Poly3D-H	Version 5.0,	Gershon Elber,\n\
	(C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Poly3D-H	" VERSION ",	Gershon Elber,	"
	__DATE__ ",  " __TIME__ "\n" COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr =
#ifdef DOUBLE
	"poly3d-h b%- m%- i%- e%-#Edges!d H%- 4%- W%-Width!F F%-PolyOpti|FineNess!d!F q%- o%-OutName!s c%- z%- DFiles!*s";
#else
	"poly3d-h b%- m%- i%- e%-#Edges!d H%- 4%- W%-Width!f F%-PolyOpti|FineNess!d!f q%- o%-OutName!s c%- z%- DFiles!*s";
#endif /* DOUBLE */

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;
EdgeStruct *EdgeHashTable[EDGE_HASH_TABLE_SIZE];
IPPolygonStruct *PolyHashTable[POLY_HASH_TABLE_SIZE];

/* The following are setable variables (via configuration file poly3d-h.cfg).*/
int GlblMore = FALSE,
    GlblClipScreen = TRUE,
    GlblFourPerFlat = FALSE,
    GlblOptimalPolyApprox = FALSE,
    GlblQuiet = FALSE,
    GlblOutputHasRGB = FALSE,
    GlblOutputRGB[3] = { 255, 255, 255 },
    GlblOutputColor = VISIBLE_COLOR,
    GlblNumEdge = 0,
    GlblBackFacing = FALSE,
    GlblInternal = FALSE,
    GlblOutputHiddenData = FALSE;

RealType
    GlblFineNess = DEFAULT_FINENESS,
    GlblOutputWidth = VISIBLE_WIDTH;

static ConfigStruct SetUp[] =
{
  { "Internal",	   "-i", (VoidPtr) &GlblInternal,	SU_BOOLEAN_TYPE },
  { "BackFacing",  "-b", (VoidPtr) &GlblBackFacing,	SU_BOOLEAN_TYPE },
  { "More",	   "-m", (VoidPtr) &GlblMore,		SU_BOOLEAN_TYPE },
  { "ClipScreen",  "-c", (VoidPtr) &GlblClipScreen,	SU_BOOLEAN_TYPE },
  { "Quiet",	   "-q", (VoidPtr) &GlblQuiet,		SU_BOOLEAN_TYPE },
  { "FourPerFlat", "-4", (VoidPtr) &GlblFourPerFlat,	SU_BOOLEAN_TYPE },
  { "DumpHidden",  "-H", (VoidPtr) &GlblOutputHiddenData,SU_BOOLEAN_TYPE },
  { "PolyOpti",    "-F", (VoidPtr) &GlblOptimalPolyApprox,SU_INTEGER_TYPE },
  { "NumOfEdges",  "-e", (VoidPtr) &GlblNumEdge,	SU_INTEGER_TYPE },
  { "LineWidth",   "-W", (VoidPtr) &GlblOutputWidth,	SU_REAL_TYPE },
  { "FineNess",	   "-F", (VoidPtr) &GlblFineNess,	SU_REAL_TYPE }
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of poly3d-h - Read command line and do what is needed...	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    int Error,
	EdgesFlag = FALSE,
	VerFlag = FALSE,
	OutFlag = FALSE,
	WidthFlag = FALSE,
	NumFiles = FALSE,
	OptPolyApproxFlag = FALSE;
    char
	*OutFileName = NULL,
	**FileNames = NULL;
    FILE *OutFile;
    IPObjectStruct *PObjects, *PTmpObj;

    Config("poly3d-h", SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
			   &GlblBackFacing, &GlblMore, &GlblInternal,
			   &EdgesFlag, &GlblNumEdge, &GlblOutputHiddenData,
			   &GlblFourPerFlat, &WidthFlag,
			   &GlblOutputWidth, &OptPolyApproxFlag,
			   &GlblOptimalPolyApprox, &GlblFineNess,
			   &GlblQuiet, &OutFlag, &OutFileName,
			   &GlblClipScreen, &VerFlag, &NumFiles,
			   &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Poly3dhExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	ConfigPrint(SetUp, NUM_SET_UP);
	Poly3dhExit(0);
    }

    if (!NumFiles) {
	fprintf(stderr, "No data file names were given, exit\n");
	GAPrintHowTo(CtrlStr);
	Poly3dhExit(1);
    }

    if (GlblQuiet)
	GlblMore = FALSE;

    if (GlblOutputHiddenData && GlblBackFacing) {
	fprintf(stderr, "Warning: You have activated both output of hidden data (-H) and back facing\n");
	fprintf(stderr, "\t elimination (-b). Chances are you did not want to do that.\n");
    }

    /* Get the data files: */
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE,
					 GlblMore)) == NULL)
	Poly3dhExit(1);

    /* If has color/width attribute in geometries - uses them. Assume */
    /* all objects share the same attribute (should be fixed).        */
    for (PTmpObj = PObjects; PTmpObj != NULL; PTmpObj = PTmpObj -> Pnext) {
	if (IP_IS_GEOM_OBJ(PTmpObj)) {
	    int Color;
	    RealType Width;

	    if ((Width = AttrGetObjectRealAttrib(PTmpObj, "width")) <
							IP_ATTR_BAD_REAL)
		GlblOutputWidth = Width;

	    if (!GlblOutputHasRGB)
		GlblOutputHasRGB = AttrGetObjectRGBColor(PTmpObj,
							 &GlblOutputRGB[0],
							 &GlblOutputRGB[1],
							 &GlblOutputRGB[2]);
	    if ((Color = AttrGetObjectColor(PTmpObj)) != IP_ATTR_NO_COLOR)
		GlblOutputColor = Color;
	}
    }

    /* And update the global viewing matrix: */
    if (IritPrsrWasPrspMat)
	MatMultTwo4by4(GlblViewMat, IritPrsrViewMat, IritPrsrPrspMat);
    else
	GEN_COPY(GlblViewMat, IritPrsrViewMat, sizeof(MatrixType));

    /* Prepare data structures to be able to decide on visibility: */
    PrepareViewData(PObjects);

    if (OutFlag) {
	if ((OutFile = fopen(OutFileName, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", OutFileName);
	    Poly3dhExit(2);
	}
    }
    else
	OutFile = stdout;

    OutVisibleEdges(OutFile);	       /* Scan all sub-edges output visible. */

    if (OutFile != stdout)
	fclose(OutFile);

    Poly3dhExit(0);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert all surfaces/curves into polylines as follows:	     M
*   Curve is converted to a single polyline with SamplesPerCurve samples.    M
*   Surface is converted into GlblNumOfIsolines curves in each axes, each    M
* handled as Curve above. The original curves and surfaces are then deleted. M
*   This function is a call back function of the irit parser.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForms:  Crvs/Srfs/Trimmed Srfs/Trivariates read from a file by the   M
*               irit parser.					             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Processed freeform geometry. This function simply    M
*                       returns what it gots.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrProcessFreeForm, conversion                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IritPrsrFreeFormStruct *FreeForms)
{
    CagdCrvStruct *Crv, *Crvs;
    CagdSrfStruct *Srf, *Srfs;
    IPObjectStruct *PObj,
	*CrvObjs = FreeForms -> CrvObjs,
	*SrfObjs = FreeForms -> SrfObjs;
    IPPolygonStruct *PPolygon, *PPolygonTemp;

    if (CrvObjs == NULL && SrfObjs == NULL)
	return NULL;

    /* Make sure requested format is something reasonable. */
    if (GlblOptimalPolyApprox == 0 && GlblFineNess < 2) {
	GlblFineNess = 2;
	if (GlblMore)
	    fprintf(stderr, "FineNess is less than 2, 2 picked instead.\n");
    }

    if (CrvObjs) {
	for (PObj = CrvObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblMore)
		fprintf(stderr, "Processing surface object \"%s\".\n", 
			PObj -> Name);
	    Crvs = PObj -> U.Crvs;
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYLINE_OBJ(PObj);
	    for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
		PPolygon = PPolygonTemp =
		    IritCurve2Polylines(Crv, (int) GlblFineNess, FALSE);

		if (PPolygon != NULL) {
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}
	    }
	    CagdCrvFreeList(Crvs);
	}
    }

    if (SrfObjs) {
	for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblMore)
		fprintf(stderr, "Processing surface object \"%s\".\n", 
			PObj -> Name);
	    Srfs = PObj -> U.Srfs;
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYGON_OBJ(PObj);
	    for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		PPolygon = PPolygonTemp =
		    IritSurface2Polygons(Srf, GlblFourPerFlat, GlblFineNess,
					 FALSE, GlblOptimalPolyApprox);

		if (PPolygon != NULL) {
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}
	    }
	    CagdSrfFreeList(Srfs);
	}
    }

    if (SrfObjs == NULL)
	return CrvObjs;
    else if (CrvObjs == NULL)
	return SrfObjs;
    else {
	for (PObj = SrfObjs; PObj -> Pnext != NULL; PObj = PObj -> Pnext);
	PObj -> Pnext = CrvObjs;
	return SrfObjs;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Poly3d-h Exit routine.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   ExitCode:    To notify O.S. with result of program.                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   Poly3dhExit                                                              M
*****************************************************************************/
void Poly3dhExit(int ExitCode)
{
    if (!GlblQuiet)
	fprintf(stderr, "\n");

    exit(ExitCode);
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*****************************************************************************/
void DummyLinkCagdDebug(void)
{
    IritPrsrDbg();
}

#endif /* DEBUG */
