/*  :ts=8 bk=0
 * Super big bitmap explorer.
 * Jon would call this a parlor trick.
 *
 * Leo L. Schwab		8607.30
 */

/*  I shouldn't have to include graphics/copper.h myself  */
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/copper.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <devices/gameport.h>
#include <devices/inputevent.h>

#define REV		0L
#define DEPTH		2
#define	WIDTH		960L
#define	HEIGHT		600L
#define DWIDTH		320
#define	DHEIGHT		200
#define MAXXOFF		(WIDTH-DWIDTH)
#define MAXYOFF		(HEIGHT-DHEIGHT)
#define ever		(;;)

extern void	*OpenLibrary(), *AllocRaster(), *AllocMem(), *GetColorMap(),
		*CreateStdIO(), *CreatePort();
extern long	OpenDevice(), DoIO();


struct GamePortTrigger	gpt = {
	GPTF_UPKEYS | GPTF_DOWNKEYS,
	0,
	1, 1
};
UWORD		colors[] = { 0, 0xf00, 0x0f0, 0x00f };

struct View	v, *oldview;
struct ViewPort	vp;
struct ColorMap	*cm;
struct RasInfo	ri;
struct BitMap	*bm;
struct RastPort	rp;
struct GfxBase	*GfxBase;
struct InputEvent	joyreport;
struct IOStdReq	*gameio;
struct MsgPort	*gameport;



main ()
{
	int i = 1, x, y;

	openstuff ();
	makescreen ();		/*  NOT Intuition call  */
	initjoystick ();

	SetDrMd (&rp, JAM1);
	SetRast (&rp, 0L);
	SetAPen (&rp, 1L);
	for (x=0, y=0; x<WIDTH; x += 16, y += 10) {
		Move (&rp, (long) x, 0L);
		Draw (&rp, WIDTH-1, (long) y);
		Draw (&rp, WIDTH-1-x, HEIGHT-1);
		Draw (&rp, 0L, HEIGHT-1-y);
		Draw (&rp, (long) x, 0L);
		if (!(++i & 3))    /*  I love weird expressions like this  */
			++i;
		SetAPen (&rp, (long) i);
	}
	SetAPen (&rp, 3L);
	Move (&rp, 429L, 301L);
	Text (&rp, "Hello, World!", 13L);
	SetAPen (&rp, 1L);
	Move (&rp, 428L, 300L);
	Text (&rp, "Hello, World!", 13L);

	x = ri.RxOffset;
	y = ri.RyOffset;
	SendIO (gameio);
	for ever {
		WaitIO (gameio);
		if (joyreport.ie_Code == IECODE_LBUTTON)
			/*  Fire button pressed; exit program  */
			break;

		x += joyreport.ie_X;
		if (x < 0 || x > MAXXOFF)
			x = x<0 ? 0 : MAXXOFF;

		y += joyreport.ie_Y;
		if (y < 0 || y > MAXYOFF)
			y = y<0 ? 0 : MAXYOFF;

		if (ri.RxOffset != x || ri.RyOffset != y) {
			ri.RxOffset = x;
			ri.RyOffset = y;
			WaitTOF ();
			ScrollVPort (&vp);
		}
		SendIO (gameio);
	}
	closeeverything ();
}


openstuff ()
{
	long err;

	if (!(GfxBase = OpenLibrary ("graphics.library", REV)))
		die ("Art shop closed.\n");

	if (!(gameport = CreatePort (0L, 0L)))
		die ("Can't make msgport.\n");

	if (!(gameio = CreateStdIO (gameport)))
		die ("Can't make IO packet.\n");

	if (err = OpenDevice ("gameport.device", 1L, gameio, 0L))
		die ("Games closed.\n");

	if (!(bm = AllocMem ((long) sizeof (*bm), MEMF_CHIP | MEMF_CLEAR)))
		die ("Can't allocate BitMap.\n");
}

makescreen ()
{
	register int i;

	InitView (&v);
	InitVPort (&vp);
	InitBitMap (bm, (long) DEPTH, WIDTH, HEIGHT);
	InitRastPort (&rp);

	v.ViewPort = &vp;

	ri.BitMap = bm;
	ri.RxOffset = ri.RyOffset = ri.Next = NULL;

	vp.DWidth = DWIDTH;
	vp.DHeight = DHEIGHT;
	vp.RasInfo = &ri;
	vp.ColorMap = GetColorMap (4L);

	rp.BitMap = bm;

	for (i=0; i<DEPTH; i++)
		if (!(bm -> Planes[i] = AllocRaster (WIDTH, HEIGHT)))
			die ("Can't allocate memory for plane.\n");

	MakeVPort (&v, &vp);
	MrgCop (&v);
	LoadRGB4 (&vp, colors, 4L);
	oldview = GfxBase -> ActiView;
	LoadView (&v);
}

closeeverything ()
{
	register int i;

	if (oldview) {
		LoadView (oldview);
		WaitTOF ();	/*  Make sure copper is using old view  */
		FreeVPortCopLists (&vp);
		FreeCprList (v.LOFCprList);
	}
	if (vp.ColorMap)
		FreeColorMap (vp.ColorMap);
	if (bm) {
		for (i=0; i<DEPTH; i++)
			if (bm -> Planes[i])
				FreeRaster (bm -> Planes[i], WIDTH, HEIGHT);
		FreeMem (bm, (long) sizeof (*bm));
	}
	if (gameio) {
		if (gameio -> io_Device)
			CloseDevice (gameio);
		DeleteStdIO (gameio);
	}
	if (gameport)
		DeletePort (gameport);
	if (GfxBase)
		CloseLibrary (GfxBase);
}

die (str)
char *str;
{
	puts (str);
	closeeverything ();
	exit (100);
}

initjoystick ()
{
	UBYTE type = GPCT_RELJOYSTICK;

	gameio -> io_Command = GPD_SETCTYPE;
	gameio -> io_Length = 1;
	gameio -> io_Data = &type;
	if (DoIO (gameio))
		die ("Error in setting controller type.\n");

	gameio -> io_Command = GPD_SETTRIGGER;
	gameio -> io_Length = sizeof (gpt);
	gameio -> io_Data = &gpt;
	if (DoIO (gameio))
		die ("Error in setting trigger values.\n");

	gameio -> io_Command = GPD_READEVENT;
	gameio -> io_Length = sizeof (joyreport);
	gameio -> io_Data = &joyreport;
}
