#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/view.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <dos.h>
#include <proto/dos.h>
#include <devices/audio.h>

#define UP 1
#define DOWN 2
#define ON 1
#define OFF 0

#define VERIFY	0
#define VIEW	1
#define NOREFRESH 2	/* Prevents calls to MakeScreen(s); RethinkDisplay(); */

#undef SINGLE

enum icons { PLYR1,PLYR2,PRACTICE,DEMO,PLAY,QUIT,_BAIZE,_BALL1,_BALL2,
	SINGLE,BEST3,BEST5,UNLIMITED,STRIP,BALL10,BALL16,
	OPPONENTBASE,ICON17,ICON18,ICON19,STRIPPERBASE };

extern struct NewScreen	ns;
extern struct NewWindow	nw;
extern struct Screen 	*s,*s2;
extern struct Window 	*w,*w2;
extern struct RastPort	*rp;
extern struct ViewPort 	*vp;
extern struct ColorMap 	*cm;
extern struct FileHandle	*fh;

extern UWORD *colorpalette;
extern UWORD rgb[32];

struct IFFGraphic
{
char		FileName[30];	/* Name of IFF file */
BYTE		ReqDepth;		/* How many bitplanes wish to be Displayed */  
USHORT	*RGB;		/* RGB colour values, filled by LoadIFF */ 
short	Width;		/* Width of graphic, filled by LoadIFF */ 
short	Height;		/* Height of graphic, filled by LoadIFF */ 
BYTE		Depth;		/* Depth of graphic, filled by LoadIFF */ 
};

extern struct IFFGraphic roomIFF;
extern struct IFFGraphic offerIFF;
struct IFFGraphic titleIFF =  { "FNPGRAPHICS:title.img",5,rgb };
struct IFFGraphic settingsIFF =  { "FNPGRAPHICS:settings.img",5,rgb };
struct IFFGraphic opponentIFF =  { "FNPGRAPHICS:opponent.img",5,rgb };
struct IFFGraphic stripperIFF =  { "FNPGRAPHICS:stripper.img",5,rgb };

struct SnapShot { short x,y; };
struct SnapShot snapshot[8] =
{ 6,44, 237,139, 6,146, 50,146, 93,146, 302,180, 0,0, 0,0 };


struct iconstruct
{ short x1,y1,x2,y2,type; };

#define FLASH 1
#define TICK 2
#define CROSS 3

struct iconstruct icon[24] =
{{ 5,43,59,97,FLASH },{ 71,43,125,97,FLASH },
{ 136,43,190,97,FLASH },{ 201,43,255,97,FLASH },

{ 266,43,315,68,NULL },{ 266,73,315,98,NULL },

{ 5,145,37,176,NULL },{ 49,145,81,176,NULL },{ 92,145,123,176,NULL },

{ 236,138,248,150,TICK },{ 236,150,248,162,TICK },{ 236,162,248,174,TICK },
{ 236,174,248,186,TICK },{ 301,123,313,135,TICK }, { 301,167,313,179,TICK },
{ 301,179,313,191,TICK },
	
{ 1,152,60,202,NULL },{ 1,206,60,256,NULL },
{ 1,44,60,94,NULL },{ 1,98,60,148,NULL },

{ 161,174,220,223,NULL },{ 228,174,287,223,NULL },
{ 27,174,85,223,NULL },{ 94,174,153,223,NULL }};

UWORD chip tickmask[8] =
{ 0x0100,0x0300,0x0700,0x8e00,0xdc00,0xf800,0x7000,0x2000 };

UWORD chip xmask[7] =
{ 0xc600,0x6c00,0x3800,0x3800,0x6c00,0xc600,0x8200 };

UWORD chip pointeron[24] = {
0,0,0xc000,0x4000,0x7000,0xb000,0x3c00,0x4c00,0x3f00,0x4300,
0x1fc0,0x20c0,0x1fc0,0x2000,0x0f00,0x1100,0x0d80,0x1280,
0x04c0,0x0940,0x0460,0x08a0,0x0020,0x0040 };

UWORD chip pointeroff[8];

enum args { GAMETYPE,GAMELENGTH,BAIZE,BALL1,BALL2,BALLCOUNT,OPPONENT,STRIPPER };

#define NOPRESETS -1

char arg[8] = { PLYR1,SINGLE,0,0,2,16,0,0 },bufarg[8],demoarg[8];
extern short defaultdemo,enableoptions;

UWORD flash;

struct shade { UWORD light,medium,dark; };
 
struct shade baizeRGB[] = {
{ 0x280,0x160,0x040 }, /* green */
{ 0x269,0x146,0x034 }, /* blue */
{ 0x954,0x743,0x521 }, /* brown */
{ 0x777,0x555,0x333 }}; /* grey */

struct shade ballRGB[] = {
{ 0xf00,0xc00,0x800 }, /* red */
{ 0xf60,0xe40,0xb20 }, /* orange */
{ 0xfc0,0xf90,0xb50 }, /* yellow */
{ 0x9d0,0x6b0,0x370 }, /* green */
{ 0x09f,0x07e,0x04a }, /* blue */
{ 0x90a,0x807,0x604 }, /* purple */
{ 0xf77,0xe55,0xa32 }}; /* pink */

/******* Audio stuff *************/

#define SAMPLES 8

enum sound { COLLIDE,STRIKE,BOUNCE,POTTED,CHEER,LONGCHEER,CHANT,CRASH };

struct audioinfostruct
{
	struct IOAudio *audio;
	char *wave,bufstate,channel,flag;
	long wavelen;
	short volume,period;
	char filename[30];
};
extern struct audioinfostruct audioinfo[SAMPLES];

char Loader(short);
void FadeScreen(struct Screen *,short);
char LoadIFF(struct IFFGraphic *,struct Screen *,char);
void DoStripper(void);
void DoOpponent(void);
char DoSettings(short);
void DrawSymbol(short,short);
char ClickDelay(short);
extern short GetMouse(void);
extern short GetInput(void);
extern void PlaySound(struct audioinfostruct *,short);
extern UWORD Rand(UWORD);
extern long MyRead(struct FileHandle *,long *,long);

#define RETURN 68
#define LMOUSE 1
#define RMOUSE 2

/************************/
char Loader(start)
short start;
{
	short i;
	
	if (start)
	{
		SetPointer(w,pointeroff,1,1,0,0);
		SetPointer(w2,pointeroff,1,1,0,0);
/*		LoadIFF(&offerIFF,s,VIEW); ClickDelay(1000); */
		LoadIFF(&roomIFF,s2,VIEW); LoadIFF(&titleIFF,s,VIEW);
		PlaySound(&audioinfo[STRIKE],64); Delay(10);
		PlaySound(&audioinfo[COLLIDE],64); Delay(20);
		PlaySound(&audioinfo[POTTED],64);
		PlaySound(&audioinfo[LONGCHEER],64);
		for (i=0; i<5; i++)
		{
			colorpalette[25] = 0xf7b; colorpalette[26] = 0xf2b;
			colorpalette[27] = 0xfcc; MakeScreen(s); RethinkDisplay();
			if (ClickDelay(50)) break;
			colorpalette[25] = 0x77f; colorpalette[26] = 0x06d;
			colorpalette[27] = 0xccf; MakeScreen(s); RethinkDisplay();
			if (ClickDelay(50)) break;
		}
	}
	LoadIFF(&settingsIFF,s,VIEW);
	ActivateWindow(w);
	if (enableoptions) SetPointer(w,pointeron,11,11,0,0);
	else
	{
		SetPointer(w,pointeroff,1,1,0,0);
		snapshot[GAMETYPE].x = 202;
		snapshot[GAMELENGTH].x = 237;
		snapshot[GAMELENGTH].y = 139;
	}
	for (i=GAMETYPE; i<OPPONENT; i++) DoSettings(i);

	if (!(DoSettings(NOPRESETS))) return(0);

	if (arg[GAMELENGTH]==STRIP)
	{
		LoadIFF(&stripperIFF,s,VIEW);
		DoStripper();
	}
	else if (arg[GAMETYPE]==PLYR1)
	{
		LoadIFF(&opponentIFF,s,VIEW);
		DoOpponent();
	}
	SetPointer(w,pointeroff,1,1,0,0);
	return(1);
}
/************************/
void DoStripper()
{
	while(1)
	{
		short x,y,i;
		
		x = s->MouseX; y = s->MouseY;
		if (GetMouse() == LMOUSE)
		{
			for (i=STRIPPERBASE; i<(STRIPPERBASE+4); i++)
			{
				if (x > icon[i].x1 && x < icon[i].x2 &&
				    y > icon[i].y1 && y < icon[i].y2)
				{
					PlaySound(&audioinfo[COLLIDE],64);
					arg[STRIPPER] = i-STRIPPERBASE;
					arg[OPPONENT] = 0;
					return;
				}
			}
		}
	}
}
/************************/
void DoOpponent()
{
	while(1)
	{
		short x,y,i;
		
		x = s->MouseX; y = s->MouseY;
		if (GetMouse() == LMOUSE)
		{
			for (i=OPPONENTBASE; i<(OPPONENTBASE+4); i++)
			{
				if (x > icon[i].x1 && x < icon[i].x2 &&
				    y > icon[i].y1 && y < icon[i].y2)
				{
					PlaySound(&audioinfo[COLLIDE],64);
					arg[OPPONENT] = i-OPPONENTBASE; return;
				}
			}
		}
	}
}
/************************/
void DrawBox(i,status)
short i,status;
{
	if (status==ON) SetAPen(rp,31);
	else SetAPen(rp,1);
	
	Move(rp,icon[i].x1,icon[i].y1); Draw(rp,icon[i].x2,icon[i].y1);
	Draw(rp,icon[i].x2,icon[i].y2); Draw(rp,icon[i].x1,icon[i].y2);
	Draw(rp,icon[i].x1,icon[i].y1);
}
/************************/
void DrawSymbol(i,status)
short i,status;
{
	SetAPen(rp,0);
	RectFill(rp,icon[i].x1+3,icon[i].y1+3,icon[i].x1+10,icon[i].y1+10);
	SetAPen(rp,3);
	if (status==TICK)
		BltPattern(rp,tickmask,icon[i].x1+3,icon[i].y1+3,icon[i].x1+10,icon[i].y1+10,2);
	else if (status==CROSS) BltPattern(rp,xmask,icon[i].x1+3,icon[i].y1+3,icon[i].x1+10,icon[i].y1+9,2);
}
/************************/
char DoSettings(preset)
short preset;
{
	short count = (enableoptions)? 0:1250;
	
	while(1)
	{
		short x,y,i,j;
		
		flash+=0x111;
		if (flash > 0xfff) flash=0;
		colorpalette[31] = flash;
		MakeScreen(s); RethinkDisplay();
		for (i=0; i<32; i++) colorpalette[i] = rgb[i];
		
		if (preset==NOPRESETS) { x = s->MouseX; y = s->MouseY; }
		else { x = snapshot[preset].x; y = snapshot[preset].y; }
		if (GetMouse() == LMOUSE || ++count==1500 || preset!=NOPRESETS)
		{
			if (count==1500)
			{
				for (i=0; i<8; i++) bufarg[i] = arg[i];
				demoarg[GAMETYPE] = DEMO;
				demoarg[GAMELENGTH] = SINGLE;
				if (demoarg[BALLCOUNT] == 16) demoarg[BALLCOUNT] = 10;
				else demoarg[BALLCOUNT] = 16;
				demoarg[BALL1] = Rand(7);
				do { demoarg[BALL2] = Rand(7); }
				while (demoarg[BALL2]==demoarg[BALL1]);
				if (++demoarg[BAIZE] > 3) demoarg[BAIZE] = 0;	
				for (i=0; i<8; i++) arg[i] = demoarg[i];
				defaultdemo=TRUE; return(TRUE);
			}
			while(GetMouse() == LMOUSE && enableoptions);
			
			if (enableoptions || preset!=NOPRESETS)
			for (count=0,i=0; i<OPPONENTBASE; i++)
			{
				if (x > icon[i].x1 && x < icon[i].x2 &&
				    y > icon[i].y1 && y < icon[i].y2)
				{
					if (preset==NOPRESETS) PlaySound(&audioinfo[COLLIDE],64);
					if (icon[i].type==FLASH)
					{
						snapshot[GAMETYPE].x = x;
						snapshot[GAMETYPE].y = y;
					
						for (j=0; j<4; j++) DrawBox(j,OFF);
						DrawBox(i,ON); arg[GAMETYPE] = i;
					
						switch(i)
						{
							case PLYR1:
							case PLYR2:
								for (j=9; j<14; j++) DrawSymbol(j,OFF);
								if (i==PLYR2) DrawSymbol(STRIP,CROSS);
								DrawSymbol(SINGLE,TICK);
								arg[GAMELENGTH] = SINGLE;
								snapshot[GAMELENGTH].x = 237;
								snapshot[GAMELENGTH].y = 139;
								break;
							case PRACTICE:
								for (j=9; j<14; j++) DrawSymbol(j,CROSS);
								DrawSymbol(UNLIMITED,TICK);
								arg[GAMELENGTH] = UNLIMITED;
								snapshot[GAMELENGTH].x = 237;
								snapshot[GAMELENGTH].y = 175;
								break;
							case DEMO:
								for (j=9; j<14; j++) DrawSymbol(j,OFF);
								DrawSymbol(STRIP,CROSS);
								DrawSymbol(UNLIMITED,TICK);
								arg[GAMELENGTH] = UNLIMITED;
								snapshot[GAMELENGTH].x = 237;
								snapshot[GAMELENGTH].y = 175;
								break;
						}
					}
					else if (icon[i].type==TICK)
					{
						if (i==BALL10 || i==BALL16)
						{
							snapshot[BALLCOUNT].x = x;
							snapshot[BALLCOUNT].y = y;

							DrawSymbol(BALL10,OFF);
							DrawSymbol(BALL16,OFF);
							DrawSymbol(i,TICK);
							arg[BALLCOUNT] = (i==BALL10)? 10:16;
						}
						else if (!(arg[GAMETYPE]!=PLYR1 && i==STRIP)
							&& arg[GAMETYPE]!=PRACTICE)
						{
							snapshot[GAMELENGTH].x = x;
							snapshot[GAMELENGTH].y = y;
							
							DrawSymbol(arg[GAMELENGTH],OFF);
							DrawSymbol(i,TICK);
							arg[GAMELENGTH] = i;
						}
					}
					else switch(i)
					{
						case _BAIZE:
							snapshot[BAIZE].x = x;
							snapshot[BAIZE].y = y;
							if (preset==NOPRESETS && ++arg[BAIZE] > 3) arg[BAIZE] = 0;
							rgb[24] = baizeRGB[arg[BAIZE]].light;
							rgb[7] = baizeRGB[arg[BAIZE]].medium;
							rgb[6] = baizeRGB[arg[BAIZE]].dark;
							break;
						
						case _BALL1:
							snapshot[BALL1].x = x;
							snapshot[BALL1].y = y;
							if (preset==NOPRESETS)
								do if (++arg[BALL1] > 6) arg[BALL1] = 0;
							while (arg[BALL1]==arg[BALL2]);
							rgb[9]=ballRGB[arg[BALL1]].light;
							rgb[8]=ballRGB[arg[BALL1]].medium;
							rgb[22]=ballRGB[arg[BALL1]].dark;
							break;
							
						case _BALL2:
							snapshot[BALL2].x = x;
							snapshot[BALL2].y = y;
							if (preset==NOPRESETS)
								do if (++arg[BALL2] > 6) arg[BALL2] = 0;
							while (arg[BALL1]==arg[BALL2]);
							rgb[12]=ballRGB[arg[BALL2]].light;
							rgb[11]=ballRGB[arg[BALL2]].medium;
							rgb[23]=ballRGB[arg[BALL2]].dark;
							break;
						
						case PLAY: return(TRUE);
						case QUIT: return(FALSE);
					}
				}
			}
		}
		if (preset!=NOPRESETS) return(TRUE);
	}
}
/****************************/
char LoadIFF(img,screen,opt)
struct IFFGraphic *img;
struct Screen *screen;
char opt;
{
	char	err = 0,ChosenDepth;
	unsigned char *buffer;
	short i,j,column[5],store,store2,remain = 0;
	UWORD *colorpalette;
	long length,pos[5],fpos = 0;
	struct BitMap *BMap = &screen->BitMap;
	struct FileHandle *fh;
	/* `buffer' must be long aligned */
	if (!(buffer = (char *)AllocMem(4096,MEMF_PUBLIC))) return(5);	
	while(!(fh = (struct FileHandle *)Open(img->FileName,MODE_OLDFILE)));
	{
		MyRead(fh,buffer,30);
		if (strncmp(buffer,"FORM",4)) 		{ err = 2; goto Escape; }
		if (strncmp(&buffer[8],"ILBMBMHD",8))	{ err = 3; goto Escape; }

		/** File is an IFF graphic! **/
		
		img->Width  = *((short *)&buffer[20]);
		img->Height = *((short *)&buffer[22]);
		img->Depth  = buffer[28];
		
		/* Don't load more bitplanes on a screen than it can handle! */
		if (!img->ReqDepth || img->ReqDepth > BMap->Depth)
			img->ReqDepth = BMap->Depth;
		
		/* Locate the start of CMAP */
		
		Seek(fh,20+ *((long *)&buffer[16]),OFFSET_BEGINNING);
		MyRead(fh,buffer,8);
		if (strncmp(buffer,"CMAP",4)) { err = 4; goto Escape; }

		if (opt==VIEW) FadeScreen(screen,DOWN);
		
		/* Now read the RGB values */
		for (i=0; i < (1 << img->Depth); i++)
		{
			MyRead(fh,buffer,3);
			if (img->RGB) img->RGB[i] = (USHORT)
			((((long)buffer[0])<<4) | ((long)buffer[1]) | buffer[2]>>4);
		}
		if (!opt) goto Escape;	/* Program requested NOT to load pic */

		MyRead(fh,buffer,8);		/* DPPV? */
		/* Don't know what following data is for - skip to image data */
		for (i=0; i<10; i++)
		{
			Seek(fh,*((long *)&buffer[4]),OFFSET_CURRENT);
			MyRead(fh,buffer,8);
			if (!strncmp(buffer,"BODY",4)) goto FoundPicData;
		}
		err = 4; goto Escape;

FoundPicData:

		length = BMap->BytesPerRow * BMap->Rows;
		colorpalette = (UWORD *)screen->ViewPort.ColorMap->ColorTable;
		if (img->Depth < img->ReqDepth) ChosenDepth = img->Depth;
		else ChosenDepth = img->ReqDepth;
		pos[0]=pos[1]=pos[2]=pos[3]=pos[4]=0;
		column[0]=column[1]=column[2]=column[3]=column[4]=0;
		if (opt == 1)
		{
			for (i=0; i< 1 << ChosenDepth; i++)
				*(colorpalette+i) = img->RGB[i];
		}
		/***** Reading graphic image *****/
		while(1)
		{
			for (i=0; i<img->Depth; i++)
			{
LineNotComplete:	if (!remain) { remain = MyRead(fh,buffer,4096); fpos=0; }
				if (!remain) goto Escape2;		 /* End Of File */
				if (pos[i] >= length) goto Escape2; /* Filled screen */
				
	/* NB: `pos' is only incremented for depths which will be     */
	/* displayed. It is pointless and more lengthy to increment   */
	/* any elements of `pos' that will not be used in the display */
				
				if (buffer[fpos] < 128)
				{
					for (j=0,store = buffer[fpos++]; j<=store; j++,column[i]++,fpos++)
					{
						if (!(--remain))
						{ remain = MyRead(fh,buffer,4096); fpos=0; }	
						if (i < img->ReqDepth && column[i] < BMap->BytesPerRow)
						  *(BMap->Planes[i] + pos[i]++) = buffer[fpos];
					}
				}
				else
				{
					store = 256-buffer[fpos++];
					if (!(--remain))
					{ remain = MyRead(fh,buffer,4096); fpos=0; }
					for (j=0,store2 = buffer[fpos++]; j<=store; j++,column[i]++)
					{
						if (i < img->ReqDepth && column[i] < BMap->BytesPerRow)
						  *(BMap->Planes[i] + pos[i]++) = store2;
					}
				}
				--remain;		
				if (column[i] < img->Width>>3) goto LineNotComplete;
				column[i] = 0;
			}
		}
Escape2:
		/* Clear any portions of screen not loaded into */
		for (fpos=pos[i]; fpos<length; fpos++)
			for (i=0; i<img->ReqDepth; i++)
				*(BMap->Planes[i] + fpos) = 0;

		/* Clear the whole screen of surplus bitplanes */
		for (fpos=0; fpos<length; fpos++)
			for (i=img->Depth; i<img->ReqDepth; i++)
				*(BMap->Planes[i] + fpos) = 0;
Escape:
		Close(fh);
		if (opt==VIEW) FadeScreen(screen,UP);
	}
	/* else err = 1; */
	FreeMem(buffer,4096);
	return(err);
}
/**************************/
char ClickDelay(time)
short time;
{
	while (GetInput() != RETURN && !GetMouse() && --time) Delay(1);
	if (time) return(1);
	return(0);
}
/**************************/
void FadeScreen(scrn,flag)
struct Screen *scrn;
short flag;
{
	short j,red,grn,blu,i,val;
	USHORT col,*cp = s->ViewPort.ColorMap->ColorTable;
	
	for (i=0; i<=16; i++)
	{
		for (j=0; j<32; j++)
		{
			val = (flag==DOWN)? 16-i:i;
			col = rgb[j];
			red = (((col >> 8) & 0xf) * val) >> 4;
			grn = (((col >> 4) & 0xf) * val) >> 4;
			blu =  ((col 	    & 0xf) * val) >> 4;
			cp[j] = (red<<8) | (grn<<4) | blu;
		}
		MakeScreen(scrn); RethinkDisplay();
	}
}
