/*****************************************************************************
*   An HP Starbase low level drawing routines.				     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 0.1, June 1994.  *
*****************************************************************************/

#include <starbase.c.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XHPlib.h>
#include <X11/cursorfont.h>
#include <X11/Xresource.h>

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "irit_sm.h"
#include "attribut.h"
#include "genmat.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritgrap.h"
#include "x11drvs.h"
#include "xsbdrvs.h"

#define MAX_NUM_OF_VERTICES	1000
#define PRGM_TITLE		"xsbmdrvs"

static int
    GlblFilDes = -1;

static short Colors[IG_MAX_COLOR + 1][3] =
{
    { 0,   0,   0   },  /* 0. BLACK */
    { 0,   0,   170 },  /* 1. BLUE */
    { 0,   170, 0   },  /* 2. GREEN */
    { 0,   170, 170 },  /* 3. CYAN */
    { 170, 0,   0   },  /* 4. RED */
    { 170, 0,   170 },  /* 5. MAGENTA */
    { 170, 170, 0   },  /* 6. BROWN */
    { 170, 170, 170 },  /* 7. LIGHTGREY */
    { 85,  85,  85  },  /* 8. DARKGRAY */
    { 85,  85,  255 },  /* 9. LIGHTBLUE */
    { 85,  255, 85  },  /* 10. LIGHTGREEN */
    { 85,  255, 255 },  /* 11. LIGHTCYAN */
    { 255, 85,  85  },  /* 12. LIGHTRED */
    { 255, 85,  255 },  /* 13. LIGHTMAGENTA */
    { 255, 255, 85  },  /* 14. YELLOW */
    { 255, 255, 255 }   /* 15. WHITE */
};

static void SetColorIndex(int c);
static void SetColorRGB(int Color[3]);
static void ClearViewArea(void);
static void ViewObject(IPObjectStruct *PObj, MatrixType Mat);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Draw a single Point/Vector object using current modes and transformations. M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     A point/vector object to draw.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGDrawPtVec                                                              M
*****************************************************************************/
void IGDrawPtVec(IPObjectStruct *PObj)
{
    int i;
    PointType Ends[6], Zero;
    RealType
	*Pt = PObj -> U.Pt;

    for (i = 0; i < 6; i++)
	PT_COPY(Ends[i], Pt);

    Ends[0][0] -= IGGlblPointWidth;
    Ends[1][0] += IGGlblPointWidth;
    Ends[2][1] -= IGGlblPointWidth;
    Ends[3][1] += IGGlblPointWidth;
    Ends[4][2] -= IGGlblPointWidth;
    Ends[5][2] += IGGlblPointWidth;

    for (i = 0; i < 6; i += 2) {
	move3d(GlblFilDes, Ends[i][0], Ends[i][1], Ends[i][2]);
	draw3d(GlblFilDes, Ends[i+1][0], Ends[i+1][1], Ends[i+1][2]);
    }

    if (IP_IS_VEC_OBJ(PObj)) {
	move3d(GlblFilDes, 0.0, 0.0, 0.0);
	draw3d(GlblFilDes, Pt[0], Pt[1], Pt[2]);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Draw a single Poly object using current modes and transformations.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     A poly object to draw.                                         M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGDrawPoly                                                               M
*****************************************************************************/
void IGDrawPoly(IPObjectStruct *PObj)
{
    IPVertexStruct *V;
    IPPolygonStruct
	*Pl = PObj -> U.Pl;

    if (Pl -> PAux == NULL) {
	/* Initialize this object's star base data. */
	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    float *r, *r2;
	    int i = 0,
		Len = IritPrsrVrtxListLen(Pl -> PVertex);

	    r2 = r = (float *) IritMalloc(1 + sizeof(float) * Len * 6);

	    *((int *) r2) = Len;
	    r2++;

	    for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		*r2++ = V -> Coord[0];
		*r2++ = V -> Coord[1];
		*r2++ = V -> Coord[2];
		*r2++ = V -> Normal[0];
		*r2++ = V -> Normal[1];
		*r2++ = V -> Normal[2];
	    }

	    Pl -> PAux = r;
	}

	Pl = PObj -> U.Pl;
    }

    if (IP_IS_POLYLINE_OBJ(PObj)) {
	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    float
		*r = (float *) Pl -> PAux;

	    polyline3d(GlblFilDes, &r[1], *((int *) r), FALSE);
	}
    }
    else if (IP_IS_POINTLIST_OBJ(PObj)) {
	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		int i;
		PointType Ends[6];
		RealType
		    *Pt = V -> Coord;

		for (i = 0; i < 6; i++)
		    PT_COPY(Ends[i], Pt);

		Ends[0][0] -= IGGlblPointWidth;
		Ends[1][0] += IGGlblPointWidth;
		Ends[2][1] -= IGGlblPointWidth;
		Ends[3][1] += IGGlblPointWidth;
		Ends[4][2] -= IGGlblPointWidth;
		Ends[5][2] += IGGlblPointWidth;

		for (i = 0; i < 6; i += 2) {
		    move3d(GlblFilDes,
			   Ends[i][0], Ends[i][1], Ends[i][2]);
		    draw3d(GlblFilDes,
			   Ends[i+1][0], Ends[i+1][1], Ends[i+1][2]);
		}
	    }
	}
    }
    else if (IP_IS_POLYGON_OBJ(PObj)) {
	int i, j,
	    NumOfVertices;
	PointType PNormal, VNormal;

	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    if (IGGlblBackFaceCull) {
	        RealType P1[3], P2[3];

		MatMultVecby4by4(P1, Pl -> PVertex -> Coord,
				 IritPrsrViewMat);
		PT_ADD(PNormal, Pl -> PVertex -> Coord, Pl -> Plane);
		MatMultVecby4by4(P2, PNormal, IritPrsrViewMat);
		if (P2[2] - P1[2] > 0.0)
		    continue;
	    }

	    if (IGGlblDrawPNormal) {
		NumOfVertices = 0;
		PNormal[0] = PNormal[1] = PNormal[2] = 0.0;
	    }

	    if (IGGlblDrawSolid) {
		float
		    *r = (float *) Pl -> PAux;

		switch (IGGlblShadingModel) {
		    case IG_SHADING_NONE:
		    case IG_SHADING_FLAT:
			polygon_with_data3d(GlblFilDes, &r[1], *((int *) r),
					    3, NULL, NULL);
			break;
		    case IG_SHADING_GOURAUD:
		    case IG_SHADING_PHONG:
			polygon3d(GlblFilDes, &r[1], *((int *) r), FALSE);
			break;
		}
	    }
	    else {
		int NextMove = TRUE;
		RealType
		    *R = Pl -> PVertex -> Coord;

		move3d(GlblFilDes, R[0], R[1], R[2]);

		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    R = V -> Coord;
		    if (NextMove)
		        move3d(GlblFilDes, R[0], R[1], R[2]);
		    else
		        draw3d(GlblFilDes, R[0], R[1], R[2]);

		    NextMove = IP_IS_INTERNAL_VRTX(V) && !IGGlblDrawInternal;

		    if (IGGlblDrawPNormal) {
			for (j = 0; j < 3; j++)
			    PNormal[j] += V -> Coord[j];
			NumOfVertices++;
		    }
		}
		if (!NextMove) {
		    R = Pl -> PVertex -> Coord;
		    draw3d(GlblFilDes, R[0], R[1], R[2]);
		}
	    }

	    if (IGGlblDrawPNormal && IP_HAS_PLANE_POLY(Pl)) {
		for (i = 0; i < 3; i++)
		    PNormal[i] /= NumOfVertices;
		move3d(GlblFilDes, PNormal[0], PNormal[1], PNormal[2]);
		for (i = 0; i < 3; i++)
		    PNormal[i] += Pl -> Plane[i] * IGGlblNormalLen;
		draw3d(GlblFilDes, PNormal[0], PNormal[1], PNormal[2]);
	    }

	    if (IGGlblDrawVNormal) {
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    if (IP_HAS_NORMAL_VRTX(V)) {
			for (j = 0; j < 3; j++)
			    VNormal[j] = V -> Coord[j] +
				         V -> Normal[j] * IGGlblNormalLen;
			move3d(GlblFilDes,
			       V -> Coord[0], V -> Coord[1], V -> Coord[2]);
			draw3d(GlblFilDes, VNormal[0], VNormal[1], VNormal[2]);
		    }
		}
	    }
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the color of an object according to its color/rgb attributes.	     M
*   If object has an RGB attribute it will be used. Otherwise, if the object M
* has a COLOR attribute it will use. Otherwise, WHITE will be used.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To set the drawing color to its color.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetColorObj                                                            M
*****************************************************************************/
void IGSetColorObj(IPObjectStruct *PObj)
{
    int c, Color[3];

    if (AttrGetObjectRGBColor(PObj, &Color[0], &Color[1], &Color[2])) {
	SetColorRGB(Color);
    }
    else if ((c = AttrGetObjectColor(PObj)) != IP_ATTR_NO_COLOR) {
	SetColorIndex(c);
    }
    else {
	/* Use white as default color: */
	SetColorIndex(IG_IRIT_WHITE);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the line width to draw the given object, in pixels.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Width:    In pixels of lines to draw with.                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetWidthObj                                                            M
*****************************************************************************/
void IGSetWidthObj(int Width)
{
    /* This is another "feature" of starbase. The line_width routine only */
    /* works for draw2d and not for draw3d, so this has no affect.	  */
    line_endpoint(GlblFilDes, ROUNDED);
    line_width(GlblFilDes, Width / 100.0, MC_UNITS);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets the color according to the given color index.		    	     *
*                                                                            *
* PARAMETERS:                                                                *
*   color:     Index of color to use. Must be between 0 and IG_MAX_COLOR.    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetColorIndex(int color)
{
    int Color[3];

    if (color < 0 || color > IG_MAX_COLOR)
        color = IG_IRIT_WHITE;

    Color[0] = Colors[color][0];
    Color[1] = Colors[color][1];
    Color[2] = Colors[color][2];

    SetColorRGB(Color);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets the color according to the given RGB values.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Color:      An RGB vector of integer values between 0 and 255.           *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetColorRGB(int Color[3])
{
    line_color(GlblFilDes,
	       Color[0] / 255.0, Color[1] / 255.0, Color[2] / 255.0);
    fill_color(GlblFilDes,
	       Color[0] / 255.0, Color[1] / 255.0, Color[2] / 255.0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Clears the viewing area.						     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ClearViewArea(void)
{
    SetColorRGB(IGGlblBackGroundColor);
    if (IGGlblDrawSolid)
	clear_control(GlblFilDes, CLEAR_DISPLAY_SURFACE | CLEAR_ZBUFFER);
    else
	clear_control(GlblFilDes, CLEAR_DISPLAY_SURFACE);
    clear_view_surface(GlblFilDes);

    hidden_surface(GlblFilDes, FALSE, FALSE);
    if (IGGlblDoDoubleBuffer)
	double_buffer(GlblFilDes, TRUE | INIT , 24);
    else
	double_buffer(GlblFilDes, FALSE , 24);
    vdc_extent(GlblFilDes, -1.0, -1.0, IGGlblZMinClip,
		            1.0,  1.0, IGGlblZMaxClip);
    clip_depth(GlblFilDes, IGGlblZMaxClip, IGGlblZMinClip);
    depth_indicator(GlblFilDes, TRUE, TRUE);

    shade_mode(GlblFilDes, INIT | CMAP_FULL, IGGlblDrawSolid);
    hidden_surface(GlblFilDes, IGGlblDrawSolid, IGGlblBackFaceCull);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets up a view window.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:   Command line.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   SetViewWindow                                                            M
*****************************************************************************/
void SetViewWindow(int argc, char **argv)
{
    char *SBDevice;
    float CrntView[4][4];
    int i, j, transparentOverlays, numOverlayVisuals, numVisuals,
	numImageVisuals, imageDepthToUse, mustFreeImageColormap;
    OverlayInfo	*pOverlayVisuals;
    XVisualInfo	*pVisuals, **pImageVisuals, *pPseudoColorVisualInfo,
	*pGrayScaleVisualInfo;
    Visual	*pImageVisualToUse;
    Colormap	imageColormap;

    if (!ViewHasSize) {
	ViewWidth = DEFAULT_VIEW_WIDTH;
	ViewHeight = DEFAULT_VIEW_HEIGHT;
    }
    if (!ViewHasPos) {
	ViewPosX = 0;
	ViewPosY = 0;
    }

    /* Get all of the visual information about the screen: */
    if (GetXVisualInfo(XDisplay, XScreen, &transparentOverlays,
		       &numVisuals, &pVisuals,
		       &numOverlayVisuals, &pOverlayVisuals,
		       &numImageVisuals, &pImageVisuals))
    {
	fprintf(stderr, "Could not get visual info.\n");
	exit(1);
    }

    /* Find an appropriate image planes visual: */
    if (FindImagePlanesVisual(XDisplay, XScreen, numImageVisuals,
			      pImageVisuals, 1, 8, 1, &pImageVisualToUse,
			      &imageDepthToUse))
    {
	fprintf(stderr, "Could not find a good image planes visual.\n");
	exit(1);
    }

    if (CreateImagePlanesWindow(XDisplay, XScreen,
				RootWindow(XDisplay, XScreen),
				ViewPosX, ViewPosY, ViewWidth, ViewHeight,
				imageDepthToUse, pImageVisualToUse,
				argc, argv, PRGM_TITLE, PRGM_TITLE,
				&ViewWndw, &imageColormap,
				&mustFreeImageColormap))
    {
	fprintf(stderr, "Could not create the image planes window.\n");
	exit(1);
    }

    /* Map the windows: */
    XMapWindow(XDisplay, ViewWndw);
    XSync(XDisplay, FALSE);
    XInstallColormap(XDisplay, imageColormap);
    XSync(XDisplay, FALSE);

    XSelectInput(XDisplay, ViewWndw,
		 ExposureMask | ResizeRedirectMask | ButtonPressMask);
    XFlush(XDisplay);

    SBDevice = make_X11_gopen_string(XDisplay, ViewWndw);
    GlblFilDes = gopen(SBDevice, OUTDEV, "", INIT | THREE_D | MODEL_XFORM);

    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
	    CrntView[i][j] = i == j;
    concat_transformation3d(GlblFilDes, CrntView, PRE, PUSH);

    light_ambient(GlblFilDes, 0.3, 0.3, 0.3);
    light_source(GlblFilDes, 1, DIRECTIONAL, 0.7, 0.7, 0.7,
		 IGGlblLightSrcPos[0], IGGlblLightSrcPos[1],
		 IGGlblLightSrcPos[2]);
    light_source(GlblFilDes, 2, DIRECTIONAL, 0.7, 0.7, 0.7,
		 -IGGlblLightSrcPos[0], -IGGlblLightSrcPos[1],
		 -IGGlblLightSrcPos[2]);
    light_switch(GlblFilDes, 1 + 2 + 4);

    vdc_extent(GlblFilDes, -1.0, -1.0, IGGlblZMinClip,
		            1.0,  1.0, IGGlblZMaxClip);
    clip_depth(GlblFilDes, IGGlblZMaxClip, IGGlblZMinClip);
    depth_indicator(GlblFilDes, TRUE, TRUE);

    background_color(GlblFilDes, IGGlblBackGroundColor[0] / 255.0,
		     	         IGGlblBackGroundColor[1] / 255.0,
				 IGGlblBackGroundColor[2] / 255.0);

    interior_style(GlblFilDes, INT_SOLID, FALSE);

    shade_mode(GlblFilDes, INIT | CMAP_FULL, IGGlblDrawSolid);

    vertex_format(GlblFilDes, 3, 3, 0, FALSE,
		  COUNTER_CLOCKWISE | UNIT_NORMALS);

    ClearViewArea();
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Redraws the view window.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGRedrawViewWindow                                                       M
*****************************************************************************/
void IGRedrawViewWindow(void)
{
    static int
	CurrentDisplay = 0;
    MatrixType IritView;
    IPObjectStruct *PObj;

    IGPredefinedAnimation();

    ClearViewArea();

    switch (IGGlblViewMode) {		 /* Update the current view. */
	case IG_VIEW_ORTHOGRAPHIC:
	    GEN_COPY(IritView, IritPrsrViewMat, sizeof(MatrixType));
	    break;
	case IG_VIEW_PERSPECTIVE:
	    MatMultTwo4by4(IritView, IritPrsrViewMat, IritPrsrPrspMat);
	    break;
    }

    IPTraverseObjListHierarchy(IGGlblDisplayList, IritView, ViewObject);

    if (IGGlblDoDoubleBuffer)
	dbuffer_switch(GlblFilDes, !CurrentDisplay);
    else
        flush_buffer(GlblFilDes);

    CurrentDisplay = !CurrentDisplay;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function of the IPTraverseObjListHierarchy above.              *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      Object to display.                                            *
*   Mat:       Viewing matrix of object.                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ViewObject(IPObjectStruct *PObj, MatrixType Mat)
{
    int i, j;
    float CrntView[4][4];

    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
	    CrntView[i][j] = j == 2 ? -Mat[i][j] : Mat[i][j];

    replace_matrix3d(GlblFilDes, CrntView);

    IGDrawObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Handles redraw of view window event.                                       M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*****************************************************************************/
void IGHandleInternalEvents(void)
{
}
