/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber		    Unix - X11 Ver 0.1, Mar. 1990    *
******************************************************************************
* Module to handle viewing of objects in the ViewWindow.		     *
*****************************************************************************/

#ifndef __MSDOS__

#include <stdio.h>
#include <math.h>
#include "program.h"
#include "windowsl.h"
#include "windowsg.h"
#include "viewobjl.h"
#include "viewobjg.h"
#include "graphgng.h"
#include "objects.h"

#include "xgraphic.h"

static int ViewNormals = FALSE, NormalsColor = 1, /* View normals to object. */
	   QuitView = FALSE;
static RealType NormalsSize = 0.1;
static ObjectStruct *ActiveObjList = NULL;   /* Currently displayed objects. */

/*****************************************************************************
*  Routine to interactively display geometric object(s) PObjList on the View *
* window enable rotating/translating/scaling it using the Input Device.      *
*****************************************************************************/
void InteractGeomObject(ObjectStruct *PObjList, RealType *UpdateGlblMat)
{
    struct ObjectStruct *ViewMat;
    MatrixType ViewMatCopy;	 /* Save original transformation to recover. */

    if (!IS_OLST_OBJ(PObjList))
	FatalError("InteractGeomObject: Not object list object!\n");

    if ((ViewMat = GetObject("VIEW_MAT")) == NULL) {
	WndwInputWindowPutStr(
	    "No view transformation matrix VIEW_MAT!", RED);
	return;
    }
    else if (!IS_MAT_OBJ(ViewMat)) {
	WndwInputWindowPutStr(
	    "VIEW_MAT object was modified (not matrix object)", RED);
	return;
    }
    MAT_COPY(ViewMatCopy, ViewMat -> U.Mat);

    /* Get data from input device, interpret it, and display interactively:  */
    InteractHandleInput(PObjList, ViewMat -> U.Mat);

    if (!APX_EQ(*UpdateGlblMat, 0.0))
	MAT_COPY(ViewMat -> U.Mat, ViewMatCopy);		 /* Recover. */
}

/*****************************************************************************
*  Routine to handle data from the input device (keyboard, mouse etc.) -     *
* clip it against the sub windows of the interactive menu and perform the    *
* required transfomation, by updating the global view matrix object VIEW_MAT *
*  The input data in the Rotation/Translation/Scaling sub windows is used    *
* (horizontal distance from sub window center) to set amount of change.	     *
*****************************************************************************/
static void InteractHandleInput(ObjectStruct *PObjList, MatrixType GlblViewMat)
{
    int i, UpdateView, ScreenCoord = TRUE;
    char *ToggleStr;
    RealType x, y, ChangeFactor;
    MatrixType Mat;

    ActiveObjList = PObjList;

    QuitView = FALSE;
    UpdateInteractHandleInput();	   /* Display it for the first time. */

    while (TRUE) {
	QuitView = FALSE;

	UpdateView = TRUE;

	switch (GetGraphicEvent(&ChangeFactor, &ToggleStr)) {
	    case EVENT_SCR_OBJ_TGL:    /* Its Coordinate system - toggle it. */
		ScreenCoord = *ToggleStr == 'S';
		UpdateView = FALSE;
		break;
	    case EVENT_ROTATE_X:	   /* Its rotation along the X axis. */
		MatGenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case EVENT_ROTATE_Y:	   /* Its rotation along the Y axis. */
		MatGenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case EVENT_ROTATE_Z:	   /* Its rotation along the Z axis. */
		MatGenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case EVENT_TRANSLATE_X:	/* Its translation along the X axis. */
		MatGenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0,
									Mat);
		break;
	    case EVENT_TRANSLATE_Y:	/* Its translation along the Y axis. */
		MatGenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0,
									Mat);
		break;
	    case EVENT_TRANSLATE_Z:	/* Its translation along the Z axis. */
		MatGenMatTrans(0.0, 0.0, ChangeFactor * MAX_TRANSLATE_FACTOR,
									Mat);
		break;
	    case EVENT_SCALE:		      /* Its scaling along all axes. */
		if (ChangeFactor > 0.0)		      /* Make it around 1... */
		     ChangeFactor = ChangeFactor * MAX_SCALE_FACTOR + 1.0;
		else ChangeFactor = 1.0 /
			(-ChangeFactor * MAX_SCALE_FACTOR + 1.0);
		MatGenMatScale(ChangeFactor, ChangeFactor, ChangeFactor, Mat);
		break;
	    case EVENT_QUIT:
		ActiveObjList = NULL;
		return;						/* Its Quit. */
	    default:
		FatalError("InteractHandleInput: Undefine input type, exit\n");
	}
	if (UpdateView) {
	    if (ScreenCoord)		/* Udpate the global viewing matrix. */
		 MatMultTwo4by4(GlblViewMat, GlblViewMat, Mat);
	    else MatMultTwo4by4(GlblViewMat, Mat, GlblViewMat);
	    UpdateInteractHandleInput();
	}
    }
}

/*****************************************************************************
*  Routine to update the viewing screen. On unix systems this routine may    *
* be invoked when X sends expose event to this program (see xgrphgen.c).     *
* In MSDOS this routine is static and been called from InteractHandleInput   *
* above routine only.							     *
*****************************************************************************/
void UpdateInteractHandleInput(void)
{
    GGClearAllScreen();
    if (ActiveObjList != NULL)
	ViewGeomObjectList(ActiveObjList);		/* And display it... */
}

/*****************************************************************************
*  Routine to display the geometric objects in PObjList, by simply calling   *
* ViewGeomObject on all of them. PObjList must be of type OBJ_LIST_OBJ.	     *
*****************************************************************************/
static void ViewGeomObjectList(ObjectStruct *PObjList)
{
    int Param = 0;
    struct ObjectStruct *PObj;
    char Line[LINE_LEN];

    while ((PObj = PObjList -> U.PObjList[Param]) != NULL &&
	   Param++ < MAX_OBJ_LIST && !QuitView) {
	if (!IS_GEOM_OBJ(PObj)) {
	    sprintf(Line, "Cannt display none geometric object %s, ignored",
		PObj -> Name);
	    WndwInputWindowPutStr(Line, RED);
	    continue;
	}
	ViewGeomObject(PObj);
    }
}

/*****************************************************************************
*  Routine to fetch the internal parameter from the INTERNAL object.	     *
*****************************************************************************/
static int GetInternal(void)
{
    int Internal;
    struct ObjectStruct *PObj = GetObject("INTERNAL");

    if (PObj == NULL || !IS_NUM_OBJ(PObj)) {
	WndwInputWindowPutStr("No numeric object name INTERNAL is defined",
									RED);
	Internal = DEFAULT_INTERNAL;
    }
    else Internal = !APX_EQ((PObj -> U.R), 0.0);

    return Internal;
}

/*****************************************************************************
*  Routine to display the geometric object PObj on the View Window:	     *
* Uses the global view transformation matrix ViewMat as view point.	     *
*****************************************************************************/
void ViewGeomObject(ObjectStruct *PObj)
{
    int Color, ShowNormals, ViewInternal = GetInternal();
    struct PolygonStruct *Pl;
    struct ObjectStruct *ViewMat;

    QuitView = FALSE;

    if (!IS_GEOM_OBJ(PObj))
	FatalError("ViewGeomObject: Not geometric object!\n");

    Pl = PObj -> U.Pl;

    if ((ViewMat = GetObject("VIEW_MAT")) == NULL) {
	WndwInputWindowPutStr(
	    "No view transformation matrix VIEW_MAT!", RED);
	return;
    }
    else if (!IS_MAT_OBJ(ViewMat)) {
	WndwInputWindowPutStr(
	    "VIEW_MAT object was modified (not matrix object)\n", RED);
	return;
    }

    Color = GET_OBJECT_COLOR(PObj);
    ShowNormals = (ViewNormals && !IS_POLYLINE_GEOM_OBJ(PObj));

    while (Pl && !QuitView) {
	ViewPolygon(Pl, Color, ShowNormals, ViewMat -> U.Mat, ViewInternal);
	Pl = Pl -> Pnext;
	TestQuitView();	   /* if break display in the middle - Set QuitView. */
    }

    GraphicFlush();
}

/*****************************************************************************
*  Routine to test if quit display event - occured -			     *
* Right button was clicked on mouse.					     *
*****************************************************************************/
static void TestQuitView(void)
{
    QuitView = IsAbortKeyPressed();
}

/*****************************************************************************
*  Routine to display one polygon on the view window using the matrix Mat as *
* a transformation matrix.						     *
*****************************************************************************/
static void ViewPolygon(PolygonStruct *Pl, int Color, int ShowNormals,
					MatrixType Mat, int ViewInternal)
{
    int NumOfPoints, DontDraw;
    PointType P, CenterP;
    struct VertexStruct *V, *VStart;

    V = VStart = Pl -> V;
    if (V == NULL) FatalError("ViewPolygon: Empty polygon to view\n");

    MatMultVecby4by4(P, V -> Pt, Mat);	       /* Transform the first point. */
    GGMyMove(P[0], P[1]);
    DontDraw = IS_INTERNAL_EDGE(V) && !ViewInternal;      /* Draw next edge? */
    GGMySetColor(Color);

    if (ShowNormals) {			/* If display of normal is required. */
	NumOfPoints = 0;
	PT_CLEAR(CenterP);
    }

    do {
	V = V -> Pnext;
	if (ShowNormals) {
	    NumOfPoints++;
	    PT_ADD(CenterP, CenterP, V -> Pt);
	}
	MatMultVecby4by4(P, V -> Pt, Mat);
	/* If edge is INTERNAL (Irit.h) and not ViewInternal - dont draw: */
	if (DontDraw)
	     GGMyMove(P[0], P[1]);
	else GGMyDraw(P[0], P[1]);

	DontDraw = IS_INTERNAL_EDGE(V) && !ViewInternal;  /* Draw next edge? */
    } while (V != VStart && V -> Pnext != NULL);

    if (ShowNormals) {
	PT_SCALE(CenterP, 1.0/NumOfPoints);	    /* Estimate for normals. */
	MatMultVecby4by4(P, CenterP, Mat);     /* Transform the first point. */
	GGMyMove(P[0], P[1]);
	PT_COPY(P, Pl -> Plane);
	PT_SCALE(P, NormalsSize);
	PT_ADD(CenterP, CenterP, P);
	MatMultVecby4by4(P, CenterP, Mat);    /* Transform the second point. */
	GGMySetColor(NormalsColor);
	GGMyDraw(P[0], P[1]);
    }
}

/*****************************************************************************
*  Routine to set the normals default values:				     *
*****************************************************************************/
void ViewSetNormals(RealType *Active, RealType *Size, RealType *Color)
{
    ViewNormals = !APX_EQ(*Active, 0.0);
    NormalsSize = *Size;
    NormalsColor = (int) *Color;
}

#endif /* __MSDOS__ */
