#include <exec/exec.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <stdlib.h>

/* our system libraries addresses */
struct GfxBase* GfxBase = 0;
struct IntuitionBase* IntuitionBase = 0;

struct RenderEngineData
{
	struct Window* window;
	BOOL run;
};

void RepaintWindow(struct RenderEngineData* rd)
{
	struct RastPort* rastPort;
	struct Rectangle outputRect;
	struct tPoint lineStep;
	struct tPoint pos;
	WORD i;

	const WORD stepCount = 32;

	/* we make a local copy of our RastPort pointer for ease of use */
	rastPort = rd->window->RPort;

	/* our output rectangle is our whole window area minus its borders */
	outputRect.MinY = rd->window->BorderTop;
	outputRect.MinX = rd->window->BorderLeft;
	outputRect.MaxX = rd->window->Width - rd->window->BorderRight - 1;
	outputRect.MaxY = rd->window->Height - rd ->window->BorderBottom - 1;

	/* clear our output rectangle */
	SetDrMd(rastPort, JAM1);
	SetAPen(rastPort, 0);
	RectFill(rastPort, (LONG)outputRect.MinX, (LONG)outputRect.MinY,
	         (LONG)outputRect.MaxX, (LONG)outputRect.MaxY);

	/* now draw our line pattern */
	lineStep.x = (outputRect.MaxX - outputRect.MinX) / stepCount;
	lineStep.y = (outputRect.MaxY - outputRect.MinY) / stepCount;

	SetAPen(rastPort, 1);
	pos.x = 0;
	pos.y = 0;
	for(i = 0; i < stepCount; i++)
	{
		Move(rastPort, (LONG)outputRect.MinX, (LONG)(outputRect.MinY + pos.y));
		Draw(rastPort, (LONG)(outputRect.MaxX - pos.x), (LONG)outputRect.MinY);
		Draw(rastPort, (LONG)outputRect.MaxX, (LONG)(outputRect.MaxY - pos.y));
		Draw(rastPort, (LONG)(outputRect.MinX + pos.x), (LONG)outputRect.MaxY);
		Draw(rastPort, (LONG)outputRect.MinX, (LONG)(outputRect.MinY + pos.y));

		pos.x += lineStep.x;
		pos.y += lineStep.y;
	}
}

void DispatchWindowMessage(struct RenderEngineData* rd,
                           struct IntuiMessage* msg)
{
	switch(msg->Class)
	{
		case IDCMP_CLOSEWINDOW:
		{
			/* User pressed the window's close gadget: exit the main loop as
			 * soon as possible */
			rd->run = FALSE;
			break;
		}
		case IDCMP_NEWSIZE:
		{
			RepaintWindow(rd);
			break;
		}
		case IDCMP_REFRESHWINDOW:
		{
			BeginRefresh(rd->window);
			RepaintWindow(rd);
			EndRefresh(rd->window, TRUE);
			break;
		}
	}
}

int MainLoop(struct RenderEngineData* rd)
{
	struct MsgPort* winport;
	ULONG winSig;

	/* remember the window port in a local variable for more easy use */
	winport = rd->window->UserPort;

	/* create our waitmask for the window port */
	winSig = 1 << winport->mp_SigBit;

	/* paint our window for the first time */
	RepaintWindow(rd);

	/* our main loop */
	while(rd->run)
	{
		struct Message* msg;

		/* let's sleep until a message from our window arrives */
		Wait(winSig);

		/* our window signaled us, so let's harvest all its messages
		 * in a loop... */
		while((msg = GetMsg(winport)))
		{
			/* ...and dispatch and reply each of them */
			DispatchWindowMessage(rd, (struct IntuiMessage*)msg);
			ReplyMsg(msg);
		}
	}
	return RETURN_OK;
}

int RunEngine(void)
{
	struct RenderEngineData* rd;

	/* as long we did not enter our main loop we report an error */
	int result = RETURN_ERROR;

	/* allocate the memory for our runtime data and ititialize it
	 * with zeros */
	if((rd = (struct RenderEngineData*)
	        AllocMem(sizeof(struct RenderEngineData), MEMF_ANY | MEMF_CLEAR)))
	{
		/* now let's open our window */
		static struct NewWindow newWindow =
		{
			0, 14,
			320, 160,
			(UBYTE)~0, (UBYTE)~0,
			IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | IDCMP_REFRESHWINDOW,
			WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET |
			WFLG_SIMPLE_REFRESH | WFLG_SIZEBBOTTOM | WFLG_SIZEGADGET,
			0, 0,
			"Gfx Workshop",
			0,
			0,
			96, 48,
			(UWORD)~0, (UWORD)~0,
			WBENCHSCREEN
		};
		if((rd->window = OpenWindow(&newWindow)))
		{
			/* the main loop will run as long this is TRUE */
			rd->run = TRUE;

			result = MainLoop(rd);

			/* cleanup: close the window */
			CloseWindow(rd->window);
			rd->window = 0;
		}

		/* free our runtime data */
		FreeMem(rd, sizeof(struct RenderEngineData));
		rd = 0;
	}

	return result;
}

int main(int argc, char* argv[])
{
	/* as long we did not execute RunEngine() we report a failure */
	int result = RETURN_FAIL;

	/* we need at least 1.2 graphic.library's drawing functions */
	if((GfxBase = (struct GfxBase*)OpenLibrary("graphics.library", 33)))
	{
		/* we need at least 1.2 intuition.library for our window */
		if((IntuitionBase = (struct IntuitionBase*)
		                   OpenLibrary("intuition.library", 33)))
		{
			/* All libraries needed are available, so let's run... */
			result = RunEngine();

			CloseLibrary((struct Library*)IntuitionBase);
			IntuitionBase = 0;
		}
		CloseLibrary((struct Library*)GfxBase);
		GfxBase = 0;
	}

	/* some startup codes do ignore main's return value, that's
	 * why we use exit() here instead of a simple return */
	exit(result);
}
