/*****************************************************************************
* Filter to convert IRIT data files to REND386 (IBMPC only) Plg format.	     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Sep 1991    *
*****************************************************************************/

#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"
#include "ip_cnvrt.h"

#define GLOBAL_SCALE	200.0  /* Scale obj space -1..1 to -200..200 pixels. */

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

static char
    *CtrlStr = "irit2Plg l%- 4%- f%-FineNess!d T%- z%- DFiles!*s";

static int
    GlblTalkative = FALSE,
    GlblFineNess = 5,
    FourPerFlat = FALSE;

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

static int TransColorTable[][4] = {
    { /* BLACK		*/ 0,    0,   0,   0 },
    { /* BLUE		*/ 1,    0,   0, 255 },
    { /* GREEN		*/ 2,    0, 255,   0 },
    { /* CYAN		*/ 3,    0, 255, 255 },
    { /* RED		*/ 4,  255,   0,   0 },
    { /* MAGENTA 	*/ 5,  255,   0, 255 },
    { /* BROWN		*/ 6,   50,   0,   0 },
    { /* LIGHTGRAY	*/ 7,  127, 127, 127 },
    { /* DARKGRAY	*/ 8,   63,  63,  63 },
    { /* LIGHTBLUE	*/ 9,    0,   0, 255 },
    { /* LIGHTGREEN	*/ 10,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 11,   0, 255, 255 },
    { /* LIGHTRED	*/ 12, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 13, 255,   0, 255 },
    { /* YELLOW		*/ 14, 255, 255,   0 },
    { /* WHITE		*/ 15, 255, 255, 255 },
    { /* BROWN		*/ 20,  50,   0,   0 },
    { /* DARKGRAY	*/ 56,  63,  63,  63 },
    { /* LIGHTBLUE	*/ 57,   0,   0, 255 },
    { /* LIGHTGREEN	*/ 58,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 59,   0, 255, 255 },
    { /* LIGHTRED	*/ 60, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 61, 255,   0, 255 },
    { /* YELLOW		*/ 62, 255, 255,   0 },
    { /* WHITE		*/ 63, 255, 255, 255 },
    {			   -1,   0,   0,   0 }
};

static int PlgColorTable[16] = {
	/* IRIT     PLG   */
    15, /* BLOCK -> WHITE */
    11, /* BLUE */
    7,  /* GREEN */
    8,  /* CYAN */
    1,  /* RED */
    13, /* MAGENTA */
    2,  /* BROWN */
    14, /* LIGHTGRAY */
    14, /* DARKGRAY */
    10, /* LIGHT BLUE */
    9,  /* LIGHT GREEN */
    8,  /* LIGHT CYAN */
    1,  /* LIGHT RED */
    13, /* LIGHT MAGENTA */
    6,  /* YELLOW */
    15, /* WHITE */    
};

static void DumpDataForPlg(IPObjectStruct *PObjects);
static void DumpOneObject(FILE *f, IPObjectStruct *PObject,
					  int DumpVertices, int *IncVertices);
static void DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon, int Color,
			   int DumpVertices, int *IncVertices, int IsPolygon);
static RealType *MapPoint(RealType *Pt);
static void Irit2PlgExit(int ExitCode);

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

    if ((Error = GAGetArgs (argc, argv, CtrlStr, &LinearOnePolyFlag,
			    &FourPerFlat, &FineNessFlag, &GlblFineNess,
			    &GlblTalkative, &VerFlag, &NumFiles,
			    &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Irit2PlgExit(1);
    }

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

    if (LinearOnePolyFlag) {
	fprintf(stderr, "Linear patch side will have a single polygon.\n");
	CagdSetLinear2Poly(CAGD_ONE_POLY_PER_COLIN);
    }
    else
        CagdSetLinear2Poly(CAGD_REG_POLY_PER_LIN);

    fprintf(stderr, "%s triangles per flat will be created.\n",
	    FourPerFlat ? "Four" : "Two");

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

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

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

    DumpDataForPlg(PObjects);

    Irit2PlgExit(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 curves and surfaces are then deleted.	     *
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
					IPObjectStruct *SrfObjs)
{
    int LocalFourPerFlat;
    float RelativeFineNess;
    CagdCrvStruct *Crvs;
    CagdSrfStruct *Srf, *Srfs;
    IPObjectStruct *PObj, *PObjNext;
    IPPolygonStruct *PPolygon, *PPolygonTemp;

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

    /* 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 rendered 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_POLYGON_OBJ(PObj);

	    LocalFourPerFlat = FourPerFlat;

	    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 REND386 Plg into stdout.				     *
*****************************************************************************/
static void DumpDataForPlg(IPObjectStruct *PObjects)
{
    int IncVertices,
        TotalVertices = 0,
	TotalPolys = 0;
    char
	*Name = NULL;
    IPVertexStruct *PVertex;
    IPPolygonStruct *PPoly;
    IPObjectStruct *PObj,
	*PObjHead = NULL;
    FILE *f = stdout;	       /* Change it if you want it into "real" file. */
    
    /* 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;

    /* Count how many polygons/vertices we have in this data set and print. */
    for (PObj = PObjects; PObj != NULL; PObj = PObj -> Pnext) {
	if (!IP_IS_POLY_OBJ(PObj) || !IP_IS_POLYGON_OBJ(PObj))
	    continue;

	if (Name == NULL && strlen(PObj -> Name) > 0)
	    Name = PObj -> Name;

	for (PPoly = PObj -> U.Pl;
	     PPoly != NULL;
	     PPoly = PPoly -> Pnext) {
	    TotalPolys++;

	    for (PVertex = PPoly -> PVertex;
		 PVertex != NULL;
		 PVertex = PVertex -> Pnext) {
		TotalVertices++;
	    }
	}
    }

    if (Name != NULL)
        fprintf(f, "IRIT %d %d\n", TotalVertices, TotalPolys);
    else
	fprintf(f, "%s_IRIT %d %d\n", Name, TotalVertices, TotalPolys);

    for (IncVertices = 0, PObj = PObjects; PObj != NULL; PObj = PObj -> Pnext)
	DumpOneObject(f, PObj, TRUE, &IncVertices);
    if (IncVertices != TotalVertices) {
	fprintf(stderr, "Inconsistent vertices count.\n");
	Irit2PlgExit(1);
    }

    for (IncVertices = 0, PObj = PObjects; PObj != NULL; PObj = PObj -> Pnext)
	DumpOneObject(f, PObj, FALSE, &IncVertices);
    if (IncVertices != TotalVertices) {
	fprintf(stderr, "Inconsistent vertices count.\n");
	Irit2PlgExit(1);
    }

    fclose(f);
}

/*****************************************************************************
* Routine to dump one object PObject.					     *
*****************************************************************************/
static void DumpOneObject(FILE *f, IPObjectStruct *PObject,
					  int DumpVertices, int *IncVertices)
{
    int i, Color;
    IPPolygonStruct *PList;

    if (!IP_IS_POLY_OBJ(PObject))
	return;

    PList = PObject -> U.Pl;

    if ((Color = AttrGetObjectColor(PObject)) != IP_ATTR_NO_COLOR) {
	for (i = 0; TransColorTable[i][0] >= 0; i++) {
	    if (TransColorTable[i][0] == Color) {
		Color = PlgColorTable[i];
		break;
	    }
	}
    }
    else
        Color = IG_DEFAULT_COLOR;

    while (PList) {
	DumpOnePolygon(f, PList, Color, DumpVertices, IncVertices,
						IP_IS_POLYGON_OBJ(PObject));
	PList =	PList -> Pnext;
    }
}

/*****************************************************************************
* Routine to dump one polygon, using global Matrix transform CrntViewMat.    *
*****************************************************************************/
static void DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon, int Color,
			   int DumpVertices, int *IncVertices, int IsPolygon)
{
    int CountVertices;
    RealType *MappedPoint;
    IPVertexStruct *V,
	*VList = PPolygon -> PVertex;

    if (VList == NULL)
	return;

    if (DumpVertices && !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;
	}
    }

    if (IsPolygon) {
	if (DumpVertices) {
	    for (V = VList; V != NULL; V = V -> Pnext) {
		MappedPoint = MapPoint(V -> Coord);
		fprintf(f, "%4d %4d %4d\n",
			(int) (MappedPoint[0] * GLOBAL_SCALE),
			(int) (MappedPoint[1] * GLOBAL_SCALE),
			(int) (MappedPoint[2] * GLOBAL_SCALE));
		(*IncVertices)++;
	    }
	}
	else {
	    for (CountVertices = 0, V = VList; V != NULL; V = V -> Pnext)
	        CountVertices++;
	    fprintf(f, "0x%02xff %d", Color | 0x0010, CountVertices);
	    while (CountVertices-- > 0)
	        fprintf(f, " %d", (*IncVertices)++);
	    fprintf(f, "\n");
	}
    }
}

/*****************************************************************************
* Maps the given E3 point using the CrntViewMat.			     *
*****************************************************************************/
static RealType *MapPoint(RealType *Pt)
{
    static RealType MappedPt[3];

    MatMultVecby4by4(MappedPt, Pt, CrntViewMat);

    return MappedPt;
}

/*****************************************************************************
* Irit2Plg exit routine.						     *
*****************************************************************************/
static void Irit2PlgExit(int ExitCode)
{
    exit(ExitCode);
}
