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

	RGBPlasma

	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 Plasma"
#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







#define	BUFWIDTHB	7
#define BUFWIDTH	(1 << BUFWIDTHB)
#define BUFHEIGHT	96

ULONG __far scrbuf[2][BUFWIDTH*BUFHEIGHT];

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

#define	TABLEN 1024
#define	TABLEN2	333

int presinus1[TABLEN+BUFWIDTH];
int presinus2[TABLEN+BUFWIDTH];
int presinus3[TABLEN+BUFWIDTH];

void initeffekt (void)
{
	int i;
	double s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
	double s7 = 0, s8 = 0, s9 = 0, s10 = 0, s11 = 0, s12 = 0;
	double ds1, ds2, ds3, ds4, ds5, ds6;
	double ds7, ds8, ds9, ds10, ds11, ds12;
	double pi2 = 6.283056;

	sa1 = rand() % TABLEN;
	sa2 = rand() % TABLEN;
	sa3 = rand() % TABLEN;

	ds1 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds2 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds3 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds4 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds5 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds6 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds7 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds8 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds9 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds10 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds11 = pi2 * (3 + (rand() % 6)) / TABLEN;
	ds12 = pi2 * (3 + (rand() % 6)) / TABLEN;
	
	for (i = 0; i < TABLEN + BUFWIDTH; ++i)
	{
		presinus1[i] = ((sin(s1) + cos(s2) + sin(s3) + sin(s4)) * 32) + 128;
		presinus2[i] = ((sin(s5) + cos(s6) + sin(s7) + cos(s8)) * 32) + 128;
		presinus3[i] = ((sin(s9) + cos(s10) + cos(s11) + cos(s12)) * 32) + 128;
		s1 += ds1;
		s2 += ds2;
		s3 += ds3;
		s4 += ds4;
		s5 += ds5;
		s6 += ds6;
		s7 += ds7;
		s8 += ds8;
		s9 += ds9;
		s10 += ds10;
		s11 += ds11;
		s12 += ds12;
	}

	sd1 = drand48() * 0.1;
	sd2 = drand48() * 0.1;
	sd3 = drand48() * 0.1;
	sd4 = drand48() * 0.1;
	sd5 = drand48() * 0.1;
	sd6 = drand48() * 0.1;

	dd1 = drand48() * 0.06 - 0.03;
	dd2 = drand48() * 0.06 - 0.03;
	dd3 = drand48() * 0.06 - 0.03;
	dd4 = drand48() * 0.06 - 0.03;
	dd5 = drand48() * 0.06 - 0.03;
	dd6 = drand48() * 0.06 - 0.03;
}


ULONG *effekt (void)
{
	static int doub = 0;
	ULONG *buffer;

	double pi2 = 6.283056;
	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;

	int s1, s2, s3;
	int *pp1, *pp2, *pp3;
	int xx, yy;


	buffer = &scrbuf[doub][0];
	doub ^= 1;


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

	for (yy = 0; yy < BUFHEIGHT; ++yy)
	{
		bp = (UBYTE *) (buffer + (yy << BUFWIDTHB));

		s1 = (int)(sa1 + (sin(sin1 + d1) + sin(sin2 + d2)) * TABLEN2/4 + TABLEN2/2) & (TABLEN - 1);
		s2 = (int)(sa2 + (sin(sin3 + d3) + sin(sin4 + d4)) * TABLEN2/4 + TABLEN2/2) & (TABLEN - 1);
		s3 = (int)(sa3 + (sin(sin5 + d5) + sin(sin6 + d6)) * TABLEN2/4 + TABLEN2/2) & (TABLEN - 1);

		d1 += sin(dd1) * .15;
		d2 += dd2;
		d3 += sin(dd3) * .15;
		d4 += dd4;
		d5 += sin(dd5) * .15;
		d6 += dd5;
		
		pp1 = presinus1 + s1;
		pp2 = presinus2 + s2;
		pp3 = presinus3 + s3;
	
		for (xx = 0; xx < BUFWIDTH; ++xx)
		{
			bp++;
			*bp++ = *pp1++;
			*bp++ = *pp2++;
			*bp++ = *pp3++;
		}
	}
	
	sa1 += 2;
	sa2 += 2;
	sa3 += 2;

	return buffer;
}


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

	mysticube
	mainloop

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

void mainloop(struct Screen *scr, struct mvwindow *win)
{
	APTR timehandle = NULL;
	int time;
	char text[30];
	BOOL timer = FALSE;
	APTR ddh;
	
	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_EXACT, TAG_DONE);

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

	initeffekt();

	if (ddh && drawhandle)
	{
		do
		{
	
			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, BUFWIDTH,BUFHEIGHT, win->innerwidth, win->innerheight, NULL);
							}
							break;
	
						case VANILLAKEY:
							switch (icode)
							{
								case 27:
									finish = TRUE;
									break;
	
								case 32:
									initeffekt();
									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

			buffer = effekt();
	
			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 (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;
}
