#include	<stdlib.h>
#include    <string.h>
#include    <stdio.h>
#include    <time.h>
#include	"GB.h"
#include	"system.h"
#include	"method1.h"
#include	"method2.h"
#include	"gfxfunc.h"
#include	"options.h"
#include	"keycodes.h"
#include	"prefs.h"
#include	"picture.h"

#define	FRAME_RATE	0

void	PrintFrameRate(void);
int 	InitMachine_null(void);
void	TrashMachine_null(void);
void	RefreshScreen_null(void);
void	RefreshLine_null(byte Y);
void	RefreshSprites_null(void);
byte	SourceColor_null(int n);
void	Sound_null(byte R,byte V);
byte	SIOSend_null(byte V);
byte	SIOReceive_null(byte *V);

typedef	struct{
	int ((*InitMachine)(void));
	void ((*TrashMachine)(void));
	void ((*RefreshScreen)(void));
	void ((*RefreshLine)(byte));
	void ((*RefreshSprites)(void));
	byte ((*SourceColor)(int));
	void ((*Sound)(byte R,byte V));
	byte ((*SIOSend)(byte V));
	byte ((*SIOReceive)(byte *V));
}GFX_FUNCTIONS;

GFX_FUNCTIONS	Gfx[]={
					{InitMachine_null,TrashMachine_null,RefreshScreen_null,RefreshLine_null,
					RefreshSprites_null,SourceColor_null,Sound_null,
					SIOSend_null,SIOReceive_null},

					{InitMachine1,TrashMachine1,RefreshScreen1,RefreshLine1,
					RefreshSprites1,SourceColor1,Sound1,
					SIOSend1,SIOReceive1},
					
					{InitMachine2,TrashMachine2,RefreshScreen2,RefreshLine2,
					RefreshSprites2,SourceColor2,Sound2,
					SIOSend2,SIOReceive2},
				};

GFX_FUNCTIONS	func;
int	SetupMachine=0;


void	SetMethod(int n)
{
	func=Gfx[n];
}
byte	SourceColor(int n)
{
	return func.SourceColor(n);
}	

/****************************************************************/
/*** Allocate resources needed by the machine-dependent code. ***/
/************************************** TO BE WRITTEN BY USER ***/
int InitMachine(void)
{
	if(SetupMachine){
		return 1;
	}else{
		if( func.InitMachine() ){
			SetupMachine=1;
			return 1;
		}else{
			return 0;
		}
	}
}
 
/****************************************************************/
/*** Deallocate all resources taken by InitMachine().         ***/
/************************************** TO BE WRITTEN BY USER ***/
void TrashMachine(void)
{
	if(!SetupMachine){
		return ;
	}else{
		SetupMachine=0;
		func.TrashMachine();
	}
}

/****************************************************************/
/*** Refresh screen.                                          ***/
/************************************** TO BE WRITTEN BY USER ***/
void RefreshScreen(void)
{
#if	FRAME_RATE
	PrintFrameRate();
#endif
	func.RefreshScreen();
	IngameUpdate();
	if(GXGetLastErrorMessage()){
		GXPrintf(GXGetLastErrorMessage());
		GXPrintf("\n");
		GXQuitCode("Code terminated due to error\n");
	}
	
	//	Put in pause to slow game down
	if(SlowDown){
		long	StartTime,interval,xx;
		
		StartTime=GXTimer();
		interval=SlowDown*GXTicksPerSecond()/50;
		do{
			xx=GXTimer()-StartTime;
			if( (xx<0) || (xx>interval) )break;				
		}while(1);
	}
}

//***************************************************
//*													*
//*	Print frame rate in frames/sec					*
//*													*
//***************************************************
void	PrintFrameRate(void)
{
#define NTEST 200
	static 	long 	LastTime=0;
	long			interval,jj;
	float			FramesSec;
	static	long 	frame=0;
	
    frame++;
    if(!(frame%NTEST)){
        jj=GXTimer();
        interval=jj-LastTime;
        if(interval&LastTime){
            FramesSec=((float)NTEST*GXTicksPerSecond())/((float)interval);
            GXPrintf("Frames per sec = %f\n",FramesSec);
            GXPrintf("interval = %d\n",(int)interval);
        }
        LastTime=jj;
        GXPrintf("F:%d\n",frame);
    }
}

/****************************************************************/
/*** Refresh line.                                            ***/
/************************************** TO BE WRITTEN BY USER ***/
void RefreshLine(byte Y)
{
	func.RefreshLine(Y);
}
/****************************************************************/
/*** Refresh sprites.                                         ***/
/************************************** TO BE WRITTEN BY USER ***/
void RefreshSprites(void)
{
	func.RefreshSprites();
}


/****************************************************************/
/*** Get joystick state: START.SELECT.B.A.D.U.L.R.            ***/
/************************************** TO BE WRITTEN BY USER ***/
byte Joystick(void)
{
	long		input;
	byte		joy,bits;
	int			j;
	long		*data;

	input=GXReadJoystick(1);
	joy=0;
	if(input&PAD_UP)joy|=0x08;
	if(input&PAD_DOWN)joy|=0x04;
	if(input&PAD_LEFT)joy|=0x02;
	if(input&PAD_RIGHT)joy|=0x01;
	if(input&PAD_BUTTON1)joy|=0x10;
	if(input&PAD_BUTTON2)joy|=0x20;
	if(input&PAD_BUTTON3)joy|=0x40;
	if(input&PAD_BUTTON4)joy|=0x80;
	
	//	Merge with keyboard input
	data=KeyPrefs;
	bits=0x01;
	for(j=NKeyPrefs; j; j--){
		if(GXKeyDown(*data++))
			joy|=bits;
		bits<<=1;
	}
	joy=(joy^0xff);	//flip the bits
	return joy;
}

/****************************************************************/
/*** Write value into sound chip register (Reg #0 at FF10h).  ***/
/************************************** TO BE WRITTEN BY USER ***/
void Sound(byte R,byte V){
	func.Sound(R,V);	
}

/****************************************************************/
/*** Send a byte onto the serial line. Returns 1 on success,  ***/
/*** 0 otherwise.                                             ***/
/************************************** TO BE WRITTEN BY USER ***/
byte SIOSend(byte V)
{
	return func.SIOSend(V);
}

/****************************************************************/
/*** Receive a byte from the serial line. Returns 1 on        ***/
/*** success, 0 otherwise.                                    ***/
/************************************** TO BE WRITTEN BY USER ***/
byte SIOReceive(byte *V)
{
	return func.SIOReceive(V);
}

int InitMachine_null(void){return 1;}
void TrashMachine_null(void){}
void RefreshScreen_null(void){}
void RefreshLine_null(byte Y){}
void RefreshSprites_null(void){}
byte SourceColor_null(int n){return n;}
void Sound_null(byte R,byte V){}
byte SIOSend_null(byte V){return 0;}
byte SIOReceive_null(byte *V){return 0;}

