/*********************************************************************
----------------------------------------------------------------------

	MystiCube 1.3
	© 1997 captain bifat / TEK neoscientists

	drag'n'drop picture viewer
	a simple demonstration for guigfx.library

	many parameters are fixed, modify them
	to suit your personal taste

----------------------------------------------------------------------
*********************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <utility/tagitem.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <intuition/intuition.h>
#include <guigfx/guigfx.h>

#include <clib/macros.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/utility.h>
#include <proto/guigfx.h>
#include <proto/wb.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/guigfx.h>

#include "MystiCube.h"

#include "timer.h"

/*------------------------------------------------------------------*/

long __stack = 20000;

static char versionstring[] = "$VER: " PROGNAME __VERSION__ "";


/*********************************************************************
----------------------------------------------------------------------

	global

----------------------------------------------------------------------
*********************************************************************/

void CloseGlobal(void)
{
	CloseLibrary(GuiGFXBase);
	GuiGFXBase = NULL;
}

BOOL InitGlobal(void)
{
	GuiGFXBase = OpenLibrary("guigfx.library", GUIGFX_VERSION);

	if (!GuiGFXBase)
	{
		GuiGFXBase = OpenLibrary("libs/guigfx.library", GUIGFX_VERSION);
	}

	if (GuiGFXBase)
	{
		return TRUE;
	}

	CloseGlobal();
}


/*********************************************************************
----------------------------------------------------------------------

	void UpdateWindowParameters(mvwindow)

	get current window parameters

----------------------------------------------------------------------
*********************************************************************/

void UpdateWindowParameters(struct mvwindow *win)
{
	win->winleft = win->window->LeftEdge;
	win->wintop = win->window->TopEdge;
	win->winwidth = win->window->Width;
	win->winheight = win->window->Height;
	win->innerleft = win->window->BorderLeft;
	win->innertop = win->window->BorderTop;
	win->innerwidth = win->winwidth - win->innerleft - win->window->BorderRight;
	win->innerheight = win->winheight - win->innertop - win->window->BorderBottom;
}


/*********************************************************************
----------------------------------------------------------------------

	DeleteMVWindow (mvwindow)

----------------------------------------------------------------------
*********************************************************************/

void DeleteMVWindow (struct mvwindow *win)
{
	if (win)
	{
		if (win->window)
		{
			Forbid();

			if (win->appwindow && win->appmsgport)
			{
				struct AppMessage *appmsg;
			
				while (appmsg = (struct AppMessage *) GetMsg(win->appmsgport))
				{
					ReplyMsg((struct Message *) appmsg);
				}
			
				RemoveAppWindow(win->appwindow);
			}
	
			DeleteMsgPort(win->appmsgport);

			Permit();

			CloseWindow(win->window);
		}
		
		free(win);
	}
}



/*********************************************************************
----------------------------------------------------------------------

	mvwindow = CreateMVWindow (screen)

----------------------------------------------------------------------
*********************************************************************/

#define inserttag(x,t,d) {(x)->ti_Tag=(t);((x)++)->ti_Data=(ULONG)(d);}

struct mvwindow *CreateMVWindow (struct Screen *scr)
{
	struct mvwindow *win;
	
	if (win = malloc(sizeof(struct mvwindow)))
	{
		BOOL success = FALSE;
		struct TagItem *taglist;

		memset(win, 0, sizeof(struct mvwindow));

		win->screen = scr;

		if(win->appmsgport = CreateMsgPort())
		{
			win->appSignal = 1L << win->appmsgport->mp_SigBit;

			if(taglist = AllocateTagItems(20))
			{
				UWORD visibleWidth, visibleHeight, visibleMidX, visibleMidY;
				UWORD visibleLeft, visibleTop;
				WORD winwidth, winheight, wintop, winleft;
				struct TagItem *tp = taglist;
				ULONG modeID;
		
				visibleWidth = scr->Width;
				visibleHeight = scr->Height;
		
				if ((modeID = GetVPModeID(&scr->ViewPort)) != INVALID_ID)
				{
					DisplayInfoHandle dih;
			
					if(dih = FindDisplayInfo(modeID))
					{
						struct DimensionInfo di;	
			
						if(GetDisplayInfoData(dih, (UBYTE*) &di, sizeof(di), DTAG_DIMS, modeID))
						{
							visibleWidth = di.TxtOScan.MaxX - di.TxtOScan.MinX;
							visibleHeight = di.TxtOScan.MaxY - di.TxtOScan.MinY;
						}
					}
				}
		
				visibleLeft = -scr->ViewPort.DxOffset;
				visibleTop = -scr->ViewPort.DyOffset;

				visibleMidX = visibleWidth/2 - scr->ViewPort.DxOffset;
				visibleMidY = visibleHeight/2 - scr->ViewPort.DyOffset;
		
				winwidth = visibleWidth / 2;
				winheight = visibleHeight / 2;
				inserttag(tp, WA_Width, winwidth);
				inserttag(tp, WA_Height, winheight);
		
				winleft = visibleMidX - winwidth/2;
				wintop = visibleMidY - winheight/2;
				inserttag(tp, WA_Left, winleft);
				inserttag(tp, WA_Top, wintop);
	
				win->otherwinpos[0] = visibleLeft;
				win->otherwinpos[1] = visibleTop;
				win->otherwinpos[2] = visibleWidth;
				win->otherwinpos[3] = visibleHeight;

				inserttag(tp, WA_Zoom, &win->otherwinpos);

				inserttag(tp, WA_PubScreen, scr);
		
				inserttag(tp, WA_Title, DEFAULT_WINTITLE);
		
				inserttag(tp, WA_NewLookMenus, TRUE);
		
		
				inserttag(tp, WA_Flags, 
							WFLG_SIZEBBOTTOM | WFLG_DRAGBAR | WFLG_REPORTMOUSE |
							WFLG_SIZEGADGET | WFLG_DEPTHGADGET | WFLG_ACTIVATE |
							WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH);

				win->idcmp = IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE |
							IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS;

				inserttag(tp, WA_IDCMP, win->idcmp);
			
				inserttag(tp, WA_MinWidth, DEFAULT_MINWIDTH);
				inserttag(tp, WA_MinHeight, DEFAULT_MINHEIGHT);
				inserttag(tp, WA_MaxWidth, DEFAULT_MAXWIDTH);
		 		inserttag(tp, WA_MaxHeight, DEFAULT_MAXHEIGHT);

				inserttag(tp, TAG_DONE, 0);
		
				if(win->window = OpenWindowTagList(NULL, taglist))
				{
					win->idcmpSignal = 1L << win->window->UserPort->mp_SigBit;
					UpdateWindowParameters(win);

					win->appwindow = AddAppWindow(0, 0, win->window, win->appmsgport, NULL);

					success = TRUE;
				}
		
				FreeTagItems(taglist);
			}
		}
		
		if (!success)
		{
			DeleteMVWindow(win);
			win = NULL;
		}
	}

	return win;
}

#undef inserttag


/*********************************************************************
----------------------------------------------------------------------

	docube(cube, zoom, bufferpic, width, height, outside)

	calculate coordinates, draw faces

----------------------------------------------------------------------
*********************************************************************/

void docube(struct cube *cube, double zoom, PICTURE *bufferpic, int width, int height, BOOL outside)
{
	struct face *face;

	double xcosinus, xsinus;
	double ycosinus, ysinus;
	double zcosinus, zsinus;
	double x, y, z, t;
	BOOL visible;
	int i;
	

	if (bufferpic)
	{
		//	calculate 2d coordinates
	
		xcosinus = cos(cube->xrot);
		xsinus = sin(cube->xrot);
		ycosinus = cos(cube->yrot);
		ysinus = sin(cube->yrot);
		zcosinus = cos(cube->zrot);
		zsinus = sin(cube->zrot);

		for (i = 0; i < 8; ++i)
		{
			x = cube->coords3d[i].x;
			y = cube->coords3d[i].y;
			z = cube->coords3d[i].z;
			
			t = y;
			y = y * xcosinus - z * xsinus;
			z = t * xsinus + z * xcosinus;
	
			t = x;
			x = x * ycosinus + z * ysinus;
			z = z * ycosinus - t * ysinus;
		
			t = x;
			x = x * zcosinus - y * zsinus;
			y = t * zsinus + y * zcosinus;
		
			z = (z + DISTANCE) / zoom;
		
			x /= z;
			y /= z;
		
			cube->coords2d[i].x = x * width/2 + width/2;
			cube->coords2d[i].y = y * width/2 + height/2;
		}


		//	clear background
		
		DoPictureMethod(bufferpic, PICMTHD_SET, cube->bgcolor, TAG_DONE);


		//	draw faces

		for (i = 0; i < 6; ++i)
		{
			face = &cube->faces[i];
	
			face->edges[0] = cube->coords2d[face->a]; 
			face->edges[1] = cube->coords2d[face->b]; 
			face->edges[2] = cube->coords2d[face->c]; 
			face->edges[3] = cube->coords2d[face->d]; 

			//	check visibility

			visible =	((face->edges[1].x - face->edges[0].x) *
						(face->edges[2].y - face->edges[0].y) -
						(face->edges[2].x - face->edges[0].x) *
						(face->edges[1].y - face->edges[0].y)) > 0 ? outside : !outside;
					
			if (visible)
			{
				face->visible = TRUE;

				//	draw texture-mapped face
			
				if (face->mappedtexturepic)
				{
					DoPictureMethod(bufferpic, PICMTHD_TEXTURE,
						face->mappedtexturepic, face->edges, NULL);
				}
				else
				{
					if (cube->mappeddefaultpic)
					{
						DoPictureMethod(bufferpic, PICMTHD_TEXTURE,
							cube->mappeddefaultpic, face->edges, NULL);
					}
				}
			}
			else
			{
				face->visible = FALSE;
			}
		}
	}
}



/*********************************************************************
----------------------------------------------------------------------

	rotatecube(cube, xstep, ystep, zstep, timescale)

----------------------------------------------------------------------
*********************************************************************/

void rotatecube(struct cube *cube, double xstep, double ystep, double zstep, double timescale)
{
//	static double pi2 = 6.283056;

	if (cube)
	{
		cube->xrot += xstep * timescale;
		cube->yrot += ystep * timescale;
		cube->zrot += zstep * timescale;
	}
}


/*********************************************************************
----------------------------------------------------------------------

	setcuberotation(cube, xr, yr, zr)

----------------------------------------------------------------------
*********************************************************************/

void setcuberotation(struct cube *cube, double xr, double yr, double zr)
{
	if (cube)
	{
		cube->xrot = xr;
		cube->yrot = yr;
		cube->zrot = zr;
	}
}


/*********************************************************************
----------------------------------------------------------------------

	is = pointinpoly(x,y, numedges, edges)

	check if a point is inside a polygon

	by Ken McElvain (slightly modified)

----------------------------------------------------------------------
*********************************************************************/

int __inline whichquad(point2d pt, point2d orig)
{
	if(pt.x < orig.x)
	{
		if (pt.y < orig.y)
			return 2;
		else
			return 1;
	}
	else
	{
		if(pt.y < orig.y)
			return 3;
		else
			return 0;
	}
}

BOOL pointinpoly(int x, int y, int cnt, point2d *polypts)
{
	int	oldquad, newquad;
	point2d	pt, thispt, lastpt;
	int	a, b, i;
	int	wind = 0;       // current winding number

	pt.x = x;
	pt.y = y;

	lastpt = polypts[cnt-1];
	oldquad = whichquad(lastpt,pt); /* get starting angle */
	for(i=0;i<cnt;i++) { /* for each point in the polygon */
	        thispt = polypts[i];
	        newquad = whichquad(thispt,pt);
	        if(oldquad!=newquad) { /* adjust wind */
	                /*
	                 * use mod 4 comparsions to see if we have
	                 * advanced or backed up one quadrant 
	                 */
	                if (((oldquad+1)&3)==newquad) wind++;
	                else if (((newquad+1)&3)==oldquad) wind--;
	                else {
	                        /*
	                         * upper left to lower right, or
	                         * upper right to lower left. Determine
	                         * direction of winding  by intersection
	                         *  with x==0.
	                         */
	                        a = lastpt.y - thispt.y;
	                        a *= (pt.x - lastpt.x);
	                        b = lastpt.x - thispt.x;
	                        a += lastpt.y * b;
	                        b *= pt.y;
	
	                        if(a > b) wind += 2;
	                        else wind -= 2;
	                }
	        }
	        lastpt = thispt;
	oldquad = newquad;
	}

	return (BOOL) (wind != 0);

}


/*********************************************************************
----------------------------------------------------------------------

	nr = findface(cube, x, y)

	find the face that is hit throughout (x|y)

----------------------------------------------------------------------
*********************************************************************/

int findface(struct cube *cube, int x, int y)
{
	int facenr = -1;
	struct face *face;
	int i;

	if (cube)
	{
		for (i = 0; i < 6; ++i)
		{
			face = &cube->faces[i];

			if (face->visible)
			{
				if (pointinpoly(x,y, 4, face->edges))
				{
					facenr = i;
					break;
				}
			}
		}
	}

	return facenr;
}


/*********************************************************************
----------------------------------------------------------------------

	releasecube(cube)

	release the cube's drawhandle
	and free mapped pictures

----------------------------------------------------------------------
*********************************************************************/

void releasecube(struct cube *cube)
{
	if (cube)
	{
		int i;
	
		ReleaseDrawHandle(cube->drawhandle);
		cube->drawhandle = NULL;
		
		for (i = 0; i < 6; ++i)
		{
			DeletePicture(cube->faces[i].mappedtexturepic);
			cube->faces[i].mappedtexturepic = NULL;
		}

		DeletePicture(cube->mappeddefaultpic);
		cube->mappeddefaultpic = NULL;
	}
}



/*********************************************************************
----------------------------------------------------------------------

	drawhandle = obtaincube(cube, screen, window)
	
	compose and obtain a drawhandle for the cube

----------------------------------------------------------------------
*********************************************************************/

APTR obtaincube(struct cube *cube, struct Screen *scr, struct Window *win)
{
	APTR drawhandle = NULL;

	if (cube)
	{
		int i;

		//	release old drawhandle and delete mapped pictures

		releasecube(cube);

		//	obtain new drawhandle
		
		if (drawhandle = cube->drawhandle = ObtainDrawHandle(cube->psm, win->RPort,
			scr->ViewPort.ColorMap, OBP_Precision, PRECISION_IMAGE, TAG_DONE))
		{
			//	map pictures to the new drawhandle
		
			for (i = 0; i < 6; ++i)
			{
				if (cube->faces[i].mappedtexturepic = ClonePicture(cube->faces[i].texturepic, NULL))
				{
					DoPictureMethod(cube->faces[i].mappedtexturepic,
						PICMTHD_MAPDRAWHANDLE, drawhandle, NULL);
				}
			}

			//	map the default picture to the new drawhandle

			cube->mappeddefaultpic = ClonePicture(cube->defaultpic, NULL);
			DoPictureMethod(cube->mappeddefaultpic, PICMTHD_MAPDRAWHANDLE, drawhandle, NULL);
		}

	}

	return drawhandle;
}


/*********************************************************************
----------------------------------------------------------------------

	deletecube(cube)

----------------------------------------------------------------------
*********************************************************************/

void deletecube(struct cube *cube)
{
	if (cube)
	{
		releasecube(cube);
		DeletePenShareMap(cube->psm);
		free(cube);	
	}
}


/*********************************************************************
----------------------------------------------------------------------

	createcube(defaulttexture, bgcolor)

----------------------------------------------------------------------
*********************************************************************/

struct cube *createcube(PICTURE *defaultpic, ULONG bgcolor)
{
	BOOL success = FALSE;

	static point3d coords[8] =
	{
		{-1,-1,1},	{1,-1,1},	{1,1,1},	{-1,1,1},
		{-1,-1,-1}, {1,-1,-1},	{1,1,-1},	{-1,1,-1}
	};

	static int faceedges[] =
	{
		2,1,0,3,
		1,2,6,5,
		0,1,5,4,
		7,6,2,3,
		4,5,6,7,
		4,7,3,0
	};

	struct cube *cube;
	
	if (cube = malloc(sizeof(struct cube)))
	{
		int i;

		memset(cube, 0, sizeof(struct cube));


		//	init 3d coordinates
		
		for (i = 0; i < 8; ++i)
		{
			cube->coords3d[i] = coords[i];
		}

		//	init edge indices
		
		for (i = 0; i < 6; ++i)
		{
			cube->faces[i].a = faceedges[i*4];
			cube->faces[i].b = faceedges[i*4+1];
			cube->faces[i].c = faceedges[i*4+2];
			cube->faces[i].d = faceedges[i*4+3];
		}

		cube->xrot = 0;
		cube->yrot = 0;
		cube->zrot = 0;

		cube->bgcolor = bgcolor;
		cube->defaultpic = defaultpic;

		//	create pen-sharemap
		
		if (cube->psm = CreatePenShareMap(GGFX_HSType, HSTYPE_12BIT_TURBO, TAG_DONE))
		{
			//	add the default picture to the pen-sharemap
		
			if (AddPicture(cube->psm, cube->defaultpic, TAG_DONE))
			{
				//	add the background color to the pen-sharemap
			
				if (AddPalette(cube->psm, &cube->bgcolor,
					GGFX_NumColors, 1, TAG_DONE))
				{
					success = TRUE;
				}
			}
		}
	}

	if (!success)
	{
		deletecube(cube);
		cube = NULL;
	}

	return cube;
}



/*********************************************************************
----------------------------------------------------------------------

	setcubetexture(cube, facenr, picture)

	set a new picture for the specified face

----------------------------------------------------------------------
*********************************************************************/

void setcubetexture(struct cube *cube, int nr, PICTURE *pic)
{
	if (cube && (nr >= 0 && nr < 6))
	{
		//	remove the old picture's colorhandle from the cube's pen-sharemap	

		RemColorHandle(cube->faces[nr].colorhandle);
		cube->faces[nr].colorhandle = NULL;

		//	delete the mapped picture

		DeletePicture(cube->faces[nr].mappedtexturepic);
		cube->faces[nr].mappedtexturepic = NULL;

		if (pic)
		{
			cube->faces[nr].texturepic = pic;

			//	add the new picture to the cube's pen-sharemap
		
			cube->faces[nr].colorhandle = AddPicture(cube->psm, pic, GGFX_Weight, 10, TAG_DONE);
		}
	}
}


/*********************************************************************
----------------------------------------------------------------------

	makergbpics
	
	calculate faces for an RGB cube

----------------------------------------------------------------------
*********************************************************************/

void makergbpics(PICTURE **pics)
{
	int r, g, b;
	ULONG *array;

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[0]);
		pics[0] = NULL;
		for (g = 0; g < 128; ++g)
		{
			for (b = 0; b < 128; ++b)
			{
				array[b*128+g] = ((127-g) << 9) + ((127-b) << 1);
			}
		}
		pics[0] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[1]);
		pics[1] = NULL;
		for (r = 0; r < 128; ++r)
		{
			for (g = 0; g < 128; ++g)
			{
				array[r*128+g] = (r << 17) + (g << 9) + 255;
			}
		}
		pics[1] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[2]);
		pics[2] = NULL;
		for (r = 0; r < 128; ++r)
		{
			for (b = 0; b < 128; ++b)
			{
				array[r*128+b] = (r << 17) + (b << 1);
			}
		}
		pics[2] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[3]);
		pics[3] = NULL;
		for (r = 0; r < 128; ++r)
		{
			for (b = 0; b < 128; ++b)
			{
				array[(127-r)*128+b] = (r << 17) + (b << 1) + (255<<8);
			}
		}
		pics[3] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[4]);
		pics[4] = NULL;
		for (g = 0; g < 128; ++g)
		{
			for (b = 0; b < 128; ++b)
			{
				array[g*128+b] = (g << 9) + (b << 1) + (255<<16);
			}
		}
		pics[4] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}

	if (array = malloc(128*128*4))
	{
		DeletePicture(pics[5]);
		pics[5] = NULL;
		for (r = 0; r < 128; ++r)
		{
			for (g = 0; g < 128; ++g)
			{
				array[(127-r)*128+g] = (r << 17) + (g << 9);
			}
		}
		pics[5] = MakePicture(array, 128,128, 
			GGFX_PixelFormat, PIXFMT_0RGB_32,
			GGFX_Independent, TRUE, TAG_DONE); 
		free(array);
	}
}


/*********************************************************************
----------------------------------------------------------------------

	mysticube
	mainloop

----------------------------------------------------------------------
*********************************************************************/

void mysticube(struct mvwindow *win, struct cube *cube)
{
	APTR timehandle = NULL;
	int time;
	char text[30];
	BOOL timer = FALSE;
	BOOL outside = TRUE;
	double zoom = ZOOM;
	
	PICTURE *bufferpic = NULL;
	APTR drawhandle;
	struct AppMessage *appmsg;
	BOOL finish = FALSE;
	ULONG signals;
	struct IntuiMessage *imsg;
	int i;

	double xstep = YROT, ystep = XROT;
	WORD startmx = 0, startmy = 0;
	BOOL mousebutton = FALSE;
	

	PICTURE *picture[6] = {NULL, NULL, NULL, NULL, NULL, NULL};


	SetABPenDrMd(win->window->RPort, 1,2, JAM2);


	drawhandle = obtaincube(cube, win->screen, win->window);


	do
	{
		//	allocate draw buffer
	
		if (!bufferpic && drawhandle)
		{
			UpdateWindowParameters(win);
			if (bufferpic = MakePicture(NULL, win->innerwidth, win->innerheight, NULL))
			{
				//	map the draw buffer to the current drawhandle
			
				DoPictureMethod(bufferpic, PICMTHD_MAPDRAWHANDLE, drawhandle, NULL);
			}

			RefreshWindowFrame(win->window);
		}


		//	get signals
	
		signals = SetSignal(0, win->idcmpSignal | win->appSignal);


		//	IDCMP message loop

		if (signals & win->idcmpSignal)
		{
			while (imsg = (struct IntuiMessage *) GetMsg(win->window->UserPort))
			{
				ULONG iclass = imsg->Class;
				ULONG icode = imsg->Code;
				WORD mx = imsg->MouseX, my = imsg->MouseY;
		
				ReplyMsg((struct Message *) imsg);
		
				switch (iclass)
				{
					case MOUSEMOVE:
						if (mousebutton)
						{
							xstep = (double)(mx-startmx) / (double) 100;
							ystep = (double)(my-startmy) / (double) 100;
							startmx = mx;
							startmy = my;
						}
						break;
					
					case MOUSEBUTTONS:
						switch (icode)
						{
							case SELECTUP:
								ModifyIDCMP(win->window, win->idcmp);
								mousebutton = FALSE;
								xstep /= 2;
								ystep /= 2;
								break;
	
							case SELECTDOWN:
								ModifyIDCMP(win->window, win->idcmp | IDCMP_MOUSEMOVE);
								mousebutton = TRUE;
								startmx = mx;
								startmy = my;
								xstep = 0;
								ystep = 0;
								setcuberotation(cube,0,0,0);
								break;
						}
						break;
				
					case CLOSEWINDOW:
						finish = TRUE;
						break;
		
					case NEWSIZE:
						if ((win->window->Width != win->winwidth) ||
							(win->window->Height != win->winheight))
						{
							DeletePicture(bufferpic);
							bufferpic = NULL;
						}
						break;

					case VANILLAKEY:
						switch (icode)
						{
							case 27:
								finish = TRUE;
								break;
							case 't':
								timer = timer ? FALSE : TRUE;
								break;

							case 'i':
								outside = !outside;
								break;

							case '+':
								zoom += .01;
								break;

							case '-':
								zoom -= .01;
								break;

							case 'r':
							{
								makergbpics(picture);

								setcubetexture(cube, 0, picture[0]);
								setcubetexture(cube, 1, picture[1]);
								setcubetexture(cube, 2, picture[2]);
								setcubetexture(cube, 3, picture[3]);
								setcubetexture(cube, 4, picture[4]);
								setcubetexture(cube, 5, picture[5]);
		
								RectFill(win->window->RPort, win->innerleft, win->innertop, win->innerleft + win->innerwidth - 1, win->innertop + win->innerheight - 1);
								drawhandle = obtaincube(cube, win->screen, win->window);
								DeletePicture(bufferpic);
								bufferpic = NULL;
								if (timehandle) timerstop(timehandle);
								timehandle = NULL;
								break;
							}
						}
				}
		
			}
		}


		//	check for pictures being dropped onto the main window

		if (signals & win->appSignal)
		{
			int x = -1, y = -1;

			while (appmsg = (struct AppMessage *) GetMsg(win->appmsgport))
			{
				struct WBArg *argptr = appmsg->am_ArgList;
				BOOL foundpic = FALSE;
				char pathname[MAXPATHNAMELEN];
				char filename[MAXFILENAMELEN];
		
				for (i = 0; i < appmsg->am_NumArgs && foundpic == FALSE; i++)
				{
					if (NameFromLock(argptr->wa_Lock, pathname, MAXPATHNAMELEN))
					{
						if(strlen(argptr->wa_Name))
						{
							strncpy(filename, argptr->wa_Name, MAXFILENAMELEN);
							foundpic = TRUE;
							UpdateWindowParameters(win);
							x = appmsg->am_MouseX - win->innerleft;
							y = appmsg->am_MouseY - win->innertop;
						}
					}
					argptr++;		// next argument
				}
				ReplyMsg((struct Message *) appmsg);
	
				if (foundpic)
				{
					//	get the surface number that has been hit
				
					int nr = findface(cube, x, y);
				
					if (nr >= 0)
					{
						char fullname[MAXFILENAMELEN+MAXPATHNAMELEN];
						PICTURE *newpic;
		
						strcpy(fullname, pathname);
						AddPart(fullname, filename, MAXFILENAMELEN+MAXPATHNAMELEN);

						//	load and set new picture
		
						if (newpic = LoadPicture(fullname, NULL))
						{
							DeletePicture(picture[nr]);
							picture[nr] = newpic;
							DoPictureMethod(picture[nr], PICMTHD_SCALE, 128, 128, NULL);
							setcubetexture(cube, nr, picture[nr]);
							RectFill(win->window->RPort, win->innerleft, win->innertop, win->innerleft + win->innerwidth - 1, win->innertop + win->innerheight - 1);
							drawhandle = obtaincube(cube, win->screen, win->window);
							DeletePicture(bufferpic);
							bufferpic = NULL;
							if (timehandle) timerstop(timehandle);
							timehandle = NULL;
						}
					}
				}
			}
		}

		//	get 1/1000 seconds since last frame

		if (timehandle)
		{
			time = timerstop(timehandle);
		}
		else
		{
			time = 0;
		}


		//	rotate cube

		if (mousebutton)
		{		
			rotatecube(cube, ystep, -xstep, 0, (double) time / 20);
		}
		else
		{
			rotatecube(cube, ystep, -xstep, ZROT, (double) time / 20);
		}


		//	start timer

		timehandle = timerstart();


		//	draw cube

		if (bufferpic)
		{
			docube(cube, zoom, bufferpic, win->innerwidth, win->innerheight, outside);
			DrawPicture(drawhandle, bufferpic, win->innerleft, win->innertop, NULL);

			if (timer)
			{
				//	display frames per second
	
				Move(win->window->RPort, win->innerleft, 
					win->innertop + win->innerheight - win->window->RPort->TxHeight + win->window->RPort->TxBaseline);
				sprintf(text, "fps: %ld", (ULONG) ((double) 1000 / (double) time));
				Text(win->window->RPort, text, strlen(text));
			}
		}


	} while (finish == FALSE);


	//	free pictures

	for (i = 0; i < 6; ++i)
	{
		DeletePicture(picture[i]);
	}

	//	free cube

	DeletePicture(bufferpic);
	releasecube(cube);
}



/*********************************************************************
----------------------------------------------------------------------

	main

----------------------------------------------------------------------
*********************************************************************/

ULONG main (int argc, char **argv)
{
	ULONG result;

	if(InitGlobal())
	{
		struct mvwindow *window;
		struct Screen *defscreen;
		struct cube *cube;
		PICTURE *logopic;

		if (defscreen = LockPubScreen(NULL))
		{
			if (window = CreateMVWindow(defscreen))
			{
				if (logopic = MakePicture(MysticLogo, LOGOWIDTH, LOGOHEIGHT,
					GGFX_Palette, MysticLogoPalette,
					GGFX_NumColors, LOGONUMCOLORS, TAG_DONE))
				{
					if (cube = createcube(logopic, BGCOLOR))
					{
						SetTaskPri(FindTask(NULL), TASKPRIO);
						mysticube(window, cube);
						deletecube(cube);
						result = 0;
					}
					else
					{
						printf("*** out of memory\n");
						result = 20;
					}
					
					DeletePicture(logopic);
				}
				else
				{
					printf("*** out of memory\n");
					result = 20;
				}

				DeleteMVWindow(window);
			}
			else
			{
				printf("*** window could not be opened\n");
				result = 20;
			}

			UnlockPubScreen(NULL, defscreen);
		}
		else
		{
			printf("*** pubscreen could not be locked\n");
			result = 20;
		}
		
		CloseGlobal();
	}
	else
	{
		printf("*** global initialization failed\n");
		result = 20;
	}
	
	return result;
}
