/*****************************************************************************
* Filter to convert IRIT data files to a FIG file.			     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Apr 1992    *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "iritgrap.h"
#include "getarg.h"
#include "genmat.h"
#include "ffcnvrt.h"

#define DEFAULT_VERSION			"#FIG 2.0"
#define DEFAULT_PIXELS_PER_INCH		80
#define DEFAULT_SIZE			7.0

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

static char *CtrlStr =
#ifdef DOUBLE
    "irit2xfg s%-Size!F t%-XTrans|YTrans!F!F I%-#UIso[:#VIso]!s S%-#SampPerCrv!d M%- P%- T%- i%- o%-OutName!s z%- DFiles!*s";
#else
    "irit2xfg s%-Size!f t%-XTrans|YTrans!f!f I%-#UIso[:#VIso]!s S%-#SampPerCrv!d M%- P%- T%- i%- o%-OutName!s z%- DFiles!*s";
#endif /* DOUBLE */

static char
    *GlblStrNumOfIsolines = NULL,
    *OutFileName = "irit2xfg.out";

static int
    GlblTalkative = FALSE,
    GlblShowInternal = FALSE,
    GlblDrawSurfaceMesh = FALSE,
    GlblDrawSurface = TRUE,
    GlblNumOfIsolines[2] = { IG_DEFAULT_NUM_OF_ISOLINES,
			     IG_DEFAULT_NUM_OF_ISOLINES },
    GlblSamplesPerCurve = IG_DEFAULT_SAMPLES_PER_CURVE;

static RealType
    GlblPrintSize = DEFAULT_SIZE,
    GlblXTranslate = 0.0,
    GlblYTranslate = 0.0;

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

static void DumpDataForFIG(char *FileName, IPObjectStruct *PObjects);
static void DumpOneObject(FILE *f, IPObjectStruct *PObject);
static void DumpOnePoly(FILE *f, IPPolygonStruct *PPolygon, int IsPolygon);
static int *MapPoint(RealType *Pt);
static void Irit2XfgExit(int ExitCode);

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

    if ((Error = GAGetArgs (argc, argv, CtrlStr,
			    &PrintSizeFlag, &GlblPrintSize,
			    &TranslateFlag, &GlblXTranslate, &GlblYTranslate,
			    &NumOfIsolinesFlag, &GlblStrNumOfIsolines,
			    &SamplesPerCurveFlag, &GlblSamplesPerCurve,
			    &GlblDrawSurfaceMesh, &GlblDrawSurface,
			    &GlblTalkative, &GlblShowInternal,
			    &OutFileFlag, &OutFileName,
			    &VerFlag, &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Irit2XfgExit(1);
    }

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

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

    if (NumOfIsolinesFlag && GlblStrNumOfIsolines != NULL) {
	if (sscanf(GlblStrNumOfIsolines, "%d:%d",
		   &GlblNumOfIsolines[0], &GlblNumOfIsolines[1]) != 2) {
	    if (sscanf(GlblStrNumOfIsolines, "%d",
		       &GlblNumOfIsolines[1]) != 1) {
		fprintf(stderr,
			"Number(s) of isolines (-I) cannot be parsed.\n");
		GAPrintHowTo(CtrlStr);
		Irit2XfgExit(1);
	    }
	    else {
		GlblNumOfIsolines[1] = GlblNumOfIsolines[0];
	    }
	}
    }

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

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

    DumpDataForFIG(OutFileFlag ? OutFileName : NULL, PObjects);

    Irit2XfgExit(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;

    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 = Curve2Polylines(Crv, GlblDrawSurface,
							  GlblDrawSurfaceMesh,
							  GlblSamplesPerCurve);
		while (PPolygonTemp -> Pnext)
		    PPolygonTemp = PPolygonTemp -> Pnext;
		PPolygonTemp -> Pnext = PObj -> U.Pl;
		PObj -> U.Pl = PPolygon;
	    }
	}
    }

    if (SrfObjs) {
	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 =
		    Surface2Polylines(Srf, GlblDrawSurface,
				      GlblDrawSurfaceMesh, GlblNumOfIsolines,
				      GlblSamplesPerCurve);
		while (PPolygonTemp -> Pnext)
		    PPolygonTemp = PPolygonTemp -> Pnext;
		PPolygonTemp -> Pnext = PObj -> U.Pl;
		PObj -> U.Pl = PPolygon;
	    }
	}
    }

    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;
    }
}

/*****************************************************************************
* Dumps the data for fig into FileName(stdout in NULL).			     *
*****************************************************************************/
static void DumpDataForFIG(char *FileName, IPObjectStruct *PObjects)
{
    IPObjectStruct *PObj,
	*PObjHead = NULL;
    FILE *f;

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

    fprintf(f, "%s\n%d 2\n", DEFAULT_VERSION, DEFAULT_PIXELS_PER_INCH);

    /* Reverse object list since it was loaded in reverse by iritprsr module.*/
    while (PObjects != NULL) {
	PObj = PObjects;
	PObjects = PObjects -> Pnext;
	PObj -> Pnext = PObjHead;
	PObjHead = PObj;
    }
    PObjects = PObjHead;

    while (PObjects) {
	DumpOneObject(f, PObjects);
	PObjects = PObjects -> Pnext;
    }

    fclose(f);
}

/*****************************************************************************
* Routine to dump one object PObject.					     *
*****************************************************************************/
static void DumpOneObject(FILE *f, IPObjectStruct *PObject)
{
    IPPolygonStruct
	*PList = NULL;

    switch (PObject -> ObjType) {
	case IP_OBJ_POLY:
	    if (IP_IS_POLYGON_OBJ(PObject) ||
		IP_IS_POLYLINE_OBJ(PObject))
	        PList = PObject -> U.Pl;
	    break;
	case IP_OBJ_CURVE:
	case IP_OBJ_SURFACE:
	    IritFatalError("Curves and surfaces should have been converted to polylines.");
	    break;
	default:
	    break;
    }

    while (PList) {
	DumpOnePoly(f, PList, IP_IS_POLYGON_OBJ(PObject));
	PList =	PList -> Pnext;
    }
}

/*****************************************************************************
* Routine to dump one polygon/line, using global transform CrntViewMat.      *
*****************************************************************************/
static void DumpOnePoly(FILE *f, IPPolygonStruct *PPolygon, int IsPolygon)
{
    static char
	*PolyHeader = "2 1 0 1 0 0 0 0 0.0 0 0";
    int Length;
    IPVertexStruct
	*VList = PPolygon -> PVertex;

    Length = 0;
    do {
	int *MappedPt = MapPoint(VList -> Coord);

	if (!GlblShowInternal && IP_IS_INTERNAL_VRTX(VList)) {
	    if (Length > 0) {
		fprintf(f, "%d %d 9999 9999\n", MappedPt[0], MappedPt[1]);
		Length = 0;
	    }
	}
	else {
	    if (Length == 0)
		fprintf(f, "%s", PolyHeader);
	    if (Length++ % 8 == 0)
		fprintf(f, "\n\t");
	    fprintf(f, "%d %d ", MappedPt[0], MappedPt[1]);
	}

	VList = VList -> Pnext;
    }
    while (VList != NULL && VList != PPolygon -> PVertex);
    if (Length > 0) {
	if (IsPolygon) {
	    int *MappedPt = MapPoint(PPolygon -> PVertex -> Coord);

	    fprintf(f, "%d %d 9999 9999\n", MappedPt[0], MappedPt[1]);
	}
	else
	    fprintf(f, "9999 9999\n");
    }
}

/*****************************************************************************
* Maps the given E3 point using the CrntViewMat.			     *
*****************************************************************************/
static int *MapPoint(RealType *Pt)
{
    static int IMappedPts[3][3],
	Count = 0;
    int *IMappedPt = IMappedPts[Count++],
	XTranslate = (int) (GlblXTranslate * DEFAULT_PIXELS_PER_INCH),
	YTranslate = (int) (-GlblYTranslate * DEFAULT_PIXELS_PER_INCH);
    RealType MappedPt[3];

    if (Count >= 3)
	Count = 0;

    MatMultVecby4by4(MappedPt, Pt, CrntViewMat);

    /* Since the origin is now ai X = -1, Y = 1 (Top left corner). */
    IMappedPt[0] = (int) ((MappedPt[0] + 1.0) * DEFAULT_PIXELS_PER_INCH *
					GlblPrintSize / 2.0 + XTranslate);
    IMappedPt[1] = (int) ((1.0 - MappedPt[1]) * DEFAULT_PIXELS_PER_INCH *
					GlblPrintSize / 2.0 + YTranslate);

    return IMappedPt;
}

/*****************************************************************************
* Irit2Xfg exit routine.						     *
*****************************************************************************/
static void Irit2XfgExit(int ExitCode)
{
    exit(ExitCode);
}
