/*****************************************************************************
* Filter to convert IRIT data files to a FIG file.			     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* 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 6.0,	Gershon Elber,\n\
	 (C) Copyright 1989/90-95 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[:#WIso]]!s f%-PolyOpti|SampPerCrv!d!d F%-PolyOpti|FineNess!d!F M%- G%- T%- i%- o%-OutName!s z%- DFiles!*s";
#else
    "irit2xfg s%-Size!f t%-XTrans|YTrans!f!f I%-#UIso[:#VIso[:#WIso]]!s f%-PolyOpti|SampPerCrv!d!d F%-PolyOpti|FineNess!d!f M%- G%- T%- i%- o%-OutName!s z%- DFiles!*s";
#endif /* DOUBLE */

static char
    *OutFileName = "irit2xfg.out";

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

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of irit2xfg - 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,
	PrintSizeFlag = FALSE,
	NumOfIsolinesFlag = FALSE,
	CrvOptimalPolyFlag = FALSE,
	SrfOptimalPolyFlag = FALSE,
	VerFlag = FALSE,
	OutFileFlag = FALSE,
	TranslateFlag = FALSE,
	NumFiles = 0;
    char
        *StrNumOfIsolines = NULL,
	**FileNames = NULL;
    IPObjectStruct *PObjects;

    if ((Error = GAGetArgs (argc, argv, CtrlStr,
			    &PrintSizeFlag, &GlblPrintSize,
			    &TranslateFlag, &GlblXTranslate, &GlblYTranslate,
			    &NumOfIsolinesFlag, &StrNumOfIsolines,
			    &CrvOptimalPolyFlag, &FFCState.OptimalPolylines,
			    &FFCState.SamplesPerCurve,
			    &SrfOptimalPolyFlag, &FFCState.OptimalPolygons,
			    &FFCState.FineNess,
			    &FFCState.DrawFFMesh, &FFCState.DrawFFGeom,
			    &FFCState.Talkative, &FFCState.ShowInternal,
			    &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);
    }

    FFCState.DumpObjsAsPolylines = !SrfOptimalPolyFlag;

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

	}
    }

    /* Get the data files: */
    IritPrsrSetFlattenObjects(TRUE);
    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);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps the data for FIG/XFIG into FileName (stdout if NULL).		     *
*                                                                            *
* PARAMETERS:                                                                *
*   FileName:   Where output should go to.                                   *
*   PObjects:   To dump into file.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpDataForFIG(char *FileName, IPObjectStruct *PObjects)
{
    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);


    while (PObjects) {
	IPObjectStruct *PObjNext;

	if (IP_IS_FFGEOM_OBJ(PObjects))
	    PObjects = ProcessFreeForm(PObjects, &FFCState);

	DumpOneObject(f, PObjects);

	PObjNext = PObjects -> Pnext;
	IPFreeObject(PObjects);
	PObjects = PObjNext;
    }

    fclose(f);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one object PObject to file f.                                        *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   PObject:      Object to dump to file f.                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
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;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one polygon/line, using global transform CrntViewMat.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   PPolygon:     Poly to dump to file f.                                    *
*   IsPolygon:    TRUE for polygon, FALSE otherwise.                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
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 (!FFCState.ShowInternal && 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");
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Maps the given E3 point using the CrntViewMat.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Pt:        Point to map.                                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType *:   Mapped point, in local static place (for up to 3 calls).   *
*****************************************************************************/
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;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Irit2Xfg exit routine.						     *
*                                                                            *
* PARAMETERS:                                                                *
*   ExitCode:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void Irit2XfgExit(int ExitCode)
{
    exit(ExitCode);
}

#ifdef DEBUG

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

#endif /* DEBUG */
