/*****************************************************************************
* Filter to convert IRIT data files to a AutoCad DXF 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 "getarg.h"
#include "genmat.h"
#include "ffcnvrt.h"
#include "ip_cnvrt.h"

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

static char *CtrlStr =
#ifdef DOUBLE
    "irit2dxf s%-Scale!F t%-Translate!F!F!F i%- f%-FineNess!d 4%- o%-OutName!s T%- z%- DFiles!*s";
#else
    "irit2dxf s%-Scale!f t%-Translate!f!f!f i%- f%-FineNess!d 4%- o%-OutName!s T%- z%- DFiles!*s";
#endif /* DOUBLE */

static char
    *OutFileName = "irit2dxf.dxf";

static int
    GlblTalkative = FALSE,
    GlblShowInternal = FALSE,
    GlblFineNess = 0,
    GlblFourPerFlat = FALSE;

static RealType
    GlblXYZScale = 1.0,
    GlblXTranslate = 0.0,
    GlblYTranslate = 0.0,
    GlblZTranslate = 0.0;

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

static void DumpDataForDXF(char *FileName, IPObjectStruct *PObjects);
static int DumpOneObject(FILE *f, IPObjectStruct *PObject);
static int DumpOnePoly(FILE *f, IPPolygonStruct *PPoly, char *ObjName,
						IPObjectStruct *PObj);
static int DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon, char *ObjName);
static int DumpOnePolyline(FILE *f, IPPolygonStruct *PPolyline, char *ObjName);
static RealType *MapPoint(RealType *Pt);
static void Irit2DxfExit(int ExitCode);

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

    if ((Error = GAGetArgs (argc, argv, CtrlStr,
			    &PrintSizeFlag, &GlblXYZScale,
			    &TranslateFlag, &GlblXTranslate, &GlblYTranslate,
			    &GlblZTranslate, &GlblShowInternal,
			    &FineNessFlag, &GlblFineNess, &GlblFourPerFlat,
			    &OutFileFlag, &OutFileName, &GlblTalkative,
			    &VerFlag, &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Irit2DxfExit(1);
    }

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

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

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

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

    DumpDataForDXF(OutFileFlag ? OutFileName : NULL, PObjects);

    Irit2DxfExit(0);
}

/*****************************************************************************
* Routine to optionally process all surfaces/curves.			     *
* This version simply chain the curves and surfaces into one list.	     *
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
					IPObjectStruct *SrfObjs)
{
    IPObjectStruct *PObj;

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

    if (GlblFineNess == 0) { /* We should dump the curves/surfaces as such. */
	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;
	}
    }
    else {		   /* Convert to polygons and return these instead. */
	int LocalFourPerFlat;
	float RelativeFineNess;
	CagdCrvStruct *Crvs;
	CagdSrfStruct *Srf, *Srfs;
	IPObjectStruct *PObjNext;
	IPPolygonStruct *PPolygon, *PPolygonTemp;

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

	if (CrvObjs) {
	    /* Curves are not supported at this time and they are ignored. */
	    for (PObj = CrvObjs; PObj != NULL;) {
		if (GlblTalkative)
		    fprintf(stderr, "Processing curve object \"%s\"\n",
			    PObj -> Name);

		Crvs = PObj -> U.Crvs;
		CagdCrvFreeList(Crvs);
		PObjNext = PObj -> Pnext;
		PObj -> U.Crvs = NULL;
		IPFreeObject(PObj);
		PObj = PObjNext;
	    }
	    CrvObjs = NULL;
	}

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

		LocalFourPerFlat = GlblFourPerFlat;

		if (AttrGetObjectStrAttrib(PObj, "twoperflat"))
		    LocalFourPerFlat = FALSE;
		if (AttrGetObjectStrAttrib(PObj, "fourperflat"))
		    LocalFourPerFlat = TRUE;

		if ((RelativeFineNess = AttrGetObjectRealAttrib(PObj,
					"resolution")) > IP_ATTR_BAD_REAL)
		    RelativeFineNess = 1.0;

		for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		    PPolygon = PPolygonTemp =
			IritSurface2Polygons(Srf, LocalFourPerFlat,
			      (int) (RelativeFineNess * GlblFineNess), FALSE);
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}

		CagdSrfFreeList(Srfs);
	    }
	}

	return SrfObjs;
    }
}

/*****************************************************************************
* Dumps the data for dxf into FileName(stdout in NULL).			     *
*****************************************************************************/
static void DumpDataForDXF(char *FileName, IPObjectStruct *PObjects)
{
    int i,
	PolyCount = 0;
    IPObjectStruct *PObj,
	*PObjHead = NULL;
    FILE *f;
    static char *Header[] = {
	"999",
	"Creator: IRIT solid modeller using irit2dxf filter.",
	"  0",
	"SECTION",
	"  2",
	"ENTITIES",
	NULL
    };
    static char *Epilog[] = {
	"  0",
	"ENDSEC",
	"  0",
	"EOF",
	NULL
    };

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

    for (i = 0; Header[i] != NULL; i++)
	fprintf(f, "%s\n", Header[i]);

    /* 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) {
	PolyCount += DumpOneObject(f, PObjects);
	PObjects = PObjects -> Pnext;
    }
    if (PolyCount > 0)
	fprintf(stderr, "%d polygons were dumped.\n", PolyCount);

    for (i = 0; Epilog[i] != NULL; i++)
	fprintf(f, "%s\n", Epilog[i]);

    fclose(f);
}

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

    switch (PObject -> ObjType) {
	case IP_OBJ_POLY:
	    PList = PObject -> U.Pl;
	    while (PList) {
		PolyCount += DumpOnePoly(f, PList,
					 PObject -> Name ? PObject -> Name
							 : "IRIT", PObject);
		PList =	PList -> Pnext;
	    }
	    break;
	case IP_OBJ_CURVE:
	case IP_OBJ_SURFACE:
	    break;
	default:
	    break;
    }

    return PolyCount;
}

/*****************************************************************************
* Routine to dump one polygon/line, using global transform CrntViewMat.      *
* Polygon must be convex.						     *
*****************************************************************************/
static int DumpOnePoly(FILE *f, IPPolygonStruct *PPoly, char *ObjName,
						IPObjectStruct *PObj)
{
    if (IP_IS_POLYGON_OBJ(PObj))
	return DumpOnePolygon(f, PPoly, ObjName);
    else if (IP_IS_POLYLINE_OBJ(PObj))
	return DumpOnePolyline(f, PPoly, ObjName);
    else
	return 0;
}

/*****************************************************************************
* Routine to dump one polygon, using global Matrix transform CrntViewMat.    *
*****************************************************************************/
static int DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon, char *ObjName)
{
    int i, j,
	PolyCount = 0;
    RealType *MappedPoint[4];
    IPVertexStruct *VFirst, *V1, *V2, *V3,
	*VList = PPolygon -> PVertex;

    if (VList == NULL)
	return 0;

    if (!IritPrsrIsConvexPolygon(PPolygon)) {
	static int Printed = FALSE;

	if (!Printed) {
	    fprintf(stderr,
		    "\nWARNING: Non convex polygon(s) might be in data (see CONVEX in IRIT),\n\t\t\t\toutput can be wrong as the result!\n");
	    Printed = TRUE;
	}
    }

    VFirst = VList;
    V1 = VFirst -> Pnext;
    V2 = V1 -> Pnext;
    V3 = V2 -> Pnext;

    while (V2 != NULL) {
	MappedPoint[0] = MapPoint(VFirst -> Coord);
	MappedPoint[1] = MapPoint(V1 -> Coord);
	MappedPoint[2] = MapPoint(V2 -> Coord);
	if (V3 != NULL)
	    MappedPoint[3] = MapPoint(V3 -> Coord);
	else
	    MappedPoint[3] = MapPoint(V2 -> Coord); /* Duplicate fourth pt. */

	if (!PT_APX_EQ(MappedPoint[0], MappedPoint[1]) &&
	    !PT_APX_EQ(MappedPoint[0], MappedPoint[2]) &&
	    !PT_APX_EQ(MappedPoint[1], MappedPoint[2])) {
	    int HiddenEdges = (IP_IS_INTERNAL_VRTX(V1) ? 2 : 0) +
			      (IP_IS_INTERNAL_VRTX(V2) ? 4 : 0);

	    /* The last edge that closes the poly is always hidden since    */
	    /* we artificially introduced it here, unless it is the same as */
	    /* the edge of the original polygon, if it is the last edge.    */
	    if (V3 != NULL &&
		(V3 -> Pnext != NULL || IP_IS_INTERNAL_VRTX(V3)))
		HiddenEdges |= 8;

	    /* The first edge is always hidden since we artificially	    */
	    /* introduced it here, unless it is first edge of orig poly.    */
	    if (V1 == VFirst -> Pnext)
	        HiddenEdges |= IP_IS_INTERNAL_VRTX(VFirst) ? 1 : 0;
	    else
		HiddenEdges |= 1;

	    PolyCount++;

	    fprintf(f, "  0\n3DFACE\n  8\n%s\n 70\n%d\n", ObjName, HiddenEdges);

	    for (i = 0; i < 4; i++)
		for (j = 0; j < 3; j++)
		    fprintf(f, " %1d%1d\n%-10.7lf\n",
			    j + 1, i, MappedPoint[i][j]);
	}

	if (V3 == NULL) {
	    V1 = V2;
	    V2 = V2 -> Pnext;
	}
	else {
	    V1 = V3;
	    V2 = V3 -> Pnext;
	    if (V2 != NULL)
		V3 = V2 -> Pnext;
	    else
	        V3 = NULL;
	}
    }

    return PolyCount;
}

/*****************************************************************************
* Routine to dump one polyline, using global Matrix transform CrntViewMat.   *
*****************************************************************************/
static int DumpOnePolyline(FILE *f, IPPolygonStruct *PPolyline, char *ObjName)
{
    return 0;
}

/*****************************************************************************
* Maps the given E3 point using the CrntViewMat.			     *
*****************************************************************************/
static RealType *MapPoint(RealType *Pt)
{
    static int
	Count = 0;
    static RealType MappedPts[4][3];
    RealType *MappedPt = MappedPts[Count++];

    if (Count >= 4)
	Count = 0;

    MatMultVecby4by4(MappedPt, Pt, CrntViewMat);

    MappedPt[0] = MappedPt[0] * GlblXYZScale + GlblXTranslate;
    MappedPt[1] = MappedPt[1] * GlblXYZScale + GlblYTranslate;
    MappedPt[2] = MappedPt[2] * GlblXYZScale + GlblZTranslate;

    return MappedPt;
}

/*****************************************************************************
* Irit2Dxf exit routine.						     *
*****************************************************************************/
static void Irit2DxfExit(int ExitCode)
{
    exit(ExitCode);
}
