
#include "Mandel.h"

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

#include <pragma/intuition_lib.h>
#include <pragma/asl_lib.h>
#include <pragma/exec_lib.h>
#include <clib/cybergraphics_protos.h>
#include <pragma/cybergraphics_lib.h>
#include <clib/alib_protos.h>
#include <pragma/graphics_lib.h>
#include <pragma/powerpc_lib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

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)

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

	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);
	return 0;
}

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

static void DrawMandelInfo(struct RastPort *rp, BOOL ppc, int N, Rect &view)
{
	ULONG h = rp->TxHeight;
	char buf[64];
	SetDrMd(rp,JAM2);
	SetAPen(rp,0);
	SetBPen(rp,1);
	Move(rp,0,rp->TxBaseline);
	if (ppc)
		PrintText(rp,"CPU: PPC")
	else
		PrintText(rp,"CPU: 68K");
	sprintf(buf,", Depth: %ld",N);
	PrintText(rp,buf);
	Move(rp,0,rp->cp_y+h);
	sprintf(buf,"From: (%g,%g)",view.x0,view.y0);
	PrintText(rp,buf);
	Move(rp,0,rp->cp_y+h);
	sprintf(buf,"To:   (%g,%g)",view.x1,view.y1);
	PrintText(rp,buf);
}

struct Library *CyberGfxBase;

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);

	CyberGfxBase = OpenLibrary("cybergraphics.library",40);
	if (CyberGfxBase)
	{
		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,
					TAG_END);
				if (screen)
				{
					struct Window *window;
					struct IntuiMessage *msg;

					window = OpenWindowTags(NULL,
						WA_Left,0,
						WA_Top,0,
						WA_Width,screen->Width,
						WA_Height,screen->Height,
						WA_Borderless,TRUE,
						WA_IDCMP,IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE|IDCMP_INTUITICKS
							|IDCMP_RAWKEY|IDCMP_VANILLAKEY,
						WA_ReportMouse,TRUE,
						WA_RMBTrap,TRUE,
						WA_RptQueue,1,
						WA_CustomScreen,screen,
						WA_Activate,TRUE,
						TAG_END);
					if (window)
					{
						view.x0 = -2.0;
						view.x1 = 2.0;
						view.y0 = 2.0;
						view.y1 = -2.0;

						DoCDrawMethodTagList(&gethook,&screen->RastPort,NULL);
						if (gfxbuf.colormodel == PIXFMT_LUT8)
						{
							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;
								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))
							};
						};
						gfxport = &gfxbuf;
						gfxport->memptr = (UBYTE *) AllocVec(gfxport->ysize*gfxport->bytesperrow,0);
						if (gfxport->memptr)
						{
							BOOL redraw = TRUE;
							BOOL quit = FALSE;
							BOOL draw_with_ppc = TRUE;
							BOOL contdraw = FALSE;
							while (!quit)
							{
								ULONG sigs;
								if (redraw)
								{
									if (draw_with_ppc)
										PPCDrawMandelbrot(&view,gfxport,N)
									else
										DrawMandelbrot(&view,gfxport,N);
									DoCDrawMethodTagList(&drawhook,&screen->RastPort,NULL);
									DrawMandelInfo(&screen->RastPort,draw_with_ppc,N,view);
									redraw = FALSE;
								};
								sigs = Wait(4096|(1L << window->UserPort->mp_SigBit));
								if (sigs & 4096)
									break;
								while (msg = (struct IntuiMessage *) GetMsg(window->UserPort))
								{
									double w = (double) gfxport->xsize;
									double h = (double) gfxport->ysize;
									double dx = (view.x1 - view.x0) / w;
									double dy = (view.y1 - view.y0) / h;
									double mx = (double) msg->MouseX;
									double my = (double) msg->MouseY;
									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;
									}
									if (msg->Class == IDCMP_RAWKEY)
									{
										switch (msg->Code)
										{
											case 0x4c: // up
												view.y0 -= dy * h / 4;
												view.y1 = view.y0 + h * dy;
												redraw = TRUE;
												break;
											case 0x4d: // down
												view.y0 += dy * h / 4;
												view.y1 = view.y0 + h * dy;
												redraw = TRUE;
												break;
											case 0x4e: // right
												view.x0 += dx * w / 4;
												view.x1 = view.x0 + w * dx;
												redraw = TRUE;
												break;
											case 0x4f: // left
												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 't':
													draw_with_ppc = !draw_with_ppc;
													redraw = TRUE;
													break;
												case 's':
													contdraw = FALSE;
													break;
											};
										};
									};
									ReplyMsg((struct Message *) msg);
								};
							};

							FreeVec(gfxport->memptr);
						};

						CloseWindow(window);

					};
					CloseScreen(screen);
				};
			};
			FreeAslRequest(request);
		};

		CloseLibrary(CyberGfxBase);
	};
}
