/*==========================================================================
 *
 *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
 *
 *  File: viewer.cpp
 *
 ***************************************************************************/



#define INITGUID
#include "d3drmwin.h"
#include "resource.h"

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <direct.h>
#include <mmsystem.h>

#include "rlds3d.h"
#include "file.h"


static char ViewerClass[32] = "ViewerClass";

static BOOL FirstInstance(HANDLE);
static BOOL AnyInstance(HANDLE, HWND*);
long FAR PASCAL WindowProc(HWND, UINT, WPARAM, LPARAM);
static void Idle();

BOOL CALLBACK WaitBoxDlgProc(HWND, UINT, WPARAM, LPARAM);

BOOL AboutBoxOn = FALSE;

BOOL    gbFatalError = FALSE;       // Global flag to exit cleanly


/*
 * Initialization, message loop
 */

int PASCAL WinMain
    (HINSTANCE this_inst, HINSTANCE prev_inst, LPSTR cmdline, int cmdshow)
{
    MSG         msg;
    int         idle;
    int         done = FALSE;
    HACCEL      accel;


	DWORD           prev_tick;
	DWORD           this_tick;
	D3DVALUE        time_delta;

    prev_inst = prev_inst;
    cmdline = cmdline;

	// Register our class if necessary
    if (!prev_inst) {
		// Do the stuff for if this is the first instance of the program.
		if (!FirstInstance(this_inst)) return FALSE;
	}

	// Create our window and get it's handle (needs to be done whether we're first or not)
	HWND win;
    if (!AnyInstance(this_inst, &win)) return FALSE;

	// Initialize the RLDS3D interface
	if (!RLDS3D_Initialize(win, this_inst)) {
		// If it doesn't initialize then we don't have anything we need to clean up, so we just exit
		exit(-1);
	}

	// Show our window!
    ShowWindow(win, cmdshow);
    UpdateWindow(win);

	// Load accelerators
    accel = LoadAccelerators((HINSTANCE)this_inst, "ViewerAccel");

	prev_tick = timeGetTime();


    while (!done)
    {   idle = TRUE;
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{   idle = FALSE;
	    if (msg.message == WM_QUIT)
	    {   done = TRUE;
		break;
	    }

	    if (!TranslateAccelerator(msg.hwnd, accel, &msg))
	    {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	    }

	}
		if (idle && !gbFatalError)
            Idle();
		this_tick = timeGetTime();
		time_delta = D3DVAL((float)((this_tick - prev_tick)/1000.0));
		prev_tick = this_tick;
		if (!gbFatalError)
            RLDS3D_Render(time_delta);
    }
    if (!gbFatalError)
	    RLDS3D_Deinitialize();
	return msg.wParam;
}

/*
 * Register window class for the application, and do any other
 * application initialization
 */
static BOOL FirstInstance(HANDLE this_inst)
{
    WNDCLASS    wc;
    BOOL        rc;

    /*
     * set up and register window class
     */
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = (HINSTANCE)this_inst;
    wc.hIcon = LoadIcon((HINSTANCE)this_inst, "ViewerIcon");
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = "ViewerMenu";
    wc.lpszClassName = ViewerClass;
    rc = RegisterClass(&wc);

    return rc;
}

/*
 * Do work required for every instance of the application:
 * create the window, initialize data
 */
static BOOL AnyInstance(HANDLE this_inst, HWND* window_handle)
{
    HWND win;
    char szCaption[] = "DirectSound3D Demo";

    /*
     * create main window
     */
    win =
	CreateWindow
	(   ViewerClass,                /* class */
	    szCaption,                  /* caption */
	    WS_OVERLAPPEDWINDOW,        /* style */
	    CW_USEDEFAULT,              /* init. x pos */
	    CW_USEDEFAULT,              /* init. y pos */
	    400,                        /* init. x size */
	    400,                        /* init. y size */
	    NULL,                       /* parent window */
	    NULL,                       /* menu handle */
	    (HINSTANCE)this_inst,       /* program handle */
	    NULL                        /* create parms */
	);


    if (!win) return FALSE;
	*window_handle = win;
    return TRUE;
}

/*
 * Processes messages for the about dialog.
 */
BOOL FAR PASCAL AboutDlgProc
    (HWND win, unsigned msg, WORD wparam, LONG lparam)
{
    lparam = lparam;

    switch (msg)
    {
    case WM_INITDIALOG:
	return TRUE;

    case WM_COMMAND:
	if (wparam == IDOK)
	{
	    AboutBoxOn = FALSE;
	    EndDialog(win, TRUE);
	    return TRUE;
	}
	break;
    case WM_CLOSE:
	{
	    AboutBoxOn = FALSE;
	    EndDialog(win, TRUE);
	    return FALSE;
	}
	break;
    }
    return FALSE;
}

static int
    left_drag = FALSE, right_drag = FALSE,
    last_x, last_y;

static void Idle()
{
	// Stop selected item being dragged from rotating/moving
    if (left_drag) {
		RLDS3D_SetSelectedRotRelToCam(D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0));
	}
    if (right_drag) {
		RLDS3D_SetSelectedVelRelToCam(D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
	}               
}

static int FillModeToMenuItem(D3DRMFILLMODE fm)
{
    switch (fm) {
    case D3DRMFILL_POINTS:
	return 2;

    case D3DRMFILL_WIREFRAME:
	return 3;

    case D3DRMFILL_SOLID:
	return 4;
    }
    return -1;
}

// Sets the polygon fill mode and checks/unchecks the appropriate menu items
static void SetFillMode(HWND win, D3DRMFILLMODE fm)
{
    HMENU menu;
    D3DRMFILLMODE oldfm = RLDS3D_GetPolygonFillMode();
    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);
    CheckMenuItem(menu, FillModeToMenuItem(oldfm),
		  MF_BYPOSITION|MF_UNCHECKED);
    CheckMenuItem(menu, FillModeToMenuItem(fm),
		  MF_BYPOSITION|MF_CHECKED);
	RLDS3D_SetPolygonFillMode(fm);
}

static int ShadeModeToMenuItem(D3DRMSHADEMODE sm)
{
    switch (sm) {
    case D3DRMSHADE_FLAT:
	return 6;

    case D3DRMSHADE_GOURAUD:
	return 7;

    case D3DRMSHADE_PHONG:
	return 8;
    }
    return -1;
}

// Sets the polygon shade mode and checks/unchecks the appropriate menu items
static void SetShadeMode(HWND win, D3DRMSHADEMODE sm)
{
    HMENU menu;
    D3DRMSHADEMODE oldsm = RLDS3D_GetPolygonShadeMode();

    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);
    CheckMenuItem(menu, ShadeModeToMenuItem(oldsm),
		  MF_BYPOSITION|MF_UNCHECKED);
    CheckMenuItem(menu, ShadeModeToMenuItem(sm),
		  MF_BYPOSITION|MF_CHECKED);
	RLDS3D_SetPolygonShadeMode(sm);
}

// Sets the rendering model and checks/unchecks the appropriate menu items
static void SetModel(HWND win, D3DRMCOLORMODEL model)
{
    HMENU menu;
    D3DRMCOLORMODEL oldModel = RLDS3D_GetColourModel();

    if (oldModel == model) return;

    RLDS3D_SetColourModel(model);
    
    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);
    CheckMenuItem(menu, 9 + (int) oldModel, MF_BYPOSITION|MF_UNCHECKED);
    CheckMenuItem(menu, 9 + (int) model, MF_BYPOSITION|MF_CHECKED);
}

// Toggles dithering and checks/unchecks menu item
static void ToggleDither(HWND win)
{
    HMENU menu;
    int dither = RLDS3D_GetDither();
    int checked;
    dither = !dither;
	RLDS3D_SetDither(dither);

    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);

    if (dither) checked = MF_CHECKED;
    else checked = MF_UNCHECKED;

    CheckMenuItem(menu, 13, MF_BYPOSITION|checked);
}

// Toggles texture filtering and checks/unchecks menu item
static void ToggleTextureFiltering(HWND win)
{
    HMENU menu;
    D3DRMTEXTUREQUALITY tq = RLDS3D_GetTextureQuality();
    int checked;
    if (tq == D3DRMTEXTURE_NEAREST)
	tq = D3DRMTEXTURE_LINEAR;
    else
	tq = D3DRMTEXTURE_NEAREST;

    RLDS3D_SetTextureQuality(tq);
    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);

    if (tq == D3DRMTEXTURE_LINEAR) checked = MF_CHECKED;
    else checked = MF_UNCHECKED;

    CheckMenuItem(menu, 14, MF_BYPOSITION|checked);
}

// Ditto for lighting
static void ToggleLighting(HWND win)
{
    HMENU menu;
    menu = GetMenu(win);
    menu = GetSubMenu(menu, 2);
    if (RLDS3D_GetLighting()) {
	    CheckMenuItem(menu, 0, (MF_BYPOSITION | MF_UNCHECKED));
		RLDS3D_SetLighting(FALSE);
	}
	else {
	    CheckMenuItem(menu, 0, (MF_BYPOSITION | MF_CHECKED));
		RLDS3D_SetLighting(TRUE);
	}
}

/*
** Windows procs for the sound param dlg boxes
*/

BOOL CALLBACK WaitBoxDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) {
	return FALSE;
};

BOOL CALLBACK GlobalParamDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam)
{
    lparam = lparam;

    char lpszTS[100];

    switch (msg)
    {
		case WM_INITDIALOG:
			{
				// Set up our edit fields appropriately
		D3DVALUE temp;
				RLDS3D_GetDistanceFactor(&temp);
				sprintf(lpszTS, "%f",temp);
				SendDlgItemMessage(win, IDC_DISTANCEFACTOR, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_DISTANCEFACTOR, EM_SETLIMITTEXT, 10, 0);
				RLDS3D_GetRolloffFactor(&temp);
				sprintf(lpszTS, "%f",temp);
				SendDlgItemMessage(win, IDC_ROLLOFFFACTOR, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_ROLLOFFFACTOR, EM_SETLIMITTEXT, 10, 0);
				RLDS3D_GetDopplerFactor(&temp);
				sprintf(lpszTS, "%f",temp);
				SendDlgItemMessage(win, IDC_DOPPLERFACTOR, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_DOPPLERFACTOR, EM_SETLIMITTEXT, 10, 0);
				return TRUE;
			}
		case WM_CLOSE:
			EndDialog(win, TRUE);
			return FALSE;

		case WM_COMMAND:
	    {
		if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_DOPPLERFACTOR) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_DOPPLERFACTOR, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_DOPPLERFACTOR, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					// No real need for error checking 'cause the edit field is numbers only
					RLDS3D_SetDopplerFactor(D3DVAL(atof(lpszTS)));
		    return TRUE;
		}
		else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_ROLLOFFFACTOR) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_ROLLOFFFACTOR, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_ROLLOFFFACTOR, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					RLDS3D_SetRolloffFactor(D3DVAL(atof(lpszTS)));
		    return TRUE;
		}
		else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_DISTANCEFACTOR) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_DISTANCEFACTOR, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_DISTANCEFACTOR, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					RLDS3D_SetDistanceFactor(D3DVAL(atof(lpszTS)));
		    return TRUE;
		}
		else return FALSE;
	    }               
	break;
    }
    return FALSE;
}

HWND hSelCfgDlg = 0;

BOOL CALLBACK SelectedParamDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam)
{
    lparam = lparam;

    char lpszTS[100];

    switch (msg)
    {
		case WM_INITDIALOG:
			{
				// Set up our edit fields appropriately
		D3DVALUE temp;
				LONG temp2;
				DWORD temp3, temp4;
				// Ignores the out side cone angle for now, which is fine, because elsewhere we
				// set it to equal the inside angle
				RLDS3D_GetSelConeAngles(&temp3, &temp4);
				sprintf(lpszTS, "%i",temp3);
				SendDlgItemMessage(win, IDC_CONEANGLE, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_CONEANGLE, EM_SETLIMITTEXT, 10, 0);
				RLDS3D_GetSelConeOutsideVolume(&temp2);
				sprintf(lpszTS, "%i",temp2);
				SendDlgItemMessage(win, IDC_CONEOUTSIDEVOLUME, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_CONEOUTSIDEVOLUME, EM_SETLIMITTEXT, 10, 0);
				RLDS3D_GetSelMaximumDistance(&temp);
				sprintf(lpszTS, "%f",temp);
				SendDlgItemMessage(win, IDC_MAXIMUMDISTANCE, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_MAXIMUMDISTANCE, EM_SETLIMITTEXT, 10, 0);
				RLDS3D_GetSelMinimumDistance(&temp);
				sprintf(lpszTS, "%f",temp);
				SendDlgItemMessage(win, IDC_MINIMUMDISTANCE, WM_SETTEXT, 0, (LPARAM) lpszTS);
				SendDlgItemMessage(win, IDC_MINIMUMDISTANCE, EM_SETLIMITTEXT, 10, 0);
				hSelCfgDlg = win;
				return TRUE;
			}

		case WM_CLOSE:
			EndDialog(win, TRUE);
			hSelCfgDlg = 0;
			return FALSE;

		case WM_COMMAND:
	    {
		if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_CONEANGLE) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_CONEANGLE, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_CONEANGLE, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					// No real need for error checking 'cause the edit field is numbers only
					RLDS3D_SetSelConeAngles((DWORD)atol(lpszTS), (DWORD)atol(lpszTS));
		    return TRUE;
		}
		else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_CONEOUTSIDEVOLUME) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_CONEOUTSIDEVOLUME, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_CONEOUTSIDEVOLUME, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					RLDS3D_SetSelConeOutsideVolume(atol(lpszTS));
		    return TRUE;
		}
		else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_MAXIMUMDISTANCE) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_MAXIMUMDISTANCE, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_MAXIMUMDISTANCE, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					RLDS3D_SetSelMaximumDistance(D3DVAL(atof(lpszTS)));
		    return TRUE;
		}
		else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_MINIMUMDISTANCE) {
					// Get value from edit box (get length, check lenth, set first char to length, request string, end it with 0)
		    int stringlength = SendDlgItemMessage(win, IDC_MINIMUMDISTANCE, EM_LINELENGTH, 0, 0);
		    if (stringlength > 10) return TRUE;
		    lpszTS[0] = (char)stringlength;
		    SendDlgItemMessage(win, IDC_MINIMUMDISTANCE, EM_GETLINE, 0, (LPARAM) lpszTS);
		    lpszTS[stringlength] = 0;
					// Set value in RLDS3D (convert string to long and go crazy with the bad boy)
					RLDS3D_SetSelMinimumDistance(D3DVAL(atof(lpszTS)));
		    return TRUE;
		}
		else return FALSE;
	    }               
	break;
    }
    return FALSE;
}

#define SIGN_EXTEND(w)    ((((int)(w)) << 16) >> 16)

/*
 * Handle messages for the main application window
 */

LONG FAR PASCAL WindowProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam)
{
    HANDLE inst_handle;
    static HCURSOR oldCursor = NULL;

    switch (msg)
    {
    case WM_KEYDOWN:
	{   
	    switch (wparam)
	    {
			
	    case 'A':
				RLDS3D_SetCamVelRelToCam(D3DVAL(5.0), D3DVAL(0.0), D3DVAL(0.0));
		break;

	    case 'Z':
				RLDS3D_SetCamVelRelToCam(D3DVAL(-5.0), D3DVAL(0.0), D3DVAL(0.0));
		break;

	    case VK_RIGHT:
				RLDS3D_SetCamRotUp(D3DVAL(1.0));
		break;
	    case VK_LEFT:
				RLDS3D_SetCamRotUp(D3DVAL(-1.0));
		break;
			case VK_UP:
				RLDS3D_SetCamRotRight(D3DVAL(1.0));
				break;
			case VK_DOWN:
				RLDS3D_SetCamRotRight(D3DVAL(-1.0));
				break;
			}
	}
	break;

    case WM_KEYUP:
	switch (wparam)
	{
		case VK_LEFT:
		case VK_RIGHT:
	case VK_UP:
	case VK_DOWN:
			// Stop the camera from rotating completely
			RLDS3D_SetCamRotForward(D3DVAL(0.0));
			break;
	    
		case 'A':
		case 'Z':
			RLDS3D_SetCamVelRelToCam(D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
	    break;
	}
	break;

    case WM_LBUTTONDOWN:
	{   HCURSOR hCur;
	    int x = LOWORD(lparam);
	    int y = HIWORD(lparam);
	    last_x = x;
	    last_y = y;
			BOOL bChanged = FALSE;
	    RLDS3D_FindAndSelectVisual(x, y, &bChanged);
			// Get rid of the Selected Object Cfg dialog if we've selected a new object
			if (bChanged && hSelCfgDlg) EndDialog(hSelCfgDlg, TRUE);
	    left_drag = TRUE;
	    SetCapture(win);
	    /* change to a groovy cursor */
	    hCur = LoadCursor(NULL, IDC_ARROW);
	    if (hCur) oldCursor = SetCursor(hCur);
	    else oldCursor = NULL;
	}
	break;

    case WM_LBUTTONUP:
	ReleaseCapture();
	left_drag = FALSE;
	if (oldCursor) SetCursor(oldCursor);
	break;

    case WM_RBUTTONDOWN:
	{
	    HCURSOR hCur;
	    int x = LOWORD(lparam);
	    int y = HIWORD(lparam);
	    last_x = x;
	    last_y = y;
			BOOL bChanged = FALSE;
	    RLDS3D_FindAndSelectVisual(x, y, &bChanged);
			// Get rid of the Selected Object Cfg dialog if we've selected a new object
			if (bChanged && hSelCfgDlg) EndDialog(hSelCfgDlg, TRUE);
	    right_drag = TRUE;
	    SetCapture(win);
	    /* change to a groovy cursor */
	    hCur = LoadCursor(NULL, IDC_ARROW);
	    if (hCur) oldCursor = SetCursor(hCur);
	    else oldCursor = NULL;
	}
	break;

    case WM_RBUTTONUP:
	right_drag = FALSE;
	ReleaseCapture();
	if (oldCursor) SetCursor(oldCursor);
	break;

    case WM_MOUSEMOVE:
	if ((wparam & MK_LBUTTON) && left_drag)
	{   double delta_x, delta_y;
	    delta_x = SIGN_EXTEND(LOWORD(lparam)) - last_x;
	    delta_y = -SIGN_EXTEND((HIWORD(lparam)) - last_y);
	    last_x = SIGN_EXTEND(LOWORD(lparam));
	    last_y = SIGN_EXTEND(HIWORD(lparam));
	    {
		double delta_r = sqrt(delta_x * delta_x + delta_y * delta_y);
		double radius = 50;
		double denom;

		denom = 0.05 * sqrt(radius * radius + delta_r * delta_r);

		if (delta_r == 0 || denom == 0) break;
				RLDS3D_SetSelectedRotRelToCam(D3DDivide(D3DVAL((float) delta_y), D3DVAL((float) delta_r)),
											  D3DDivide(D3DVAL((float) -delta_x), D3DVAL((float) delta_r)),
											  D3DVAL(0.0),
											  D3DDivide(D3DVAL((float) delta_r), D3DVAL((float) denom)));
	    }
	}
	else if ((wparam & MK_RBUTTON) && right_drag)
	{   double delta_x, delta_y;

	    delta_x = SIGN_EXTEND(LOWORD(lparam)) - last_x;
	    delta_y = SIGN_EXTEND(HIWORD(lparam)) - last_y;
	    last_x = SIGN_EXTEND(LOWORD(lparam));
	    last_y = SIGN_EXTEND(HIWORD(lparam));
			RLDS3D_MoveSelectedPosByScreenCoords(delta_x, delta_y);
	}
	break;
    case WM_COMMAND:
	switch( wparam & 0xffff )
	{
		case MENU_MOTION_ORBITSELECTEDOBJECT:
			{
				RLDS3D_OrbitSelected();
			}
			break;
		case MENU_MOTION_BULLETSELECTEDOBJECT:
			{
				RLDS3D_BulletSelected();
			}
			break;
		case MENU_SOUND_ATTACHSOUND:
			{
				if (RLDS3D_SoundInitialized())
				{
					if (RLDS3D_FrameSelected()) {
						char* file = OpenNewSoundFile(win);
						if (file) RLDS3D_AttachSound(file);                                     
					}
					else MessageBox(win, "No object selected to attach a sound to!", "Doh!", MB_ICONEXCLAMATION);
				}
				else MessageBox(win, "Sound not initialized!", "Doh!", MB_ICONEXCLAMATION);
			}
			break;
		case MENU_SOUND_REMOVESOUND:
			RLDS3D_RemoveSound();
			break;
		case MENU_SOUND_REMOVEALLSOUNDS:
			RLDS3D_RemoveAllSounds();
			break;

		case MENU_SOUND_PLAYSOUND:
			RLDS3D_PlaySound(FALSE);
			break;
		case MENU_SOUND_PLAYSOUNDLOOPING:
			RLDS3D_PlaySound(TRUE);
			break;
		case MENU_SOUND_STOPSOUND:
			RLDS3D_StopSelectedSound();
			break;
		
		case MENU_SOUND_STOPALLSOUNDS:
			RLDS3D_StopAllSounds();
			break;

		case MENU_SOUND_SELECTEDSOUNDPROPERTIES:
			{
				if (RLDS3D_SoundInitialized()) {
					if (RLDS3D_SoundSelected()) {
						inst_handle = (HANDLE) GetWindowLong(win, GWL_HINSTANCE);
						HWND mydial = CreateDialog((HINSTANCE)inst_handle, "SelectedParamBox", win, (DLGPROC)SelectedParamDlgProc);
						if (mydial) {
							ShowWindow(mydial, SW_SHOW);
							UpdateWindow(mydial);
						}
					}
					else MessageBox(win, "No sound currently selected", "Doh!", MB_ICONEXCLAMATION);
				}
				else MessageBox(win, "Sound not initialized!", "Doh!", MB_ICONEXCLAMATION);
			}
			break;
		case MENU_SOUND_GLOBALPROPERTIES:
			{
				if (RLDS3D_SoundInitialized()) {
					inst_handle = (HANDLE) GetWindowLong(win, GWL_HINSTANCE);
					HWND mydial = CreateDialog((HINSTANCE)inst_handle, "GlobalParamBox", win, (DLGPROC)GlobalParamDlgProc);
					if (mydial) {
						ShowWindow(mydial, SW_SHOW);
						UpdateWindow(mydial);
					}
				}
				else MessageBox(win, "Sound not initialized!", "Doh!", MB_ICONEXCLAMATION);
			}
			break;

		case MENU_FILE_ABOUT:
			{
				inst_handle = (HANDLE) GetWindowLong(win, GWL_HINSTANCE);
				DialogBox((HINSTANCE)inst_handle,"AboutBox", win, (DLGPROC)AboutDlgProc);
			}
	    break;

	case MENU_FILE_OPEN:
	    {   
		char *file = OpenNewFile(win);
				if (file) {
					RLDS3D_LoadXOF(file);
		}
	    }
	    break;

	case MENU_FILE_EXIT:
	    PostQuitMessage(0);
	    break;

	case MENU_EDIT_CUT:
	    RLDS3D_CutVisual();
	    break;

	case MENU_EDIT_COPY:
	    RLDS3D_CopyVisual();
	    break;

	case MENU_EDIT_PASTE:
	    RLDS3D_PasteVisual();
	    break;

	case MENU_EDIT_DELETE:
	    RLDS3D_DeleteVisual();
	    break;

	case MENU_EDIT_COLOUR:
			// Currently everything is handled there but the selection wheel maybe should be out here?
			RLDS3D_SetSelColour();
	    break;

	case MENU_EDIT_BOXES:
	    {
		HMENU menu;
				if (RLDS3D_GetBoxes() == TRUE) RLDS3D_SetBoxes(FALSE); else RLDS3D_SetBoxes(TRUE);
		int checked = (RLDS3D_GetBoxes()==TRUE) ? MF_CHECKED : MF_UNCHECKED;
		menu = GetMenu(win);
		menu = GetSubMenu(menu, 1);
		CheckMenuItem(menu, MENU_EDIT_BOXES, MF_BYCOMMAND|checked);
	    }
	    break;

		case MENU_QUALITY_LIGHTING:
			ToggleLighting(win);
			break;

		// Fill Modes
		case MENU_QUALITY_POINTS:
			SetFillMode(win, D3DRMFILL_POINTS);
			break;

	case MENU_QUALITY_WIREFRAME:
	    SetFillMode(win, D3DRMFILL_WIREFRAME);
	    break;

	case MENU_QUALITY_SOLID:
	    SetFillMode(win, D3DRMFILL_SOLID);
	    break;

		// Shading modes
	case MENU_QUALITY_FLAT:
	    SetShadeMode(win, D3DRMSHADE_FLAT);
	    break;

	case MENU_QUALITY_GOURAUD:
	    SetShadeMode(win, D3DRMSHADE_GOURAUD);
	    break;

	case MENU_QUALITY_PHONG:
	    SetShadeMode(win, D3DRMSHADE_PHONG);
	    break;

		// Color models
	case MENU_MODEL_MONO:
	    SetModel(win, D3DCOLOR_MONO);
	    break;

	case MENU_MODEL_RGB:
	    SetModel(win, D3DCOLOR_RGB);
	    break;

	case MENU_DITHER:
	    ToggleDither(win);
	    break;

	case MENU_TEXTURE_FILTERING:
	    ToggleTextureFiltering(win);
	    break;

	case MENU_LIGHT_DIRECTIONAL:
			RLDS3D_AddDirectionalLight();
			break;
	case MENU_LIGHT_PARALLEL_POINT:
			RLDS3D_AddParallelPointLight();
			break;
	case MENU_LIGHT_POINT:
			RLDS3D_AddPointLight();
			break;
	case MENU_LIGHT_SPOT:
			RLDS3D_AddSpotlight();
			break;
	}
	break;

    case WM_DESTROY:
	PostQuitMessage( 0 );
	break;

    case WM_SIZE:
	{
	    int width = LOWORD(lparam);
	    int height = HIWORD(lparam);
	    RLDS3D_ResizeViewport(width, height);
	}
	break;

    case WM_ACTIVATE:
		{
			RLDS3D_HandleActivate(wparam);
		}
	break;

    case WM_PAINT:
		if (RLDS3D_WinDevice())
		{
			RECT r;
	    PAINTSTRUCT ps;

	    if (GetUpdateRect(win, &r, FALSE))
	    {   BeginPaint(win, &ps);
				RLDS3D_HandlePaint(&ps);
		EndPaint(win, &ps);
	    }
	}
	else return DefWindowProc(win, msg, wparam, lparam);

    default:
	return DefWindowProc(win, msg, wparam, lparam);
    }
    return 0L;
}


