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

	Shade Bobs 1.0
	
	a test for
	guigfx.library/DirectDrawTrueColor()

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

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

#include <utility/tagitem.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <intuition/intuition.h>
#include <guigfx/guigfx.h>
#include <exec/memory.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 "misc.h"
#include "timer.h"

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

long __stack = 20000;
#define PROGNAME				"RGB ShadeBobs"
#define	__VERSION__				" 1.0"
static char versionstring[] = "$VER: " PROGNAME __VERSION__ "";
struct Library *GuiGFXBase = NULL;

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

	global

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

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

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

	srand(time(NULL));

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

	if (GuiGFXBase)
	{
		return TRUE;
	}
	
	printf("*** could not open guigfx.library v%d.\n", GUIGFX_VERSION);

	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_SIZEGADGET | WFLG_DEPTHGADGET | WFLG_ACTIVATE |
							WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH);

				inserttag(tp, WA_IDCMP, 
							IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | 
							IDCMP_NEWSIZE | IDCMP_ACTIVEWINDOW |
							IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS);
			
				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




double sd1, sd2, sd3, sd4, sd5, sd6;
double dd1, dd2, dd3, dd4, dd5, dd6;
int intensity;

void initeffekt (ULONG *buffer)
{
	int x, y;
	
	double r1, g1, b1;
	double r2, g2, b2;
	double xr, xg, xb;
	double dyr1, dyg1, dyb1, dyr2, dyg2, dyb2, dxr, dxg, dxb;

	sd1 = drand48() * 0.4 + 0.3;
	sd2 = drand48() * 0.4 + 0.3;
	sd3 = drand48() * 0.4 + 0.3;
	sd4 = drand48() * 0.4 + 0.3;
	sd5 = drand48() * 0.4 + 0.3;
	sd6 = drand48() * 0.4 + 0.3;

	dd1 = drand48() * 0.3 + 0.1;
	dd2 = drand48() * 0.3 + 0.1;
	dd3 = drand48() * 0.3 + 0.1;
	dd4 = drand48() * 0.3 + 0.1;
	dd5 = drand48() * 0.3 + 0.1;
	dd6 = drand48() * 0.3 + 0.1;

	intensity = rand() % 12 + 6;

	r1 = rand() % 255;
	g1 = rand() % 255;
	b1 = rand() % 255;

	r2 = rand() % 255;
	g2 = rand() % 255;
	b2 = rand() % 255;

	dyr1 = ((rand() % 255) - r1) / 128;
	dyg1 = ((rand() % 255) - g1) / 128;
	dyb1 = ((rand() % 255) - b1) / 128;
	dyr2 = ((rand() % 255) - r2) / 128;
	dyg2 = ((rand() % 255) - g2) / 128;
	dyb2 = ((rand() % 255) - b2) / 128;

	for (y = 0; y < 128; ++y)
	{
		xr = r1;
		xg = g1;
		xb = b1;

		dxr = (r2 - r1) / 128;
		dxg = (g2 - g1) / 128;
		dxb = (b2 - b1) / 128; 
	
		for (x = 0; x < 128; ++x)
		{
			buffer[y*128+x] = ((int)xr << 16) + ((int)xg << 8) + (int)xb;
			xr += dxr;
			xg += dxg;
			xb += dxb;
		}
		
		r1 += dyr1;
		g1 += dyg1;
		b1 += dyb1;

		r2 += dyr2;
		g2 += dyg2;
		b2 += dyb2;
	}
}


void effekt (ULONG *buffer)
{
	double pi2 = 6.283056;
	int x, y, i;
	UBYTE *bp;
	double d1 = 0, d2 = 0, d3 = 0, d4 = 0, d5 = 0, d6 = 0;
	static double sin1 = 0, sin2 = 0, sin3 = 0, sin4 = 0, sin5 = 0, sin6 = 0;

	#define SETPOS(x,y)	bp = ((UBYTE *)(buffer+((y)<<7)+(x)));
	#define SHADEPIXEL(ir, ig, ib) bp++;*bp+++=ir;*bp+++=ig;*bp+++=ib;
	#define SETBOB(x,y,ir,ig,ib) 		\
		SETPOS((x)-1, (y)-4);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-3, (y)-3);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-3, (y)-2);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-4, (y)-1);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-4, (y));				\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-4, (y)+1);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-3, (y)+2);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-3, (y)+3);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SETPOS((x)-1, (y)+4);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);			\
		SHADEPIXEL(ir, ig, ib);

	sin1 += sd1; while (sin1 > pi2 || sin1 < 0) sin1 -= pi2;
	sin2 += sd2; while (sin2 > pi2 || sin2 < 0) sin2 -= pi2;
	sin3 += sd3; while (sin3 > pi2 || sin3 < 0) sin3 -= pi2;
	sin4 += sd4; while (sin4 > pi2 || sin4 < 0) sin4 -= pi2;
	sin5 += sd5; while (sin5 > pi2 || sin5 < 0) sin5 -= pi2;
	sin6 += sd6; while (sin6 > pi2 || sin6 < 0) sin6 -= pi2;

	for (i = 0; i < 10; ++i)
	{
		x = (sin(sin1 + d1) + cos(sin2 + d2) + sin(sin3 + d3)) * 18 + 64;
		y = (cos(sin4 + d4) + sin(sin5 + d5) + sin(sin6 + d5)) * 18 + 64;

		SETBOB((int)x, (int)y, intensity, intensity, intensity);
		
		d1 += dd1;
		d2 += dd2;
		d3 += dd3;
		d4 += dd4;
		d5 += dd5;
		d6 += dd6;
	}

}




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

	mysticube
	mainloop

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

void mainloop(struct Screen *scr, struct mvwindow *win)
{
	APTR timehandle = NULL;
	int time;
	char text[30];
	BOOL timer = FALSE;
	APTR ddh;
	int frames = 0;
	
	ULONG *buffer;
	APTR drawhandle;
	BOOL finish = FALSE;
	ULONG signals;
	struct IntuiMessage *imsg;

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

	drawhandle = ObtainDrawHandle(NULL, win->window->RPort,
			scr->ViewPort.ColorMap, OBP_Precision, PRECISION_IMAGE, TAG_DONE);

	ddh = CreateDirectDrawHandle(drawhandle, 128,128, win->innerwidth, win->innerheight, NULL);

	buffer = AllocVec(128*128*4, MEMF_ANY+MEMF_CLEAR);

	if (ddh && buffer && drawhandle)
	{
		do
		{
			if (--frames < 0)
			{
				initeffekt(buffer);
				frames = 200;
			}
		
			effekt(buffer);
	
			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;
			
					ReplyMsg((struct Message *) imsg);
			
					switch (iclass)
					{
						case CLOSEWINDOW:
							finish = TRUE;
							break;
			
						case NEWSIZE:
							if ((win->window->Width != win->winwidth) ||
								(win->window->Height != win->winheight))
							{
								UpdateWindowParameters(win);
								DeleteDirectDrawHandle(ddh);
								ddh = CreateDirectDrawHandle(drawhandle, 128,128, win->innerwidth, win->innerheight, NULL);
							}
							break;
	
						case VANILLAKEY:
							switch (icode)
							{
								case 27:
									finish = TRUE;
									break;
	
								case 32:
									initeffekt(buffer);
									frames = 200;
									break;
	
								case 't':
									timer = timer ? FALSE : TRUE;
									break;
							}
					}
			
				}
			}
	
	
			//	get 1/1000 seconds since last frame
	
			if (timehandle)
			{
				time = timerstop(timehandle);
			}
			else
			{
				time = 0;
			}
	
	
			//	start timer
	
			timehandle = timerstart();
	
	
			//	draw
	
			if (buffer)
			{
				DirectDrawTrueColor(ddh, buffer, 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);
	}

	if (buffer) FreeVec(buffer);
	if (ddh) DeleteDirectDrawHandle(ddh);
	if (drawhandle) ReleaseDrawHandle(drawhandle);
}



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

	main

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

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

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

		if (defscreen = LockPubScreen(NULL))
		{
			if (window = CreateMVWindow(defscreen))
			{
				SetTaskPri(FindTask(NULL), TASKPRIO);
				mainloop(defscreen, window);
				DeleteMVWindow(window);
				result = 0;
			}
			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;
}
