/*****************************************************************************
*   A Windows NT driver - framework.					     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <process.h>
#include "irit_sm.h"
#include "genmat.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "ip_cnvrt.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritgrap.h"
#include "irit_soc.h"
#include "wntdrvs.h"

/* Interactive menu setup structure: */
#define INTERACT_NUM_OF_STRINGS		3
#define INTERACT_NUM_OF_SUB_WNDWS	15
#define INTERACT_SUB_WINDOW_WIDTH  0.8		 /* Relative to window size. */
#define INTERACT_SUB_WINDOW_HEIGHT 0.04

typedef struct InteractString {
    RealType X, Y;
    int Color;
    char *Str;
} InteractString;
typedef struct InteractSubWindow {
    RealType X, Y;					   /* Center points. */
    int Color;
    IGGraphicEventType Event;
    int TextInside; /* If TRUE, Str will be in window, otherwise left to it. */
    char *Str;
} InteractSubWindow;
typedef struct InteractWindowStruct {	 /* The interactive menu structures. */
    /* Rotate, Translate, Scale strings: */
    InteractString Strings[INTERACT_NUM_OF_STRINGS];
    InteractSubWindow SubWindows[INTERACT_NUM_OF_SUB_WNDWS];
} InteractWindowStruct;

/* Interactive mode menu set up structure is define below: */
static InteractWindowStruct InteractMenu =
{
  {
    { 0.5, 0.76, IG_IRIT_RED,   "Rotate" },
    { 0.5, 0.56, IG_IRIT_GREEN, "Translate" },
    { 0.5, 0.36, IG_IRIT_CYAN,  "Scale" },
  },
  {
    { 0.5, 0.92, IG_IRIT_YELLOW, IG_EVENT_SCR_OBJ_TGL,    TRUE,  "Screen Coords." },
    { 0.5, 0.84, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_TGL, TRUE,  "Perspective" },
    { 0.5, 0.79, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_Z,   FALSE, "Z" },
    { 0.5, 0.69, IG_IRIT_RED,    IG_EVENT_ROTATE_X,       FALSE, "X" },  /* Rot */
    { 0.5, 0.64, IG_IRIT_RED,    IG_EVENT_ROTATE_Y,       FALSE, "Y" },
    { 0.5, 0.59, IG_IRIT_RED,    IG_EVENT_ROTATE_Z,       FALSE, "Z" },
    { 0.5, 0.49, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_X,    FALSE, "X" },/* Trans */
    { 0.5, 0.44, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Y,    FALSE, "Y" },
    { 0.5, 0.39, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Z,    FALSE, "Z" },
    { 0.5, 0.29, IG_IRIT_CYAN,   IG_EVENT_SCALE,	  FALSE, "" }, /* Scale */
    { 0.5, 0.21, IG_IRIT_YELLOW, IG_EVENT_DEPTH_CUE,      TRUE,  "Depth Cue" },
    { 0.5, 0.16, IG_IRIT_YELLOW, IG_EVENT_SAVE_MATRIX,    TRUE,  "Save Matrix" },
    { 0.5, 0.12, IG_IRIT_YELLOW, IG_EVENT_PUSH_MATRIX,    TRUE,  "Push Matrix" },
    { 0.5, 0.08, IG_IRIT_YELLOW, IG_EVENT_POP_MATRIX,     TRUE,  "Pop Matrix" },
    { 0.5, 0.03, IG_IRIT_WHITE,  IG_EVENT_QUIT,	          TRUE,  "Quit" },
  }
};

/* Colors to be used for viewed object (see also iritgrap.h):		   */
/* This colors are adjusted so as to give Windows NT better chance at      */
/* selecting these colors as solid.					   */
static short Colors[IG_MAX_COLOR + 1][3] =
{
    { 0,   0,   0   },  /* 0. BLACK */
    { 0,   0,   255 },  /* 1. BLUE */
    { 0,   255, 0   },  /* 2. GREEN */
    { 0,   255, 255 },  /* 3. CYAN */
    { 255, 0,   0   },  /* 4. RED */
    { 255, 0,   255 },  /* 5. MAGENTA */
    { 127, 127, 0   },  /* 6. BROWN */
    { 127, 127, 127 },  /* 7. LIGHTGREY */
    { 63,  63,  63  },  /* 8. DARKGRAY */
    { 63,  63,  255 },  /* 9. LIGHTBLUE */
    { 63,  255, 63  },  /* 10. LIGHTGREEN */
    { 63,  255, 255 },  /* 11. LIGHTCYAN */
    { 255, 63,  63  },  /* 12. LIGHTRED */
    { 255, 63,  255 },  /* 13. LIGHTMAGENTA */
    { 255, 255, 63  },  /* 14. YELLOW */
    { 255, 255, 255 }   /* 15. WHITE */
};

static int
    CurrentXPosition = 0,
    CurrentYPosition = 0;
static unsigned int
    TransWidth = DEFAULT_TRANS_WIDTH,
    TransHeight = DEFAULT_TRANS_HEIGHT,
    TransWidth2 = DEFAULT_TRANS_WIDTH / 2,
    TransHeight2 = DEFAULT_TRANS_HEIGHT / 2;
unsigned int
    IGViewWidth = DEFAULT_VIEW_WIDTH,
    IGViewHeight = DEFAULT_VIEW_HEIGHT,
    IGViewWidth2 = DEFAULT_VIEW_WIDTH / 2,
    IGViewHeight2 = DEFAULT_VIEW_HEIGHT / 2;

HBRUSH IGBackGroundBrush;
COLORREF
    IGBackGroundColor, IGCrntColorLowIntensity, IGCrntColorHighIntensity,
    IGColorsLowIntensity[IG_MAX_COLOR + 1],
    IGColorsHighIntensity[IG_MAX_COLOR + 1];
HPEN
    IGCurrenthPen = 0;
HDC IGCurrenthDC = 0;
HWND IGhWndView, IGhWndTrans;
HMENU GlblStateMenu = 0;

static void GetArgCV(CHAR *Str, int *argc, CHAR ***argv);
static void GetInputFromSocket(void *Data);
static LONG APIENTRY WndProc(HWND hWndFrame,
			     UINT wMsg,
			     WPARAM wParam,
			     LONG lParam);
static LONG APIENTRY ViewWndProc(HWND hWndFrame,
				 UINT wMsg,
				 WPARAM wParam,
				 LONG lParam);
static LONG APIENTRY TransWndProc(HWND hWndFrame,
				  UINT wMsg,
				  WPARAM wParam,
				  LONG lParam);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor,
					  int X,
					  int Y);
static void RedrawTransformationWindow(HWND hWnd);
static void SetColorIndex2(int color, int width);
static void DrawTextLocal(char *Str, int PosX, int PosY);

/*****************************************************************************
* DESCRIPTION:                                                               *
* Gets the command line into a form understandable by K&R definition.        *
*                                                                            *
* PARAMETERS:                                                                *
*   Str:         Command line to decipher.                                   *
*   argc, argv:  Command line, K&R style.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetArgCV(CHAR *Str, int *argc, CHAR ***argv)
{
    static CHAR CommandLine[ARGCV_LINE_LEN];
    static CHAR *Argv[ARGCV_MAX_WORDS];
    int Argc;
    CHAR *p;

    strncpy(CommandLine, Str, ARGCV_LINE_LEN - 1);
    CommandLine[ARGCV_LINE_LEN] = 0;

    for (Argc = 1, Argv[0] = "wntdrvs", p = strtok(CommandLine, " \t\n\r");
         p != NULL && Argc < ARGCV_MAX_WORDS - 1;
         p = strtok(NULL, " \t\n\r"))
	Argv[Argc++] = p;

    Argv[Argc] = NULL;

    *argc = Argc;
    *argv = Argv;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of wntdrvs - Windows NT graphics driver of IRIT.        	     M
*                                                                            *
* PARAMETERS:                                                                M
*   hInst, hPrevInst, lpszLine, nShow: Command line and wierd Windows staff. M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:         Exit code.                                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   WinMain                                                                  M
*****************************************************************************/
int PASCAL WinMain(HANDLE hInst, HANDLE hPrevInst, LPSTR lpszLine, int nShow)
{
    int argc;
    char **argv;
    HWND hWndFrame;
    MSG msg;
    int TransPrefPos[4], ViewPrefPos[4];

    GetArgCV(lpszLine, &argc, &argv);
    IGConfigureGlobals("wntdrvs", argc, argv);

    IGBackGroundColor = RGB(IGGlblBackGroundColor[0],
		            IGGlblBackGroundColor[1],
			    IGGlblBackGroundColor[2]);
    IGBackGroundBrush = CreateSolidBrush(IGBackGroundColor);

    if (!hPrevInst) {
	WNDCLASS wndClass;
	int Registered = TRUE;

	/* Set up common defaults. */
        wndClass.style         = CS_HREDRAW | CS_VREDRAW;
	wndClass.cbClsExtra    = 0;
	wndClass.cbWndExtra    = 0;
	wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wndClass.hInstance     = hInst;

	/* Register top level main window. */
	wndClass.lpfnWndProc   = WndProc;
	wndClass.hIcon         = LoadIcon(hInst, APP_CLASS);
	wndClass.hbrBackground = GetStockObject(BLACK_BRUSH);
	wndClass.lpszMenuName  = APP_CLASS;
	wndClass.lpszClassName = APP_CLASS;
	if (!RegisterClass(&wndClass))
	    return FALSE;

	/* Register viewing window. */
	wndClass.lpfnWndProc   = ViewWndProc;
	wndClass.hIcon         = NULL;
	wndClass.hbrBackground = CreateSolidBrush(IGBackGroundColor);
	wndClass.lpszClassName = APP_VIEW_CLASS;
	if (!RegisterClass(&wndClass))
	    return FALSE;

	/* Register transformation window. */
	wndClass.lpfnWndProc   = TransWndProc;
	wndClass.lpszClassName = APP_TRANS_CLASS;
	if (!RegisterClass(&wndClass))
	    return FALSE;
    }

    if (sscanf(IGGlblTransPrefPos, "%d,%d,%d,%d",
	       &TransPrefPos[0], &TransPrefPos[1],
	       &TransPrefPos[2], &TransPrefPos[3]) == 4 &&
	sscanf(IGGlblViewPrefPos, "%d,%d,%d,%d",
	       &ViewPrefPos[0], &ViewPrefPos[1],
	       &ViewPrefPos[2], &ViewPrefPos[3]) == 4) {
	hWndFrame = CreateWindow(APP_CLASS,
				 APP_TITLE,
				 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
				 ViewPrefPos[0],
				 ViewPrefPos[2],
				 ViewPrefPos[1] - ViewPrefPos[0] +
				     TransPrefPos[1] - TransPrefPos[0],
				 MAX(ViewPrefPos[3] - ViewPrefPos[2],
				     TransPrefPos[3] - TransPrefPos[2]),
				 NULL,
				 NULL,
				 hInst,
				 NULL);
    }
    else if (sscanf(IGGlblTransPrefPos, "%d,%d,%d,%d",
		    &TransPrefPos[0], &TransPrefPos[1],
		    &TransPrefPos[2], &TransPrefPos[3]) == 2 &&
	     sscanf(IGGlblViewPrefPos, "%d,%d,%d,%d",
	            &ViewPrefPos[0], &ViewPrefPos[1],
	            &ViewPrefPos[2], &ViewPrefPos[3]) == 2) {
	hWndFrame = CreateWindow(APP_CLASS,
				 APP_TITLE,
				 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
				 CW_USEDEFAULT,
				 CW_USEDEFAULT,
				 ViewPrefPos[0],
				 ViewPrefPos[2],
				 NULL,
				 NULL,
				 hInst,
				 NULL);
    }
    else if (sscanf(IGGlblTransPrefPos, "%d,%d,%d,%d",
		    &TransPrefPos[0], &TransPrefPos[1],
		    &TransPrefPos[2], &TransPrefPos[3]) == 4 &&
	     sscanf(IGGlblViewPrefPos, "%d,%d,%d,%d",
	            &ViewPrefPos[0], &ViewPrefPos[1],
	            &ViewPrefPos[2], &ViewPrefPos[3]) == 4) {
    }
    else 
        hWndFrame = CreateWindow(APP_CLASS,
				 APP_TITLE,
				 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
				 CW_USEDEFAULT,
				 CW_USEDEFAULT,
				 CW_USEDEFAULT,
				 CW_USEDEFAULT,            
				 NULL,
				 NULL,
				 hInst,
				 NULL);

    msg.wParam = 1;
    if (hWndFrame) {
	HDC hDC;

	if (hDC = GetDC(hWndFrame)) {
	    int i;

	    for (i = 0; i <= IG_MAX_COLOR; i++) {
	        IGColorsHighIntensity[i] = GetNearestColor(hDC,
				RGB(Colors[i][0], Colors[i][1], Colors[i][2]));
	        IGColorsLowIntensity[i] = GetNearestColor(hDC,
		    RGB(Colors[i][0] / 2, Colors[i][1] / 2, Colors[i][2] / 2));
	    }
	    ReleaseDC(hWndFrame, hDC);
	}

        ShowWindow(hWndFrame, nShow);
        UpdateWindow(hWndFrame);

	/* Set up the input socket as a secondary threat, if we are to wait  */
	/* for input from input socket.					     */
	if (!IGGlblStandAlone)
	    _beginthread(GetInputFromSocket, 0, NULL);

        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    UnregisterClass(APP_CLASS, hInst);
    UnregisterClass(APP_VIEW_CLASS, hInst);
    UnregisterClass(APP_TRANS_CLASS, hInst);

    return msg.wParam;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Optionally construct a state pop up menu for the driver, if has one.       M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGCreateStateMenu                                                        M
*****************************************************************************/
void IGCreateStateMenu(void)
{
    if (GlblStateMenu) 
	DestroyMenu(GlblStateMenu);

    GlblStateMenu = CreatePopupMenu();

    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_MORE_SENSITIVE,
	       "More Sensitive");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_LESS_SENSITIVE,
	       "Less Sensitive");
    AppendMenu(GlblStateMenu,
	       SET_MENU_FLAGS(IGGlblTransformMode == IG_TRANS_SCREEN),
	       IG_STATE_SCR_OBJ_TGL, "Screen Coords.");
    AppendMenu(GlblStateMenu,
	       SET_MENU_FLAGS(IGGlblViewMode == IG_VIEW_PERSPECTIVE),
	       IG_STATE_PERS_ORTHO_TGL, "Perspective");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDepthCue),
	       IG_STATE_DEPTH_CUE, "Depth Cue");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDoDoubleBuffer),
	       IG_STATE_DOUBLE_BUFFER, "DoubleBuffer");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawSolid),
	       IG_STATE_DRAW_SOLID, "DrawSolid");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblBackFaceCull),
	       IG_STATE_BACK_FACE_CULL, "Back Face Cull");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawInternal),
	       IG_STATE_DRAW_INTERNAL, "Draw Internal Edges");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawVNormal),
	       IG_STATE_DRAW_VNORMAL, "Draw Vrtx Normals");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawPNormal),
	       IG_STATE_DRAW_PNORMAL, "Draw Poly Normals");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawSurfaceMesh),
	       IG_STATE_DRAW_SRF_MESH, "Draw Surface Mesh");
    AppendMenu(GlblStateMenu, SET_MENU_FLAGS(IGGlblDrawSurfacePoly),
	       IG_STATE_DRAW_SRF_POLY, "Surface Polygons");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_FOUR_PER_FLAT,
	       IGGlblFourPerFlat ? "Four Per Flat" : "Two Per Flat");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_MORE_ISOLINES,
	       "More Isolines");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_LESS_ISOLINES,
	       "Less Isolines");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_FINER_APPROX,
	       "Finer approximation");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_COARSER_APPROX,
	       "Coarser approximation");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_LONGER_VECTORS,
	       "Longer Vectors");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_SHORTER_VECTORS,
	       "Shorter Vectors");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_WIDER_LINES,
	       "Wider Lines");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_NARROW_LINES,
	       "Thinner Lines");
    AppendMenu(GlblStateMenu, MF_STRING, IG_STATE_CLEAR_VIEW,
	       "Clear View Area");
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* A secondary thread that waits for input from socket.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Data:      Not used.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetInputFromSocket(void *Data)
{
    while (TRUE) {
	if (IGReadObjectsFromSocket(IGGlblViewMode, &IGGlblDisplayList))
	    InvalidateRect(IGhWndView, NULL, FALSE);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Top level window call back drawing function.				     *
*									     *
* PARAMETERS:                                                                *
*   hWndFrame:       A handle on the window.                                 *
*   wMsg:            Type of event to handle.                                *
*   mParam, lParam:  Parameters of event.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static LONG APIENTRY WndProc(HWND hWndFrame,
			     UINT wMsg,
			     WPARAM wParam,
			     LONG lParam)
{
    static RECT rect;
    HDC hDC;
    TEXTMETRIC tm;
    int Refresh;

    switch (wMsg) {
        case WM_CREATE:
            if (hDC = GetDC(hWndFrame)) {
		SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
		GetTextMetrics(hDC, &tm);
		ReleaseDC(hWndFrame, hDC);
	    }

	    GetClientRect(hWndFrame, &rect);

	    IGhWndView = CreateWindow(APP_VIEW_CLASS,
				      "v",
				      WS_CHILD | WS_BORDER | WS_VISIBLE |
				      WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
				      0,
				      0,
				      IGViewWidth = (rect.right * 3) / 4 - 2,
				      IGViewHeight = rect.bottom,
				      hWndFrame,
				      0,
				      ((LPCREATESTRUCT) lParam)->hInstance,
				      NULL);
	    IGViewWidth2 = IGViewWidth / 2;
	    IGViewHeight2 = IGViewHeight / 2;

	    IGhWndTrans = CreateWindow(APP_TRANS_CLASS,
				       "t",
				       WS_CHILD | WS_BORDER | WS_VISIBLE,
				       (rect.right * 3) / 4,
				       0,
				       TransWidth = rect.right / 4,
				       TransHeight = rect.bottom,
				       hWndFrame,
				       0,
				       ((LPCREATESTRUCT) lParam)->hInstance,
				       NULL);
	    TransWidth2 = TransWidth / 2;
	    TransHeight2 = TransHeight / 2;
 	    return 0;

	case WM_SIZE:
	    rect.left   = 0;
	    rect.top    = 0;
	    rect.right  = LOWORD(lParam);
	    rect.bottom = HIWORD(lParam);

	    SetWindowPos(IGhWndView,
			 HWND_TOP,
			 0,
			 0,
			 IGViewWidth = (rect.right * 3) / 4 - 2,
			 IGViewHeight = rect.bottom,
			 0);
	    IGViewWidth2 = IGViewWidth / 2;
	    IGViewHeight2 = IGViewHeight / 2;

	    SetWindowPos(IGhWndTrans,
			 HWND_TOP,
			 (rect.right * 3) / 4,
			 0,
			 TransWidth = rect.right / 4 - 1,
			 TransHeight = rect.bottom - 1,
			 0);
	    TransWidth2 = TransWidth / 2;
	    TransHeight2 = TransHeight / 2;
	    return 0;

	case WM_QUERYNEWPALETTE:
	case WM_PALETTECHANGED:
	    if (wParam != (WPARAM) hWndFrame)
		SendMessage(IGhWndView, wMsg, 0, 0L);
	    return 0;

	case WM_DESTROY:
	    PostQuitMessage(0);
	    return 0;

        case WM_COMMAND:
	    Refresh = FALSE;
            switch (LOWORD(wParam)) {
		case IDM_FILE_SAVE:
		    IGSaveCurrentMat(IGGlblViewMode, NULL);
		    break;
		case IDM_FILE_SAVE_AS:
		    {
			OPENFILENAME ofn;
			char FileName[LINE_LEN_LONG];

			FileName[0] = 0;

			memset(&ofn, 0, sizeof(OPENFILENAME));
			ofn.lStructSize = sizeof(OPENFILENAME);
			ofn.hwndOwner = hWndFrame;
			ofn.lpstrFile = FileName;
			ofn.nMaxFile = LINE_LEN_LONG;
			ofn.lpstrTitle = "Save View Matrix";
			ofn.Flags = OFN_OVERWRITEPROMPT;

		        if (GetOpenFileName(&ofn) && strlen(FileName) > 0)
			IGSaveCurrentMat(IGGlblViewMode, FileName);
		    }
		    break;
		case IDM_FILE_QUIT:
		    exit(0);
		    break;

		case IDM_MOUSE_MORE:
		    IGHandleState(IG_STATE_MORE_SENSITIVE, TRUE);
		    break;
		case IDM_MOUSE_LESS:
		    IGHandleState(IG_STATE_LESS_SENSITIVE, TRUE);
		    break;

		case IDM_STATE_MORE_ISO:
		    Refresh = IGHandleState(IG_STATE_MORE_ISOLINES, TRUE);
		    break;
		case IDM_STATE_LESS_ISO:
		    Refresh = IGHandleState(IG_STATE_LESS_ISOLINES, TRUE);
		    break;
		case IDM_STATE_FINER_APPROX:
		    Refresh = IGHandleState(IG_STATE_FINER_APPROX, TRUE);
		    break;
		case IDM_STATE_COARSER_APPROX:
		    Refresh = IGHandleState(IG_STATE_COARSER_APPROX, TRUE);
		    break;
		case IDM_STATE_SHORTER_VEC:
		    Refresh = IGHandleState(IG_STATE_SHORTER_VECTORS, TRUE);
		    break;
		case IDM_STATE_LONGER_VEC:
		    Refresh = IGHandleState(IG_STATE_LONGER_VECTORS, TRUE);
		    break;
		case IDM_STATE_WIDE_LINES:
		    Refresh = IGHandleState(IG_STATE_WIDER_LINES, TRUE);
		    break;
		case IDM_STATE_THIN_LINES:
		    Refresh = IGHandleState(IG_STATE_NARROW_LINES, TRUE);
		    break;

		case IDM_TGLS_SCREEN:
		    IGHandleState(IG_STATE_SCR_OBJ_TGL, TRUE);
		    break;
		case IDM_TGLS_PERSP:
		    Refresh = IGHandleState(IG_STATE_PERS_ORTHO_TGL, TRUE);
		    break;
		case IDM_TGLS_DEPTH_CUE:
		    Refresh = IGHandleState(IG_STATE_DEPTH_CUE, TRUE);
		    break;
		case IDM_TGLS_DOUBLE_BUFFER:
		    Refresh = IGHandleState(IG_STATE_DOUBLE_BUFFER, TRUE);
		    break;
		case IDM_TGLS_DRAW_SOLID:
		    Refresh = IGHandleState(IG_STATE_DRAW_SOLID, TRUE);
		    break;
		case IDM_TGLS_INTERNAL:
		    Refresh = IGHandleState(IG_STATE_DRAW_INTERNAL, TRUE);
		    break;
		case IDM_TGLS_VRTX_NRML:
		    Refresh = IGHandleState(IG_STATE_DRAW_VNORMAL, TRUE);
		    break;
		case IDM_TGLS_POLY_NRML:
		    Refresh = IGHandleState(IG_STATE_DRAW_PNORMAL, TRUE);
		    break;
		case IDM_TGLS_CTL_MESH:
		    Refresh = IGHandleState(IG_STATE_DRAW_SRF_MESH, TRUE);
		    break;
		case IDM_TGLS_SRF_POLYS:
		    Refresh = IGHandleState(IG_STATE_DRAW_SRF_POLY, TRUE);
		    break;
		case IDM_TGLS_4_PER_FLAT:
		    Refresh = IGHandleState(IG_STATE_FOUR_PER_FLAT, TRUE);
		    break;

		case IDM_VIEW_FRONT:
		    Refresh = IGHandleState(IG_STATE_VIEW_FRONT, TRUE);
		    break;
		case IDM_VIEW_SIDE:
		    Refresh = IGHandleState(IG_STATE_VIEW_SIDE, TRUE);
		    break;
		case IDM_VIEW_TOP:
		    Refresh = IGHandleState(IG_STATE_VIEW_TOP, TRUE);
		    break;
		case IDM_VIEW_ISOMETRY:
		    Refresh = IGHandleState(IG_STATE_VIEW_ISOMETRY, TRUE);
		    break;
            }
	    if (Refresh)
		InvalidateRect(IGhWndView, NULL, FALSE);
            break;

        default:
	    return DefWindowProc(hWndFrame, wMsg, wParam, lParam);
    }
    return 0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* View window call back drawing function.				     *
*									     *
* PARAMETERS:                                                                *
*   hWndFrame:       A handle on the window.                                 *
*   wMsg:            Type of event to handle                                 *
*   mParam, lParam:  Parameters of event                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static LONG APIENTRY ViewWndProc(HWND hWndFrame,
				 UINT wMsg,
				 WPARAM wParam,
				 LONG lParam)
{
    switch (wMsg) {
	case WM_CREATE:
	case WM_PAINT:
	case WM_QUERYNEWPALETTE:
	case WM_PALETTECHANGED:
	    return RedrawViewWindow(hWndFrame, wMsg, wParam);

	case WM_DESTROY:
	    PostQuitMessage(0);
	    return 0;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	    IGGlblAbortKeyPressed = TRUE;
	    break;

	default:
	    return DefWindowProc(hWndFrame, wMsg, wParam, lParam);
    }
    return 0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Trans window call back drawing function.				     *
*									     *
* PARAMETERS:                                                                *
*   hWndFrame:       A handle on the window.                                 *
*   wMsg:            Type of event to handle                                 *
*   mParam, lParam:  Parameters of event                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static LONG APIENTRY TransWndProc(HWND hWndFrame,
				  UINT wMsg,
				  WPARAM wParam,
				  LONG lParam)
{
    static IGGraphicEventType
	LastEvent = IG_EVENT_NONE;
    static BOOL
	bLeftBtnDown = FALSE;
    static int
	iMouseX = 0,
	iMouseY = 0;
    int iNewMouseX, iNewMouseY;
    IGGraphicEventType Event;
    RealType ChangeFactor;
    POINT Point;

    switch (wMsg) {
	case WM_PAINT:
	    RedrawTransformationWindow(hWndFrame);
	    return 0;

	case WM_RBUTTONDOWN:
	    if (!GlblStateMenu)
		IGCreateStateMenu();

	    Point.x = LOWORD(lParam);
	    Point.y = HIWORD(lParam);
	    ClientToScreen(hWndFrame, &Point);

	    /* Activate the pop up menu. Events goes to WM_COMMAND here. */
	    TrackPopupMenu(GlblStateMenu,
			   TPM_CENTERALIGN | TPM_RIGHTBUTTON,
			   Point.x, Point.y, 0, hWndFrame, NULL);
	    return 0;

	case WM_LBUTTONDOWN:
	    /* Save fact that button is pressed. Might be a drag operation.*/
	    bLeftBtnDown = TRUE;
	    iMouseX = LOWORD(lParam);
	    iMouseY = HIWORD(lParam);

	    if ((Event = GetGraphicEvent(&ChangeFactor,
					 iMouseX, iMouseY)) != IG_EVENT_NONE) {
		if (Event == IG_EVENT_QUIT) {
		    exit(0);		   /* Not the nicest ways to quit. */
		}
		else {
		    if (IGProcessEvent(Event,
				       ChangeFactor * IGGlblChangeFactor))
		        InvalidateRect(IGhWndView, NULL, FALSE);

		    /* Save the event in case drag operation is performed. */
		    LastEvent = Event;
		}
	    }
	    return 0;

	case WM_LBUTTONUP:
	    /* Done with mouse click or drag operation. */
	    bLeftBtnDown = FALSE;
	    return 0;

	case WM_MOUSEMOVE:
	    if (bLeftBtnDown && IG_IS_DRAG_EVENT(LastEvent)) {
		iNewMouseX = LOWORD(lParam);
		iNewMouseY = HIWORD(lParam);
		ChangeFactor = (iNewMouseX - iMouseX) /
						     ((RealType) TransWidth);
		iMouseX = iNewMouseX;
		if (IGProcessEvent(LastEvent,
				   ChangeFactor * IGGlblChangeFactor))
		    InvalidateRect(IGhWndView, NULL, FALSE);
	    }
	    return 0;

	case WM_DESTROY:
	    PostQuitMessage(0);
	    return 0;

	case WM_COMMAND:
	    /* Comamnds from the popup menu. */
	    if (IGHandleState(wParam, TRUE))
		InvalidateRect(IGhWndView, NULL, FALSE);
	    return 0;

	default:
	    return DefWindowProc(hWndFrame, wMsg, wParam, lParam);
    }
    return 0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Handles input events                                                       *
*                                                                            *
* PARAMETERS:                                                                *
*   ChangeFactor:        A continuous numeric value between -1 and 1. This   *
*			 value will be used to set amount of event such as   *
*			 rotation or translation.			     *
*   X, Y:		 Location of mouse event.			     *
*                                                                            *
* RETURN VALUE:                                                              *
*   IGGraphicEventType:  Type of new event.                                  *
*****************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor, int X, int Y)
{
    int i;
    IGGraphicEventType
	RetVal = IG_EVENT_NONE;
    RealType XPos, YPos;

    XPos = ((RealType) X) / TransWidth;
    YPos = 1.0 - ((RealType) Y) / TransHeight;

    /* Make sure we are in bound in the X direction. */
    if (XPos < (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0 ||
        XPos > 1.0 - (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0)
	return IG_EVENT_NONE;

    /* Now search the sub window the event occured in. */
    for (i = 0; i < INTERACT_NUM_OF_SUB_WNDWS; i++) {
        if (InteractMenu.SubWindows[i].Y <= YPos &&
	    InteractMenu.SubWindows[i].Y + INTERACT_SUB_WINDOW_HEIGHT >=
								      YPos) {
	    RetVal = InteractMenu.SubWindows[i].Event;
	    break;
	}
    }

    /* Take care of special cases in which the window should be updated. */
    switch (RetVal) {
	case IG_EVENT_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    InvalidateRect(IGhWndTrans, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
	case IG_EVENT_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    InvalidateRect(IGhWndTrans, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
	case IG_EVENT_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    InvalidateRect(IGhWndTrans, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
    }

    *ChangeFactor = (((RealType) X) - TransWidth2) / TransWidth2;

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Handles the events of the pop up window.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   State:      Event to handle.                                             M
*   Refresh:    Do we need to refresh the screen according to what we know   M
*		on entry.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE, if we need to refresh the screen.                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGHandleState                                                            M
*****************************************************************************/
int IGHandleState(int State, int Refresh)
{
    int UpdateView = TRUE;

    switch (State) {
	case IG_STATE_DRAW_SOLID:
	    IGGlblDrawSolid = !IGGlblDrawSolid;
	    /* And disable the depth cueing. */
	    IGGlblDepthCue = TRUE; 
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    InvalidateRect(IGhWndTrans, NULL, TRUE);
	    break;
	case IG_STATE_DOUBLE_BUFFER:
	    IGGlblDoDoubleBuffer = !IGGlblDoDoubleBuffer;
	    break;
        case IG_STATE_SCR_OBJ_TGL:
	case IG_STATE_PERS_ORTHO_TGL:
	    InvalidateRect(IGhWndTrans, NULL, TRUE);
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Redraws the transformation window.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   hwnd:       A handle on the window.                                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void RedrawTransformationWindow(HWND hWnd)
{
    int i;
    long SubTransPosX, SubTransPosY, SubTransWidth, SubTransHeight;
    RECT rect;
    PAINTSTRUCT ps;

    /* Make sure the menu is consistent with internatal data. */
    InteractMenu.SubWindows[0].Str =
	IGGlblTransformMode == IG_TRANS_OBJECT ? "Object Coords."
					       : "Screen Coords.";
    InteractMenu.SubWindows[1].Str =
	IGGlblViewMode == IG_VIEW_PERSPECTIVE ? "Perspective" : "Orthographic";
    InteractMenu.SubWindows[10].Str =
	IGGlblDepthCue ? "Depth Cue" : "No Depth Cue";

    SubTransWidth = (int) (TransWidth * INTERACT_SUB_WINDOW_WIDTH);
    SubTransHeight = (int) (TransHeight * INTERACT_SUB_WINDOW_HEIGHT);
    SubTransPosX = (TransWidth - SubTransWidth) / 2;

    if (IGCurrenthDC = BeginPaint(hWnd, &ps)) {
	GetClientRect(hWnd, &rect);
	FillRect(IGCurrenthDC, &rect, IGBackGroundBrush);

	SetBkMode(IGCurrenthDC, TRANSPARENT);
	SetTextAlign(IGCurrenthDC, TA_CENTER | VTA_CENTER);

	for (i = 0; i < INTERACT_NUM_OF_SUB_WNDWS; i++) {
	    SetColorIndex2(InteractMenu.SubWindows[i].Color, 2);
	    SetTextColor(IGCurrenthDC, IGCrntColorHighIntensity);

	    SubTransPosY = (int) (TransHeight *
					(1.0 - InteractMenu.SubWindows[i].Y));

	    MoveToEx(IGCurrenthDC, SubTransPosX, SubTransPosY, NULL);
	    LineTo(IGCurrenthDC, SubTransPosX + SubTransWidth, SubTransPosY);
	    LineTo(IGCurrenthDC,
		   SubTransPosX + SubTransWidth, SubTransPosY - SubTransHeight);
	    LineTo(IGCurrenthDC, SubTransPosX, SubTransPosY - SubTransHeight);
	    LineTo(IGCurrenthDC, SubTransPosX, SubTransPosY);
	    if (InteractMenu.SubWindows[i].TextInside) {
	        DrawTextLocal(InteractMenu.SubWindows[i].Str,
			      TransWidth / 2,
			      SubTransPosY - SubTransHeight);
	    }
	    else {
		DrawTextLocal(InteractMenu.SubWindows[i].Str,
			      (TransWidth - SubTransWidth) / 3,
			      SubTransPosY - SubTransHeight);
		MoveToEx(IGCurrenthDC,
			 SubTransPosX + SubTransWidth / 2, SubTransPosY, NULL);
		LineTo(IGCurrenthDC,
		       SubTransPosX + SubTransWidth / 2,
		       SubTransPosY - SubTransHeight);
	    }
	}

	for (i = 0; i < INTERACT_NUM_OF_STRINGS; i++) {
	    SetColorIndex2(InteractMenu.Strings[i].Color, 2);
	    SetTextColor(IGCurrenthDC, IGCrntColorHighIntensity);

	    DrawTextLocal(InteractMenu.Strings[i].Str,
		  (int) (InteractMenu.Strings[i].X * TransWidth),
		  (int) ((1.0 - InteractMenu.Strings[i].Y) * TransHeight
						- SubTransHeight / 2));
	}

	if (IGCurrenthPen)
            DeleteObject(SelectObject(IGCurrenthDC, IGCurrenthPen));
	EndPaint(hWnd, &ps);
	IGCurrenthDC = 0;
	IGCurrenthPen = 0;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Draw text centered at the given position.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Str:         Text to draw.                                               *
*   PosX, PosY:  And where to draw it.                                       *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DrawTextLocal(char *Str, int PosX, int PosY)
{
    TextOut(IGCurrenthDC, PosX, PosY, Str, strlen(Str));
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets the color/width according to the given color index/width.   	     *
*                                                                            *
* PARAMETERS:                                                                *
*   color:     Index of color to use. Must be between 0 and IG_MAX_COLOR.    *
*   width:     In pixel, for line draw.                                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetColorIndex2(int color, int width)
{
    if (color > IG_MAX_COLOR)
	color = IG_IRIT_WHITE;

    IGCrntColorHighIntensity = IGColorsHighIntensity[color];
    IGCrntColorLowIntensity = IGColorsLowIntensity[color];

    if (!IGCurrenthDC)
	return;
    if (IGCurrenthPen)
        DeleteObject(SelectObject(IGCurrenthDC, IGCurrenthPen));
    IGCurrenthPen = SelectObject(IGCurrenthDC, CreatePen(PS_SOLID, width,
						     IGCrntColorHighIntensity));

    IGGlblIntensityHighState = TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Make some sound.                                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGIritBeep                                                               M
*****************************************************************************/
void IGIritBeep(void)
{
    Beep(1000, 100);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Should we stop this animation?                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   Anim:     The animation to abort.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      TRUE if we need to abort, FALSE otherwise.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimCheckInterrupt                                                       M
*****************************************************************************/
int AnimCheckInterrupt(AnimationStruct *Anim)
{
    return FALSE;
}
