
#include "Mandel.h"

#include <intuition/intuition.h>
#include <libraries/cybergraphics.h>
#include <libraries/powerpc.h>

#include <wbstartup.h>
#include <workbench/startup.h>

#include <clib/cybergraphics_protos.h>
#include <clib/alib_protos.h>

#ifdef __ORIGINAL__
 #undef __PUB_SCREEN__
#endif

#ifdef __PPC__

// ----------------------------------------------
// PPC doesn't understand 68K Register Maps !
// ----------------------------------------------

#include <clib/dos_protos.h>
#include <clib/icon_protos.h>
#include <clib/cybergraphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/asl_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/powerpc_protos.h>

#else

#include <pragmas/dos_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragma/cybergraphics_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/asl_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/powerpc_lib.h>

#endif

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

// use a CG Hook Function to draw the image
//#define __ORIGINAL__ -> See Project Settings

Rect view; // aktuelles Rechteck
struct DrawMsg *gfxport;
int N = 20; // Rechentiefe

static struct DrawMsg gfxbuf; // Hier wird die Graphik aufgebaut
static ULONG lut[256]; // Farbtabelle für 8 bit Farbtiefe
static Rect views[10]; // Zwischenspeicher (Ctrl 1 bis 9)
long width,height;
long x0 = 0,y0 = 0;

UBYTE shared_pens[256];	// remapped colors !

#ifdef __PPC__
 #define _WINDOW_TITLE_ "Mandeldemo, PPC"
#else
 #define _WINDOW_TITLE_ "Mandeldemo, 68K"
#endif

static char* foo = "PRE CALL";

static ULONG getit(
	register __a0 struct Hook *hook,
	register __a2 struct RastPort *rp,
	register __a1 struct DrawMsg *msg)
{
	if (msg)
		gfxbuf = *msg;

	foo = "GetIt()";

	return 0;
}

static ULONG drawit(
	register __a0 struct Hook *hook,
	register __a2 struct RastPort *rp,
	register __a1 struct DrawMsg *msg)
{
	CopyMem(gfxbuf.memptr,msg->memptr,msg->ysize*msg->bytesperrow);

	foo = "DrawIt()";

	return 0;
}

void TextOutXY(struct RastPort *rp,long x, long y,  STRPTR s)
{
	Move(rp,x,y);
	Text(rp,s,strlen(s));
}

void TextOut(struct RastPort *rp,STRPTR s)
{
	Text(rp,s,strlen(s));
}

static void DrawMandelInfo(struct RastPort *rp, int N, Rect &view)
{
	char buf[64];
	ULONG h = rp->TxHeight;

	SetDrMd(rp,JAM2);
	SetAPen(rp,0);
	SetBPen(rp,1);

	sprintf(buf,", Depth: %ld",N);
	TextOutXY(rp,0,rp->TxBaseline,buf);
	sprintf(buf,"From: (%g,%g)",view.x0,view.y0);
	TextOutXY(rp,0,rp->cp_y+h,buf);
	sprintf(buf,"To:   (%g,%g)",view.x1,view.y1);
	TextOutXY(rp,0,rp->cp_y+h,buf);
}

void ScrollView(Window *window,DrawMsg *gfxport,long dx, long dy)
{
	if (window && gfxport && gfxport->memptr)
	{
		long addr;
		long w = window->Width - window->BorderLeft - window->BorderRight - 1;
		long h = window->Height - window->BorderBottom - window->BorderTop - 1;
		long x = window->LeftEdge + window->BorderLeft;
		long y = window->TopEdge + window->BorderTop;

		long x1 = x0,y1 = y0;
		x0 += dx;
		y0 += dy;

		if (x0 < 0) x0 = 0;
		if (x0 > width - w) x0 = width - w;
		if (y0 < 0) y0 = 0;
		if (y0 > height - h) y0 = height - h;

		dx = x0 - x1;
		dy = y0 - y1;

		if (dx || dy)
		{
			struct BitMap *map = window->RPort->BitMap;
			struct RastPort *rp = window->RPort;

			if (dx < 0)
			{
				if (dy == 0)
				{
					BltBitMapRastPort(map,x,y,rp,-dx,dy,w+dx,h+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,-dx,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
				else if (dy > 0)
				{
					BltBitMapRastPort(map,x,y,rp,-dx,dy,w+dx,h-dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,-dx,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
				else
				{
					BltBitMapRastPort(map,x,y-dy,rp,-dx,0,w+dx,h+dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,-dx,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
			}
			else if (dx > 0)
			{
//				addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;

//				WriteChunkyPixels(window->RPort,0,0,w,h,(UBYTE*)addr,gfxport->bytesperrow);

				if (dy == 0)
				{
					BltBitMapRastPort(map,x+dx,y,rp,0,dy,w-dx,h+1,0xc0);

					addr = (long)gfxport->memptr + x0 + w - dx + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,w-dx,0,w,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
				else if (dy > 0)
				{
					BltBitMapRastPort(map,x,y,rp,dx,dy,w-dx,h-dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,dx,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
				else
				{
					BltBitMapRastPort(map,x,y-dy,rp,dx,0,w-dx,h+dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,dx,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
			}
			else
			{
//				addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
//				WriteChunkyPixels(window->RPort,0,0,w,h,(UBYTE*)addr,gfxport->bytesperrow);

				if (dy < 0)
				{
					BltBitMapRastPort(map,x,y,rp,0,-dy,w,h+dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,0,w,-dy,(UBYTE*)addr,gfxport->bytesperrow);
				}
				else
				{
					BltBitMapRastPort(map,x,y+dy,rp,0,0,w,h-dy+1,0xc0);

					addr = (long)gfxport->memptr + x0 + (y0 + h) * gfxport->bytesperrow;
					WriteChunkyPixels(window->RPort,0,h-dy,w,h,(UBYTE*)addr,gfxport->bytesperrow);
				}
			}
		}
	}
}


struct Library *PowerPCBase = NULL;
struct Library *CyberGfxBase = NULL;

void main()
{
	struct Screen *screen;
	struct Hook gethook,drawhook;
	struct ScreenModeRequester *request;
	int i;

	gethook.h_Entry = (ULONG (*)())getit;
	drawhook.h_Entry = (ULONG (*)())drawit;

	for (i = 0; i < 256; i++)
	{
		lut[i] = (int) (sin(2.0*PI/256.0*(double) i)*127.0 + 127.0);
	};
	for (i = 0; i < 256; i++)
	{
		lut[i] |= (int) (sin(2.2*PI/256.0*(double) i + 0.4)*127.0 + 127.0) << 8;
	};
	for (i = 0; i < 256; i++)
	{
		lut[i] |= (int) (sin(1.8*PI/256.0*(double) i + 2.5)*127.0 + 127.0) << 16;
	};
	lut[0] = 0x000000;

	// Bug in ppc.library from phase 5
	SetTaskPri(FindTask(NULL),0);

	PowerPCBase = OpenLibrary("powerpc.library",7);
	CyberGfxBase = OpenLibrary("cybergraphics.library",40);

	if (CyberGfxBase)
	{
#ifdef __PUB_SCREEN__
		screen = LockPubScreen(NULL);
#else
		ULONG id;
		id = BestCModeIDTags(CYBRBIDTG_Depth,8,
									CYBRBIDTG_NominalWidth,320,
									CYBRBIDTG_NominalHeight,240,
									TAG_END);

		request = (struct ScreenModeRequester *) AllocAslRequestTags(ASL_ScreenModeRequest,
							ASLSM_PrivateIDCMP,TRUE,
							ASLSM_TitleText,"Select Cybergraphics Screen Mode",
							ASLSM_DoDepth,TRUE,
							ASLSM_DoHeight,TRUE,
							ASLSM_DoWidth,TRUE,
							ASLSM_InitialDisplayID,id,
							ASLSM_InitialDisplayDepth,GetCyberIDAttr(CYBRIDATTR_DEPTH,id),
							ASLSM_InitialDisplayWidth,GetCyberIDAttr(CYBRIDATTR_WIDTH,id),
							ASLSM_InitialDisplayHeight,GetCyberIDAttr(CYBRIDATTR_HEIGHT,id),
							TAG_END);
		if (request)
		{
			if (AslRequest(request,NULL))
			{
				screen = OpenScreenTags(NULL,
					SA_DisplayID, request->sm_DisplayID,
					SA_Width,     request->sm_DisplayWidth,
					SA_Height,    request->sm_DisplayHeight,
					SA_Depth,     request->sm_DisplayDepth,
					SA_Behind,FALSE,
					SA_Quiet,TRUE,
					SA_LikeWorkbench, TRUE,
					TAG_END);
#endif
				if (screen)
				{
					struct Window *window;
					struct IntuiMessage *msg;

					window = OpenWindowTags(NULL,
#ifndef __PUB_SCREEN__
						WA_Left,0,
						WA_Top,0,
#endif
#ifdef __ORIGINAL__
						WA_Width,  screen->Width,
						WA_Height, screen->Height,
#else
						WA_Title, _WINDOW_TITLE_,
						WA_Width,  (screen->Width > 320) ? 320 : screen->Width,
						WA_Height, (screen->Height > 256) ? 256 : screen->Height,
#endif
						WA_MinWidth, 10,
						WA_MinHeight, 10,
						WA_MaxWidth, -1l,
						WA_MaxHeight, -1l,
						WA_AutoAdjust, TRUE,
#ifdef __ORIGINAL__
						WA_Borderless,TRUE,
#else
						WA_Borderless,FALSE,
#endif
						WA_IDCMP,IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE|IDCMP_INTUITICKS|
									IDCMP_RAWKEY|IDCMP_VANILLAKEY|
									IDCMP_NEWSIZE|IDCMP_SIZEVERIFY|IDCMP_CLOSEWINDOW,
						WA_ReportMouse,TRUE,
						WA_RMBTrap,TRUE,
						WA_RptQueue,1,
						WA_CustomScreen,screen,
#ifndef __ORIGINAL__
						WA_GimmeZeroZero, TRUE,
						WA_DragBar, TRUE,
						WA_CloseGadget, TRUE,
						WA_SizeGadget, TRUE,
						WA_SizeBBottom, TRUE,
						WA_DepthGadget, TRUE,
#endif
						WA_Activate,TRUE,
						TAG_END);

					if (window)
					{
						long col;

						view.x0 = -2.0;
						view.x1 = 2.0;
						view.y0 = 2.0;
						view.y1 = -2.0;

#ifdef __ORIGINAL__
						DoCDrawMethodTagList(&gethook,&screen->RastPort,NULL);
#else
						width = /* 3 **/ (window->Width - window->BorderLeft - window->BorderRight);
						height = /*3 **/ (window->Height - window->BorderBottom - window->BorderTop);
						x0 = 0;// width/3;
						y0 = 0;// height/3;
						gfxbuf.memptr = NULL;
						gfxbuf.offsetx = 0;
						gfxbuf.offsety = 0;
						gfxbuf.xsize = width;
						gfxbuf.ysize = height;

						gfxbuf.bytesperrow = gfxbuf.xsize;
						gfxbuf.bytesperpix = 1;
						gfxbuf.colormodel =  PIXFMT_LUT8;
#endif
						// -----------------------------------------------------------

						if (gfxbuf.colormodel == PIXFMT_LUT8)
						{
							char cbuf[200];
							int i;
							int red,green,blue;
							for (i = 0; i < 256; i++)
							{
								red   = lut[i] >> 16 & 0xff;
								green = lut[i] >> 8 & 0xff;
								blue  = lut[i] & 0xff;
#ifdef __ORIGINAL__
								SetRGB32(&screen->ViewPort,
									i,
									red|(red<<8)|(red<<16)|(red<<24),
									green|(green<<8)|(green<<16)|(green<<24),
									blue|(blue<<8)|(blue<<16)|(blue<<24))
#else
								sprintf(cbuf,"%s, obtaining shared pens...",_WINDOW_TITLE_);
								SetWindowTitles(window,cbuf,(UBYTE*)-1);

                        shared_pens[i] = ObtainBestPen(window->WScreen->ViewPort.ColorMap,
															red|(red<<8)|(red<<16)|(red<<24),
															green|(green<<8)|(green<<16)|(green<<24),
															blue|(blue<<8)|(blue<<16)|(blue<<24),
                                             OBP_Precision,PRECISION_EXACT,
                                             TAG_DONE);
#endif
							};
						};
#ifndef __ORIGINAL__
						// public
						else
						{
							gfxbuf.bytesperrow = gfxbuf.xsize * 4;
							gfxbuf.bytesperpix = 4;
							gfxbuf.colormodel = PIXFMT_ARGB32; // 24 Bit
						}
#endif
						// ----------------------------------------------------------

						gfxport = &gfxbuf;
						gfxport->memptr = (UBYTE *) AllocVec(gfxport->ysize*gfxport->bytesperrow,0);
						if (gfxport->memptr)
						{
							BOOL redraw = TRUE;
							BOOL quit = FALSE;
							BOOL contdraw = FALSE;
							double tim = 0,tim_prev = 0;
							while (!quit)
							{
								ULONG sigs;
								if (redraw)
								{
									struct RastPort *rp = window->RPort;
									ULONG h = rp->TxHeight;
									char title[256];
									clock_t tim1,tim2;

									SetDrMd(rp,JAM2);
									SetAPen(rp,0);
									SetBPen(rp,1);

#ifdef __ORIGINAL__
									TextOutXY(rp,0,rp->TxBaseline + 3*h,"calculating...");
									DrawMandelInfo(rp,N,view);
#else
									sprintf(title,"%s, Depth %ld, From (%g,%g) To (%g,%g), calculating...",
												_WINDOW_TITLE_,N,view.x0,view.y0,view.x1,view.y1);

									SetWindowTitles(window,title,(UBYTE*)-1);
#endif

									tim1 = clock();
									DrawMandelbrot(&view,gfxport,N);
									tim2 = clock();
#ifdef __ORIGINAL__
									DoCDrawMethodTagList(&drawhook,&screen->RastPort,NULL);

									DrawMandelInfo(rp,N,view);
									TextOutXY(rp,0,rp->TxBaseline + 3*h,"ready         ");

#else
									if (gfxbuf.colormodel == PIXFMT_LUT8)
									{
										long addr;
										long w = window->Width - window->BorderLeft - window->BorderRight - 1;
										long h = window->Height - window->BorderBottom - window->BorderTop - 1;
										if (w > gfxbuf.xsize) w = gfxbuf.xsize;
										if (h > gfxbuf.ysize) h = gfxbuf.ysize;
//										x0 = 0;
//										y0 = 0;
										addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;

										WriteChunkyPixels(rp,0,0,w,h,(UBYTE*)addr,gfxport->bytesperrow);
									}
									else
									{
										WritePixelArray(gfxport->memptr,0,0,gfxport->bytesperrow,rp,
														 0,0,gfxport->xsize,gfxport->ysize,RECTFMT_ARGB);
									}

									tim_prev = tim;
									tim = difftime(tim2,tim1)/CLOCKS_PER_SEC;
									sprintf(title,"%s, Depth %ld, From (%g,%g) To (%g,%g), ready: %g sec. (was: %g)",
												_WINDOW_TITLE_,N,view.x0,view.y0,view.x1,view.y1,tim,tim_prev);
									SetWindowTitles(window,title,(UBYTE*)-1);
#endif
									redraw = FALSE;
								};
								sigs = Wait(4096|(1L << window->UserPort->mp_SigBit));
								if (sigs & 4096)
								{
									break;
								}
								while (msg = (struct IntuiMessage *) GetMsg(window->UserPort))
								{
									double w,h;
									double dx,dy;
									double mx,my;

#ifndef __ORIGINAL__
									if ((msg->Class == IDCMP_NEWSIZE) ||
										 (msg->Class == IDCMP_SIZEVERIFY))
									{
										long addr,w,h;

										APTR mem = gfxport->memptr;
										gfxbuf.xsize =/*3 **/ (window->Width - window->BorderLeft - window->BorderRight);
										gfxbuf.ysize =/* 3 **/ (window->Height - window->BorderBottom - window->BorderTop);
										gfxbuf.bytesperrow = gfxbuf.xsize; // GetCyberMapAttr(window->RPort->BitMap,CYBRMATTR_XMOD);
										gfxport->memptr = (UBYTE *) AllocVec(gfxport->ysize*gfxport->bytesperrow,0);
										FreeVec(mem);
										redraw = TRUE;
/*
										w = window->Width - window->BorderLeft - window->BorderRight - 1;
										h = window->Height - window->BorderBottom - window->BorderTop - 1;
										if (w > gfxbuf.xsize) w = gfxbuf.xsize;
										if (h > gfxbuf.ysize) h = gfxbuf.ysize;
										addr = (long)gfxport->memptr + x0 + y0 * gfxport->bytesperrow;

										WriteChunkyPixels(window->RPort,0,0,w,h,(UBYTE*)addr,gfxport->bytesperrow);
*/									}
#endif
									w = (double) gfxport->xsize;
									h = (double) gfxport->ysize;
									dx = (view.x1 - view.x0) / w;
									dy = (view.y1 - view.y0) / h;
									mx = (double) msg->MouseX;
									my = (double) msg->MouseY;
#ifdef __ORIGINAL__
									if ((msg->Class == IDCMP_MOUSEBUTTONS && msg->Code == SELECTDOWN)
										|| msg->Qualifier & IEQUALIFIER_LEFTBUTTON
//										|| contdraw
										)
									{
										view.x0 = view.x0 + 0.05 * mx * dx;
										view.x1 = view.x0 + 0.95 * dx * w;
										view.y0 = view.y0 + 0.05 * my * dy;
										view.y1 = view.y0 + 0.95 * dy * h;
										redraw = TRUE;
										contdraw = TRUE;
									}
									if ((msg->Class == IDCMP_MOUSEBUTTONS && msg->Code == MENUDOWN)
										|| msg->Qualifier & IEQUALIFIER_RBUTTON)
									{
										view.x0 = view.x0 - 0.05 * mx * dx;
										view.x1 = view.x0 + 1.05 * dx * w;
										view.y0 = view.y0 - 0.05 * my * dy;
										view.y1 = view.y0 + 1.05 * dy * h;
										redraw = TRUE;
									}
#endif
									if (msg->Class == IDCMP_RAWKEY)
									{
										BOOL shift_on = FALSE;
										long delta = 20;
/*
										if ((msg->Qualifier & IEQUALIFIER_LSHIFT) ||
											 (msg->Qualifier & IEQUALIFIER_RSHIFT))
										{
											shift_on = FALSE;
										}
										if ((msg->Qualifier & IEQUALIFIER_LALT) ||
											 (msg->Qualifier & IEQUALIFIER_RALT))
										{
											delta = 100;
										}
*/
										switch (msg->Code)
										{
											case CURSORUP: // up
												if (shift_on)
												{
													ScrollView(window,gfxport,0,-delta);
												}
												else
												{
													view.y0 -= dy * h / 4;
													view.y1 = view.y0 + h * dy;
													redraw = TRUE;
												}
												break;
											case CURSORDOWN: // down
												if (shift_on)
												{
													ScrollView(window,gfxport,0, delta);
												}
												else
												{
													view.y0 += dy * h / 4;
													view.y1 = view.y0 + h * dy;
													redraw = TRUE;
												}
												break;
											case CURSORRIGHT: // right
												if (shift_on)
												{
													ScrollView(window,gfxport,delta,0);
												}
												else
												{
													view.x0 += dx * w / 4;
													view.x1 = view.x0 + w * dx;
													redraw = TRUE;
												}
												break;
											case CURSORLEFT: // left
												if (shift_on)
												{
													ScrollView(window,gfxport,-delta,0);
												}
												else
												{
													view.x0 -= dx * w / 4;
													view.x1 = view.x0 + w * dx;
													redraw = TRUE;
												}
												break;
										};
									}
									if (msg->Class == IDCMP_VANILLAKEY)
									{
										if (msg->Code >= '1' && msg->Code <= '9')
										{
											if (msg->Qualifier & IEQUALIFIER_CONTROL)
												views[msg->Code - '1'] = view
											else
												view = views[msg->Code - '1'];
											redraw = TRUE;
										}
										else
										{
											switch (msg->Code)
											{
												case '+':
													if (N < 1000)
													{
														N += 20;
														redraw = TRUE;
													};
													break;
												case '-':
													if (N >= 40)
													{
														N -= 20;
														redraw = TRUE;
													};
													break;
												case 'q':
													quit = TRUE;
													break;
												case 's':
													contdraw = FALSE;
													break;
												case 'i':
													view.x0 = view.x0 + 0.05 * mx * dx;
													view.x1 = view.x0 + 0.95 * dx * w;
													view.y0 = view.y0 + 0.05 * my * dy;
													view.y1 = view.y0 + 0.95 * dy * h;
													redraw = TRUE;
													contdraw = TRUE;
													break;
												case 'o':
													view.x0 = view.x0 - 0.05 * mx * dx;
													view.x1 = view.x0 + 1.05 * dx * w;
													view.y0 = view.y0 - 0.05 * my * dy;
													view.y1 = view.y0 + 1.05 * dy * h;
													redraw = TRUE;
													break;
											};
										};
									};
									if (msg->Class == IDCMP_CLOSEWINDOW)
									{
										quit = TRUE;
									}
									ReplyMsg((struct Message *) msg);
								};
							};

							FreeVec(gfxport->memptr);
						};
                  for (col = 0; col < 256 ; col++)
	                  ReleasePen(window->WScreen->ViewPort.ColorMap,shared_pens[col]);

						CloseWindow(window);

					};
#ifdef __PUB_SCREEN__
					UnlockPubScreen(NULL,screen);
#else
					CloseScreen(screen);
#endif
				};
#ifndef __PUB_SCREEN__
			};
			FreeAslRequest(request);
		};
#endif
		CloseLibrary(CyberGfxBase);
	};

	if (PowerPCBase)	CloseLibrary(PowerPCBase);

}

void wbmain(struct WBStartup * argmsg)
{
	main();
}
