/*****************************************************************************
*   An X11 drawing routines.						     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#ifdef __hpux
typedef char *caddr_t;	       /* Awful kludge. Let me know of a better way. */
#endif /* __hpux */

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

#include "irit_sm.h"
#include "genmat.h"
#include "iritprsr.h"
#include "iritgrap.h"
#include "attribut.h"
#include "allocate.h"
#include "x11drvs.h"

#define X11_MAP_X_COORD(x) (((int) ((x + 1.0) * ViewWidth)) / 2)
#define X11_MAP_Y_COORD(y) (((int) ((1.0 - y) * ViewWidth)) / 2 + \
			    (ViewHeight - ViewWidth) / 2)

static int
    CurrentXPosition = 0,
    CurrentYPosition = 0;
static XGCValues CrntColorHighIntensity, CrntColorLowIntensity;

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

/*****************************************************************************
* 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)
{
    Cursor XCursor;
    long ValueMask;
    XSizeHints Hints;
    XSetWindowAttributes SetWinAttr;

    SetWinAttr.background_pixel = ViewBackGroundPixel;
    SetWinAttr.border_pixel = ViewBorderPixel;
    ValueMask = CWBackPixel | CWBorderPixel;

    Hints.flags = PMinSize | PMaxSize;
    Hints.x = Hints.y = 1;
    Hints.min_width = 50;
    Hints.max_width = 2000;
    Hints.min_height = 50;
    Hints.max_height = 2000;
    if (ViewHasSize) {
	Hints.flags |= PSize;
	if (ViewWidth < Hints.min_width)
	    ViewWidth = Hints.min_width;
	if (ViewWidth > Hints.max_width)
	    ViewWidth = Hints.max_width;
	if (ViewHeight < Hints.min_height)
	    ViewHeight = Hints.min_height;
	if (ViewHeight > Hints.max_height)
	    ViewHeight = Hints.max_height;
	Hints.width = ViewWidth;
	Hints.height = ViewHeight;
    }
    else {
	Hints.flags |= PSize;
	Hints.width = ViewWidth = DEFAULT_VIEW_WIDTH;
	Hints.height = ViewHeight = DEFAULT_VIEW_HEIGHT;
    }
    if (ViewHasPos) {
	Hints.flags |= USPosition;
	Hints.x = ViewPosX;
	Hints.y = ViewPosY;
    }

    ViewWndw = XCreateWindow(XDisplay, XRoot,
			     ViewPosX, ViewPosY,
			     ViewWidth, ViewHeight,
			     1, 0, CopyFromParent, CopyFromParent,
			     ValueMask, &SetWinAttr);

    XSetStandardProperties(XDisplay, ViewWndw,
			   RESOURCE_NAME, RESOURCE_NAME, None,
			   argv, argc,
			   &Hints);

    /* Set our own cursor: */
    XCursor = XCreateFontCursor(XDisplay, XC_iron_cross);
    XDefineCursor(XDisplay, ViewWndw, XCursor);
    if (ViewCursorColor != NULL)
	XRecolorCursor(XDisplay, XCursor, ViewCursorColor, &BlackColor);

    XSelectInput(XDisplay, ViewWndw,
		 ExposureMask | ButtonPressMask | ButtonMotionMask);
    
    XMapWindow(XDisplay, ViewWndw);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Redraws the view window.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGRedrawViewWindow                                                       M
*****************************************************************************/
void IGRedrawViewWindow(void)
{
    MatrixType IritView;

    IGPredefinedAnimation();

    XClearWindow(XDisplay, ViewWndw);

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

    XFlush(XDisplay);
}

/*****************************************************************************
* 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)
{
    GEN_COPY(IGGlblCrntViewMat, Mat, sizeof(MatrixType));

    IGDrawObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* To handle internal events. Should not block.                               M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*****************************************************************************/
void IGHandleInternalEvents(void)
{
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Low level 2D drawing routine. Coordinates are normalized to -1 to 1 by     M
* this time.                                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   X, Y:    Coordinates of 2D location to move to.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGMoveTo2D                                                               M
*****************************************************************************/
void IGMoveTo2D(RealType X, RealType Y)
{
    CurrentXPosition = X11_MAP_X_COORD(X);
    CurrentYPosition = X11_MAP_Y_COORD(Y);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Low level 2D drawing routine. Coordinates are normalized to -1 to 1 by     M
* this time.                                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   X, Y:    Coordinates of 2D location to draw to.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGLineTo2D                                                               M
*****************************************************************************/
void IGLineTo2D(RealType X, RealType Y)
{
    int NewX, NewY;

    XDrawLine(XDisplay, ViewWndw, XViewGraphContext,
	      CurrentXPosition,
	      CurrentYPosition,
	      NewX = X11_MAP_X_COORD(X),
	      NewY = X11_MAP_Y_COORD(Y));

    CurrentXPosition = NewX;
    CurrentYPosition = NewY;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the intensity of a color (high or low).				     M
*                                                                            *
* PARAMETERS:                                                                M
*   High:     TRUE for high, FALSE for low.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetColorIntensity                                                      M
*****************************************************************************/
void IGSetColorIntensity(int High)
{
    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      High ? &CrntColorHighIntensity : &CrntColorLowIntensity);
    IGGlblIntensityHighState = High;
}

/*****************************************************************************
* 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)
{
    XGCValues values;

    values.line_width = Width;
    XChangeGC(XDisplay, XViewGraphContext, GCLineWidth, &values);
}

/*****************************************************************************
* 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)
{
    if (color >= MaxColors)
	color = IG_IRIT_WHITE;

    CrntColorHighIntensity.foreground = XViewColorsHigh[color].pixel;
    CrntColorLowIntensity.foreground = XViewColorsLow[color].pixel;
    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      &CrntColorHighIntensity);
    IGGlblIntensityHighState = TRUE;
}

/*****************************************************************************
* 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])
{
    XColor XClr;

    XClr.red   = (Color[0] << 8);
    XClr.green = (Color[1] << 8);
    XClr.blue  = (Color[2] << 8);

    /* If fails to allocate the color - take WHITE instead. */
    if (!XAllocColor(XDisplay, XColorMap, &XClr)) {
	fprintf(stderr,
		"x11drvs: Failed to allocate color, selected WHITE instead\n");
	XClr.pixel = WhitePixel(XDisplay, XScreen);
    }
    CrntColorHighIntensity.foreground = XClr.pixel;

    XClr.red   = (Color[0] << 7);
    XClr.green = (Color[1] << 7);
    XClr.blue  = (Color[2] << 7);

    /* If fails to allocate the color - take WHITE instead. */
    if (!XAllocColor(XDisplay, XColorMap, &XClr)) {
	fprintf(stderr,
		"x11drvs: Failed to allocate color, selected WHITE instead\n");
	XClr.pixel = WhitePixel(XDisplay, XScreen);
    }
    CrntColorLowIntensity.foreground = XClr.pixel;

    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      &CrntColorHighIntensity);
    IGGlblIntensityHighState = TRUE;
}
