#include	<stdlib.h>
#include	<stdio.h>
#include	<string.h>
#include	<stdarg.h>
#include	<time.h>
#include	"gfxfunc.h"
#include	"picture.h"
#include	"keycodes.h"

int		FileRequestor(char *FileName, char *title);
void	SaveScreen(void);
void	RestoreScreen(void);
extern 	GXIMAGE		*ScreenSaveImage;

#define	PRINT_MESSAGES	0
#define	SNAPSHOTS		0

#define	MAX_BLIT	(16384/sizeof(GXBLITLIST))

GXPALETTE			MasterPalette[256];
char				CantUse[256];
GXBLITLIST			*BlitList=0,*BlitPtr;
int					NBlit=0;


int 	GBMain(int argc,char *argv[]);



int	main(int argc,char *argv[])
{ 	
	return GBMain(argc,argv);
}
void	GXReadMouse(GXMOUSEINFO	*info)
{
	ReadMouse(info);
}

void	GXSystemUpdate(void)
{
	SystemUpdate();
	if(GXCheckQuit()){
		GXQuitCode("Escape pressed\n");
	}
	#if	SNAPSHOTS
		if(GXKeyState(KEY_F1)){
			char 		LoadGame[256]="ScreenShot.lbm";
			GXPICTURE	*pic;
			GXOBLONG	screen;

			SaveScreen();
			if( FileRequestor(LoadGame, "Select screen shot") ){
				screen.X=screen.Y=0;
				screen.Width=GXImageWidth(ScreenSaveImage);
				screen.Height=GXImageHeight(ScreenSaveImage);
				pic=GXGetPicture(ScreenSaveImage, &screen);
				SavePicture(LoadGame, pic, PICTURE_LBM);
			}
			RestoreScreen();
		}
	#endif
}

void	GXInitiateHardware(void)
{
	memset(MasterPalette, 0, sizeof(MasterPalette) );
	memset(CantUse, 0, sizeof(CantUse));
	InitiateHardware();
	BlitList=(GXBLITLIST *)GXGetMem(MAX_BLIT*sizeof(GXBLITLIST), MEMF_ZERO|MEMF_ABORT);
	NBlit=0;
	BlitPtr=BlitList;
}

GXBLITLIST	*GXNextBlit(void)
{
	if(NBlit==MAX_BLIT){
		GXDoBlit();
		BlitPtr=BlitList;
		NBlit=0;
	}
	BlitPtr++;
	NBlit++;
	return	BlitPtr-1;
}
void		GXDoBlit(void)
{
	DrawBlitList(BlitList, NBlit);	
	NBlit=0;
	BlitPtr=BlitList;
}


int		GXGetPaletteRange(GXPALETTE *Pal, int FirstColor, int NumberColors)
{
	if( (FirstColor<0) || ((FirstColor+NumberColors)>256) ){
		return 0;
	}else{
		memcpy(Pal, MasterPalette+FirstColor, NumberColors*sizeof(GXPALETTE) );	
	}
}

void	GXSetPaletteRange(GXPALETTE *Pal, int FirstColor, int NumberColors)
{
	SetPaletteRange(Pal, FirstColor, NumberColors);
	memcpy(MasterPalette+FirstColor, Pal, NumberColors*sizeof(GXPALETTE) ); 
}
int		GXMapColor(GXPALETTE *pal)
{
	int			j,best,diff,Red,Green,Blue,total,jj;
	GXPALETTE	*from;
	char		*reserved;
	
	best=0;
	diff=256*3;
	Red=pal->Red;
	Blue=pal->Blue;
	Green=pal->Green;
	from=MasterPalette;
	reserved=CantUse;

	for(j=0; j<256; j++){
		if(! (*reserved++) ){
			total=0;
			jj=	 ((int)from->Red)-Red;
			if(jj<0){
				total-=jj;
			}else{
				total+=jj;
			}
			jj=	 ((int)from->Green)-Green;
			if(jj<0){
				total-=jj;
			}else{
				total+=jj;
				}
			jj=((int)from->Blue)-Blue;
			if(jj<0){
				total-=jj;
			}else{
				total+=jj;
			}
			if(total<diff){
				diff=total;
				best=j;
			}
		}
		from++;
	}
	GXPrintf("Mapped color (%d,%d,%d) to (%d,%d,%d)\n",
		(int)pal->Red,
		(int)pal->Green,
		(int)pal->Blue,
		(int)MasterPalette[best].Red,
		(int)MasterPalette[best].Green,
		(int)MasterPalette[best].Blue);
	return	best;
}

GXIMAGE	*GXMakeImage(int width, int height)
{
	return MakeImage(width, height);
}
void	GXKillImage(GXIMAGE *image)
{
	KillImage(image);
}

GXIMAGE	*GXPictureToImage(GXPICTURE *pic, long transparency, long TransparentColor)
{
	return	(GXIMAGE *)PictureToImage(pic, transparency, TransparentColor);
}
int	GXSetDrawBuffer(int n)
{
	return	1;
}
int	GXSetDisplayBuffer(int n)
{
	return	1;
}

GXIMAGE	*GXStartDrawing(int n)
{
	return	(GXIMAGE *)StartDrawing(n);
}
void	GXEndDrawing(void)
{
}


void	GXDrawBlitList(GXBLITLIST *b, int nblit)
{
	DrawBlitList(b, nblit);
}
long	GXReadJoystick(int n)
{
	return	ReadJoystick(n);
}
int		GXCheckQuit(void)
{
	return GXKeyDown(KEY_ESCAPE);
//	return	CheckQuit();
}
void	GXQuitCode(char *message, ...)
{
	GXPrintf(message);
	FreeHardware();
	exit(1);
}

long	GXImageWidth(GXIMAGE *image)
{
	return	ImageWidth(image);
}
long	GXImageHeight(GXIMAGE *image)
{
	return	ImageHeight(image);
}
void	GXReserveColors(int start, int number, int flags)
{
	ReserveColors(start, number, flags);
	memset( CantUse+start, flags, number*sizeof(char));
}

GXVOLUME	*GXMakeVolumeInfo(long *size)
{
	return	MakeVolumeInfo(size);
}
void	GXKillVolumeInfo(GXVOLUME *vol)
{
	KillVolumeInfo(vol);
}
int	GXMakeDirectoryInfo(char *name, GXFILEINFO **output)
{
	return	GetDirectoryInfo(name, output);
}
void	GXKillDirectoryInfo(GXFILEINFO *info)
{
	FreeDirectoryInfo(info);
}

typedef struct{
	void	*Next;
}MEMBLOCK;

MEMBLOCK	*list=0;

//***************************************************************************
//	Allocate system memory
//***************************************************************************
//	input:  amount wanted
//	output: Block allocate or zero
//*************************************************************************
void	*GXGetMem(size_t amount, long flags)
{
	MEMBLOCK	*data;
	
	data=malloc(amount+sizeof(MEMBLOCK));
	if(!data){
		if(flags&MEMF_ABORT){
			GXQuitCode("Ran out of memory\n");
		}else{
			return	0;
		}
	}
	if(flags&MEMF_ZERO){
		memset(data, 0, amount+sizeof(MEMBLOCK));
	}
	data->Next=list;
	list=data;
	return (data+1);
}
//***************************************************************************
//	Free system memory
//***************************************************************************
//	input:  Block to free
//	output: none
//*************************************************************************
void	GXFreeMem(void *data)
{
	MEMBLOCK	*block,*ptr,*last=0;
		
	ptr=(MEMBLOCK *)data;
	ptr--;
	block=list;
	while(block){
		if(block==ptr)
			break;
		last=block;
		block=(MEMBLOCK *)block->Next;
	}
	if(!block){
		GXPrintf("Non existant memory block\n");
		return;
	}else if(last){
		last->Next=block->Next;
	}else{
		list=block->Next;
	}
	free(block);
}
//***************************************************************************
//	Load a file into system memory
//***************************************************************************
//	input:  File name
//	output: Picture structure or zero
//***************************************************************************
void	*GXLoadFileToMemory(char *name, size_t *size)
{
	FILE	*f=0;
	void	*data=0;
	size_t	length;
	
	f=fopen(name,"r");
	if(f==NULL){
		goto fail;
	}
	if(fseek(f, 0, SEEK_END)){
		goto fail;
	}
	length=ftell(f);
	data=GXGetMem(length, 0);
	if(!data){
		goto fail;
	}
	if(fseek(f, 0, SEEK_SET)){
		goto fail;
	}
	if(length!=fread(data, 1, length, f)){
		goto fail;
	}
	fclose(f);
	*size=length;
	return	data;
fail:
	if(data){
		GXFreeMem(data);
	}
	if(f){
		fclose(f);
	}
	return	0;
}

//***************************************************************************
//	Save memory to disk
//***************************************************************************
//	input:  data, length of data, file name
//	output: did it work?
//***************************************************************************
int	GXSaveMemoryToFile(void *data, size_t LengthData, char *FileName)
{
	FILE	*handle;
	size_t	bytes;
	
	handle=fopen(FileName, "wb");
	if(handle==NULL){
		return 0;
	}
	bytes=fwrite(data, 1, LengthData, handle);
	fclose(handle);
	return (bytes==LengthData);
}

int	GXPrintf(const char *message, ...)
{
#if	PRINT_MESSAGES
	va_list	marker;

	va_start(marker, message);
	vprintf( message, marker);
#endif
}

GXSCREENMODEINFO	*GXGetScreenModeInfo(long *number)
{
	return	(GXSCREENMODEINFO *)GetScreenModeInfo(number);
}
void				GXKillScreenModeInfo(GXSCREENMODEINFO *info)
{
	KillScreenModeInfo(info);
}
long		GXSetScreenMode(long mode)
{
	return SetScreenMode(mode);
}
long	GXKeyDown(long key)
{
	return KeyState(key);
}
long	GXNextKey(void)
{
	return NextKey();
}
typedef struct{
	long	Width;		
	long	Height;				
	long	PlaneSeperation;		
	long	*XLineSeperation;	
	void	*BitPlanes;			
	long	LineSeperation;		
	long	Depth;			
	void	*Mask;				
	long	MaskLineSeperation;	
	long	*XMaskLineSeperation;	
}THEIMAGE;

GXPICTURE	*GXGetPicture(GXIMAGE *image, GXOBLONG *area)
{
	THEIMAGE	*im;
	GXPICTURE	*pic;
	UBYTE		*from,*to,*to2,*from2,*from3,pixel;
	int			ix,iy,iplane,offset;
	UWORD		*tow,data[8],mask,*fromw,jj;
	
	im=(THEIMAGE *)image;

	//	Setup picture structure
	pic=(GXPICTURE *)GXGetMem(sizeof(GXPICTURE), MEMF_ZERO);
	if(!pic)goto fail;
	pic->Width=area->Width;
	pic->Height=area->Height;
	pic->BytesAcross=area->Width;
	pic->Data=GXGetMem(area->Width*area->Height, MEMF_ZERO|MEMF_ABORT);
	pic->Palette=(GXPALETTE *)GXGetMem(256*sizeof(GXPALETTE), MEMF_ZERO);
	if(!pic->Palette)goto fail;
	pic->NPalette=256;
	GXGetPaletteRange(pic->Palette, 0, 256);
	
	//	Create byte map data
	to=(UBYTE *)pic->Data;
	from=((UBYTE *)im->BitPlanes)	+	area->Y*im->LineSeperation +
			(area->X/8);
	for(iy=0; iy<area->Height; iy++){
		to2=to;
		from2=from;
		offset = area->X & 0x07;
		mask = (1 << (7-offset) );
		for(ix=0; ix<area->Width; ix++){
			jj=0;
			pixel=0x01;
			from3=from2;
			for(iplane=0; iplane<8; iplane++){
				if( (*from3)&mask ){
					jj|=pixel;
				}
				from3+=im->PlaneSeperation;
				pixel*=2;
			}	
			*to2++=jj;
			mask>>=1;
			if(!mask){
				mask=0x80;
				from2++;
			}
		}
		to+=pic->BytesAcross;
		from+=im->LineSeperation;
	}
	return pic;
	fail:
		GXQuitCode("Faliure in GXGetPicture()\n");
		return 0;
}
char	*GXGetLastErrorMessage(void)
{
	return GetLastErrorMessage();
}
long	GXTicksPerSecond(void)
{
	return 50;
}
long	GXTimer(void)
{
	return clock();
}



