/*****************************************************************************
* Program to create illustrations of wireframe drawings.		     *
*   Usually the output of this program is piped to irit2ps, although it      *
* creates a regular IRIT data files with polylines.			     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, June 1993   *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "iritgrap.h"
#include "allocate.h"
#include "poly_cln.h"
#include "getarg.h"
#include "ip_cnvrt.h"

#ifdef NO_CONCAT_STR
static char *VersionStr =
    "Illustrt		Version 4.0,	Gershon Elber,\n\
	 (C) Copyright 1989/90/91/92/93 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr =
    "Illustrt		" VERSION ",	Gershon Elber,	"
	__DATE__ ",   " __TIME__ "\n" COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr =
    "Illustrt I%-#IsoLines!d S%-#SampPerCrv!d s%- M%- P%- p%- l%-MaxLnLen!F a%- t%-TrimInter!F o%-OutName!s T%- z%- DFiles!*s";

static char
    *GlblOutFileName = "illustrt.dat";

static int
    GlblTalkative = FALSE,
    GlblSortOutput = FALSE,
    GlblDrawSurfaceMesh = FALSE,
    GlblDrawSurface = TRUE,
    GlblNumOfIsolines = IG_DEFAULT_NUM_OF_ISOLINES,
    GlblSamplesPerCurve = IG_DEFAULT_SAMPLES_PER_CURVE;

static RealType
    GlblMaxLineLen = DEFAULT_MAX_LINE_LEN;

int GlblAngularDistance = TRUE,
    GlblVertexPoints = FALSE,
    GlblSplitLongLines = FALSE;

RealType
    GlblTrimIntersect = DEFAULT_TRIM_INTERSECT;

static MatrixType CrntViewMat;			/* This is the current view! */

static void MapObjects(IPObjectStruct *PObjects, MatrixType Mat);
static void DumpData(char *FileName,
		     IPObjectStruct *NoProcessObjs,
		     IPObjectStruct *PObjects);

/*****************************************************************************
* Main routine - Read Parameter	line and do what you need...		     *
*****************************************************************************/
void main(int argc, char **argv)
{
    int Error,
	NumOfIsolinesFlag = FALSE,
	SamplesPerCurveFlag = FALSE,
        TrimInterFlag = FALSE,
	OutFileFlag = FALSE,
	VerFlag = FALSE,
	NumFiles = 0;
    char
	**FileNames = NULL;
    IPObjectStruct *PObjects, *NoProcessObjs, *PObj;

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
			   &NumOfIsolinesFlag, &GlblNumOfIsolines,
			   &SamplesPerCurveFlag, &GlblSamplesPerCurve,
			   &GlblSortOutput, &GlblDrawSurfaceMesh,
			   &GlblDrawSurface, &GlblVertexPoints,
			   &GlblSplitLongLines, &GlblMaxLineLen,
			   &GlblAngularDistance,
			   &TrimInterFlag, &GlblTrimIntersect,
			   &OutFileFlag, &GlblOutFileName, &GlblTalkative,
			   &VerFlag, &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	IllustrateExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	IllustrateExit(0);
    }

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

    /* Get the data files: */
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE, FALSE)) ==
									NULL)
	IllustrateExit(0);

    if (IritPrsrWasPrspMat)
	MatMultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat);
    else
	GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType));

    MapObjects(PObjects, CrntViewMat);

    for (PObj = PObjects, PObjects = NoProcessObjs = NULL; PObj != NULL; ) {
	IPObjectStruct
	    *PObjNext = PObj -> Pnext;

	if (AttrGetObjectStrAttrib(PObj, "IllustrtNoProcess") != NULL) {
	    PObj -> Pnext = NoProcessObjs;
	    NoProcessObjs = PObj;
	}
	else {
	    PObj -> Pnext = PObjects;
	    PObjects = PObj;
	}

	PObj = PObjNext;
    }

    if (GlblVertexPoints) {
	/* Add vertices of each polyline as points into data set. */
	for (PObj = PObjects; PObj != NULL;) {
	    if (IP_IS_POLY_OBJ(PObj)) {
		IPObjectStruct
		    *PtList = CopyObject(NULL, PObj, TRUE);

		IP_SET_POINTLIST_OBJ(PtList);

		RemoveInternalVertices(PtList);

		PObj -> Pnext = PtList;
		PObj = PtList -> Pnext;
	    }
	    else
		PObj = PObj -> Pnext;
	}
    }

    ProcessIntersections(PObjects);
    if (GlblSplitLongLines)
	SplitLongLines(PObjects, GlblMaxLineLen);

    if (GlblSortOutput)
	SortOutput(&PObjects);

    DumpData(OutFileFlag ? GlblOutFileName : NULL, NoProcessObjs, PObjects);

    IllustrateExit(0);
}

/*****************************************************************************
* Routine to convert all surfaces/curves into polylines as follows:	     *
* Curves are converted to single polyline with SamplesPerCurve samples.	     *
* Surface are converted into GlblNumOfIsolines curves in each axes, each     *
* handled as Curves above. The polylines are saved in the appropriate        *
* surface/curve slots.							     *
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
					IPObjectStruct *SrfObjs)
{
    CagdCrvStruct *Crv, *Crvs;
    CagdSrfStruct *Srf, *Srfs;
    IPObjectStruct *PObj;
    IPPolygonStruct *PPolygon, *PPolygonTemp;

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

    /* Make sure requested format is something reasonable. */
    if (GlblNumOfIsolines < 2) {
	GlblNumOfIsolines = 2;
	fprintf(stderr, "NumOfIsolines is less than 2, 2 picked instead.\n");
    }

    if (GlblSamplesPerCurve < 1) {
	GlblSamplesPerCurve = 1;
	fprintf(stderr,
		"SamplesPerCurve is less than 1, 1 picked instead.\n");
    }
    if (GlblSamplesPerCurve > CAGD_MAX_BEZIER_CACHE_ORDER) {
	GlblSamplesPerCurve = CAGD_MAX_BEZIER_CACHE_ORDER;
	fprintf(stderr,
		"Log2 SamplesPerCurve is more than %d, %d picked instead.\n",
		CAGD_MAX_BEZIER_CACHE_ORDER, CAGD_MAX_BEZIER_CACHE_ORDER);
    }
    BzrCrvSetCache(GlblSamplesPerCurve, TRUE); /* Set up bezier cache. */

    if (CrvObjs) {
	for (PObj = CrvObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblTalkative)
		fprintf(stderr, "Processing curve 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,
							   GlblSamplesPerCurve);
		while (PPolygonTemp -> Pnext)
		    PPolygonTemp = PPolygonTemp -> Pnext;
		PPolygonTemp -> Pnext = PObj -> U.Pl;
		PObj -> U.Pl = PPolygon;
	    }
	    CagdCrvFreeList(Crvs);
	}
    }

    if (SrfObjs) {
	int NumOfIso[2];

	NumOfIso[0] = -GlblNumOfIsolines;
	NumOfIso[1] = -GlblNumOfIsolines;

	for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblTalkative)
		fprintf(stderr, "Processing surface object \"%s\"\n",
			PObj -> Name);
	    
	    Srfs = PObj -> U.Srfs;
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYLINE_OBJ(PObj);
	    for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		PPolygon = PPolygonTemp =
		    IritSurface2Polylines(Srf, NumOfIso,
					  GlblSamplesPerCurve);
		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;
    }
}

/*****************************************************************************
* Maps the objects according to given transformation matrix.		     *
* Only polylines are expected at this time, other objects are ignored.	     *
*****************************************************************************/
static void MapObjects(IPObjectStruct *PObjects, MatrixType Mat)
{
    IPObjectStruct *PObj;

    for (PObj = PObjects; PObj != NULL; PObj = PObj -> Pnext) {
	if (IP_IS_POLY_OBJ(PObj)) {
	    IPPolygonStruct *PPoly;

	    for (PPoly = PObj -> U.Pl; PPoly != NULL; PPoly = PPoly -> Pnext) {
		IPVertexStruct *V;

		for (V = PPoly -> PVertex; V != NULL; V = V -> Pnext)
		    MatMultVecby4by4(V -> Coord, V -> Coord, Mat);
	    }
	}
    }
}

/*****************************************************************************
* Dumps the data out into FileName (stdout in NULL).			     *
*****************************************************************************/
static void DumpData(char *FileName,
		     IPObjectStruct *NoProcessObjs,
		     IPObjectStruct *PObjects)
{
    FILE *f;

    if (FileName != NULL) {
	if ((f = fopen(FileName, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", FileName);
	    IllustrateExit(2);
	}
    }
    else
	f = stdout;

    fprintf(f, "\tIrit Solid Modeller Data File (ILLUSTRT), %s\n\n",
	    IritRealTimeDate());

    while (PObjects) {
	/* Dump only polys with at least two vertices and points/vectors. */
	if (IP_IS_POLY_OBJ(PObjects)) {
	    if (IP_IS_POLYLINE_OBJ(PObjects))
		CleanUpPolylineList(&PObjects -> U.Pl);
	    if (PObjects -> U.Pl != NULL)
		IritPrsrPutObject(f, PObjects);
	}
	else if (IP_IS_POINT_OBJ(PObjects) ||
		 IP_IS_VEC_OBJ(PObjects)) {
	    IritPrsrPutObject(f, PObjects);
	}

	PObjects = PObjects -> Pnext;
    }

    while (NoProcessObjs) {
	IritPrsrPutObject(f, NoProcessObjs);

	NoProcessObjs = NoProcessObjs -> Pnext;
    }

    fclose(f);
}

/*****************************************************************************
* Illustrate exit routine. 						     *
*****************************************************************************/
void IllustrateExit(int ExitCode)
{
    exit(ExitCode);
}
