// great game(boy)
// (c) 2004 John Klima 
// implements the gba3ds API by torlus
// free non-commercial use
// for limited help with this code contact gameboy@cityarts.com
   
// base  includes
#include "gba.h"
#include "dispcnt.h"
#include "keypad.h"
#include "dma.h"
#include "print.h"
#include "timers.h"
 
//for the text
#include <string.h>
 
#define SEPCHAR " "
  
// some useful functions
#include "util.h"
#include "gbautil.h"
 
// engine-related declarations
#include "engine.h"
 
// model definitions (half-life SMD format)
#include "afghan.h"
#include "tomahawk.h"
#include "b52.h"
#include "f14.h"

// animations definitions
#include "anim_afghan.h"
#include "anim_tomahawk.h"
#include "anim_b52.h"
#include "anim_f14.h"

// textures definitions
extern const u16 afghan_raw[];
u16 EWRAM *texture_afghan[] = { afghan_raw }; //the map

extern const u16 un_texture[];
u16 EWRAM *texture_un[] = { un_texture}; //default blue

extern const u16 b52_texture[];
u16 EWRAM *texture_b52[] = { b52_texture}; //specific b52 texture

extern const u16 f14_texture[];
u16 EWRAM *texture_f14[] = { f14_texture}; //specific f14 texture


// vertex definitions
vertex EWRAM afghan_Vertex[472];
triangle *afghan_Triangle = (triangle *) afghan_triangle; //the terrain geometry

vertex EWRAM tomahawk_Vertex[79];
triangle *tomahawk_Triangle = (triangle *) tomahawk_triangle;	//cruise missle

vertex EWRAM b52_Vertex[167];
triangle *b52_Triangle = (triangle *) b52_triangle;					//b52

vertex EWRAM f14_Vertex[85];
triangle *f14_Triangle = (triangle *) f14_triangle;					//f14


#define MULTIBOOT volatile const u8 __gba_multiboot;
//MULTIBOOT

//background images
extern const u16 title_back[];

// angles and position of the character/object
s32 A_afghan[3];
s32 T_afghan[3];

s32 A_tomahawk[3];
s32 T_tomahawk[3];

s32 A_b52[3];
s32 T_b52[3];

s32 A_f14[3];
s32 T_f14[3];


// camera position
s32 camT[3];
s32 camA[3];

// camera matrix
s32 cam[16];

// some vars for animation
u32 afghan_frame=0;


// fps counter
u32 totframes = 0;
u32 lastsec = 0;
u32 fps=10; //non zero start


u32 data_ptr_start = 0;				//current "seek" offset into data
u32 data_ptr_end = 0;
u32 datetick = 0;
u32 last_datetick = 100;	//greater than biggest data date index to start
u32 secondtick = 0;


// data munition constants (not all geometry has been implemented in this release)
#define M_b52				1;  
#define	M_b2b				2;
#define M_f_14			3;
#define M_b1				4;
#define M_tomahawk	5;
#define M_tali_g		6;
#define M_un_g			7;
#define M_c130			8;
#define M_hum				9;
#define M_leaf			10;


//total count of munition type for current date
u8 ct_b52				=	0;
u8 ct_b2b				=	0;
u8 ct_f_14			=	0;
u8 ct_b1				=	0;
u8 ct_tomahawk	=	0;
u8 ct_tali_g		=	0;
u8 ct_un_g			=	0;
u8 ct_c130			=	0;
u8 ct_hum				=	0;
u8 ct_leaf			=	0;


//for multi-line text parsing
typedef struct {
	char line[128];
}STRING;

// the date text for given data
extern const STRING datetext[];

// the munitions position data
extern const s16 munition[];

////////////////////////////////////////////////////////////////////////////////////////////////////
//general functions
void GetInput(void);
void Init(void);
void Render(void);

//model functions
void DoAfghan(s32 * animation, s8  framecount);
void DoTomahawk(s32 * animation, s8  framecount);
void DoB52(s32 * animation, s8  framecount);
void Dof14(s32 * animation, s8  framecount);

void getNextDataDate(void);

//sound functions
void InterruptProcess(void) __attribute__ ((section(".iwram"))); //the interrupt handle from crt0.s
void DmaPlaySound (void);

//DEBUG FUNCTIONS
s16 debug_out[32];
void DrawDebug(void);
void DrawText(s16 index, STRING *text);


////////////////////////////////////////////////////////////////////////////////////////////////////
//	AGB MAIN
///////////////////////////////////////////////////////////////
void AgbMain(void)
{	

  // go into mode 3
  SetMode(MODE_3 | BG2_ENABLE);

  // initialization stuff
  Init();	
	
  // start timer for FPS counter
  REG_TM2CNT = TIME_FREQUENCY_256 | TIME_ENABLE;
  REG_TM3CNT = TIME_OVERFLOW | TIME_ENABLE;

	while(1)
	{	
  
		//framerate
		if (REG_TM3D != lastsec)
		{
			fps = totframes;
			totframes=0;
			lastsec=REG_TM3D;
			//every 15 seconds, click the day ahead
			secondtick++;
			if (secondtick>10)
			{
				secondtick=0;
				datetick++;
				if (datetick > 34)
				{
					datetick = 0;
					last_datetick = 100;
					data_ptr_start = 0;				//current "seek" offset into data
					data_ptr_end = 0;
				}

			}
		}
		
		totframes++;

		//iterate next in list
		if (datetick != last_datetick)
		{
			getNextDataDate();
			last_datetick = datetick;
		}
		GetInput();						// process keyboard input
		Render();							//render scene					

	}
}


//HANDY DEBUG
void DrawDebug()
{
	int i = 0;
  for(i = 0; i < 8;i++)
	{
		printnum(5,8*i,debug_out[i]);
	}
}


////////////////////////////////////////////////////////////////////
//GAMEBOY INPUT
///////////////////////////////////////////////////////////////////
void GetInput(void)
{

	int WALK_STEP = 15;	
	
	if(!(*KEYS & KEY_UP )) {
    camT[2] -= int2fp(WALK_STEP) ;
	}
  if(!(*KEYS & KEY_DOWN)) {
    camT[2] += int2fp(WALK_STEP) ;
	}

  if(!(*KEYS & KEY_LEFT)) {
			camT[0] += int2fp(WALK_STEP);
	}
  if(!(*KEYS & KEY_RIGHT)) {
    	camT[0] -= int2fp(WALK_STEP);
	}

	if(!(*KEYS & KEY_A)) {
		
		camA[0] += 1  ;		
		if (camA[0]>255) camA[0]=1;

	}

	if(!(*KEYS & KEY_B)) {

		camA[0] -= 1  ;
		if (camA[0]<=0) camA[0]=255;		

	}

	if(!(*KEYS & KEY_R)) {

		//camA[2] -= 1;		
		//if (camA[2]<=0) camA[2]=255;  
    camT[1] -= int2fp(WALK_STEP) ;

		
	}

	if(!(*KEYS & KEY_L)) { 
		
		//camA[2] += 1;		
		//if (camA[2]>255) camA[2]=1;
		camT[1] += int2fp(WALK_STEP) ;
	}

	

}

void Init(void) {

  s32 i;
	  
	// camera initialization
  camT[0]=int2fp(0);
  camT[1]=int2fp(250);
  camT[2]=int2fp(0);

	camA[0] = 32 ;

	// set up the camera matrix
  matrix_loadIdentity(cam);
  matrix_setTranslation(cam, camT);
	matrix_setRotation(cam, camA);

  // scene/character angles and position
  A_afghan[0] = 0; A_afghan[1] = 0; A_afghan[2] = 0;
  T_afghan[0] = 0; T_afghan[1] = 0; T_afghan[2] = 0;

  A_tomahawk[0] = 0; A_tomahawk[1] = 0; A_tomahawk[2] = 0;
  T_tomahawk[0] = 0; T_tomahawk[1] = 0; T_tomahawk[2] = int2fp(20);
	
	A_b52[0] = 0; A_b52[1] = 0; A_b52[2] = 0;
  T_b52[0] = int2fp(20); T_b52[1] = 0; T_b52[2] = int2fp(20);


  A_f14[0] = 0; A_f14[1] = 0; A_f14[2] = 0;
  T_f14[0] = int2fp(20); T_f14[1] = 0; T_f14[2] = int2fp(20);


	//initialize object vertices
	for(i=0; i< afghan_VERTEXCOUNT;i++)
	{
		afghan_Vertex[i].bone = afghan_vertex[ 6*i + 0 ];

		afghan_Vertex[i].rel[0] = afghan_vertex[ 6*i + 1 ];
		afghan_Vertex[i].rel[1] = afghan_vertex[ 6*i + 2 ];
		afghan_Vertex[i].rel[2] = afghan_vertex[ 6*i + 3 ];

		afghan_Vertex[i].u = afghan_vertex[ 6*i + 4 ];
		afghan_Vertex[i].v = afghan_vertex[ 6*i + 5 ];
	}

	for(i=0; i< tomahawk_VERTEXCOUNT;i++)
	{
		tomahawk_Vertex[i].bone = tomahawk_vertex[ 6*i + 0 ];

		tomahawk_Vertex[i].rel[0] = tomahawk_vertex[ 6*i + 1 ];
		tomahawk_Vertex[i].rel[1] = tomahawk_vertex[ 6*i + 2 ];
		tomahawk_Vertex[i].rel[2] = tomahawk_vertex[ 6*i + 3 ];

		tomahawk_Vertex[i].u = tomahawk_vertex[ 6*i + 4 ];
		tomahawk_Vertex[i].v = tomahawk_vertex[ 6*i + 5 ];
	}

	for(i=0; i< b52_VERTEXCOUNT;i++)
	{
		b52_Vertex[i].bone = b52_vertex[ 6*i + 0 ];

		b52_Vertex[i].rel[0] = b52_vertex[ 6*i + 1 ];
		b52_Vertex[i].rel[1] = b52_vertex[ 6*i + 2 ];
		b52_Vertex[i].rel[2] = b52_vertex[ 6*i + 3 ];

		b52_Vertex[i].u = b52_vertex[ 6*i + 4 ];
		b52_Vertex[i].v = b52_vertex[ 6*i + 5 ];
	}

	for(i=0; i< f14_VERTEXCOUNT;i++)
	{
		f14_Vertex[i].bone = f14_vertex[ 6*i + 0 ];

		f14_Vertex[i].rel[0] = f14_vertex[ 6*i + 1 ];
		f14_Vertex[i].rel[1] = f14_vertex[ 6*i + 2 ];
		f14_Vertex[i].rel[2] = f14_vertex[ 6*i + 3 ];

		f14_Vertex[i].u = f14_vertex[ 6*i + 4 ];
		f14_Vertex[i].v = f14_vertex[ 6*i + 5 ];
	}


}


////////////////////////////////////////////////////////////////////
//moving scene rendering functions
////////////////////////////////////////////////////////////////////

void Render(void)
{

	  // loads the background image into our screen buffer
  DMACopy(title_back, ScreenBuffer, (WIDTH*HEIGHT)>>1, DMA_ENABLE | DMA_TIMEING_IMMEDIATE | DMA_32 );
  // initialize the Z-Buffer (240*160*2) bytes
  Fill(ZBuffer,500,8);


	//position camera
  matrix_loadIdentity(cam);
	matrix_setRotation(cam, camA);
  matrix_setTranslation(cam, camT);


	DoAfghan(anim_afghan_animation, anim_afghan_FRAMECOUNT);	
	Draw(afghan_Triangle, afghan_Vertex, texture_afghan, afghan_TRIANGLECOUNT );
	
	
	//rip through data and draw what needs drawing
	u32 index =0;
	s16 date = 0;
	s16 type = 0;
	s16 x = 0;
	s16 y = 0;
	s16 z = 0;
 
	for (index = data_ptr_start; index<data_ptr_end;index++)
	{
		date	= munition[(index*5) +0]; //data width 5
		type	= munition[(index*5) +1];
		x			= munition[(index*5) +2];
		y			= munition[(index*5) +3];
		z			= munition[(index*5) +4];
		

		switch (type)
		{
		case 1:	//M_b52:
				T_b52[0]=int2fp(x);
				T_b52[1]=int2fp(z);
				T_b52[2]=int2fp(y);
				DoB52(anim_b52_animation, anim_b52_FRAMECOUNT);	
				Draw(b52_Triangle, b52_Vertex, texture_b52, b52_TRIANGLECOUNT );
				break;

		case 3:	//M_f14:
				T_f14[0]=int2fp(x);
				T_f14[1]=int2fp(z);
				T_f14[2]=int2fp(y);
				Dof14(anim_f14_animation, anim_f14_FRAMECOUNT);	
				Draw(f14_Triangle, f14_Vertex, texture_f14, f14_TRIANGLECOUNT );
				break;


		case 5:	//M_tomahawk:
				T_tomahawk[0]=int2fp(x);
				T_tomahawk[1]=int2fp(z);
				T_tomahawk[2]=int2fp(y);
				DoTomahawk(anim_tomahawk_animation, anim_tomahawk_FRAMECOUNT);	
				Draw(tomahawk_Triangle, tomahawk_Vertex, texture_un, tomahawk_TRIANGLECOUNT );
				break;				
		}


	}
	
	//debug_out[0] = data_ptr_start;
	//debug_out[1] = data_ptr_end;
	//DrawDebug();

	DrawText(datetick,datetext);
  // copy our screen buffer into VRAM
  DMACopy( ScreenBuffer, VideoBuffer, (WIDTH*HEIGHT)>>1, DMA_ENABLE | DMA_TIMEING_IMMEDIATE | DMA_32 );

}

//////////////////////////////////////////////////////////////
//model drawing/animations
//////////////////////////////////////////////////////////////

void DoAfghan(s32 * animation, s8  framecount)
{
	 s32 org[16];
	 s32 *afghan_first_joint ;
	
	// transform vertices according to current skeleton  	
  afghan_first_joint = animation + ((afghan_JOINTCOUNT * afghan_frame)<<4);  
	afghan_frame++;

	debug_out[0] = afghan_frame;

  if (afghan_frame == framecount ) afghan_frame=0;	

	// transform vertices according to current skeleton  
	Animate( afghan_Vertex, afghan_VERTEXCOUNT, afghan_first_joint );

  // moves the character in order to set its first joint (character hostspot) to (0,0,0)  
  matrix_loadIdentity(org);
  matrix_setInverseTranslation(org, afghan_first_joint+12);
  TransformVertices( afghan_Vertex, afghan_VERTEXCOUNT, org );

	
	//rotate-translate objects
	matrix_loadIdentity(org);
  matrix_setRotation(org, A_afghan);
  matrix_setTranslation(org, T_afghan);
  TransformVertices( afghan_Vertex, afghan_VERTEXCOUNT, org );

	//apply camera position
  TransformVertices( afghan_Vertex, afghan_VERTEXCOUNT, cam );	
	Perspective( afghan_Vertex, afghan_VERTEXCOUNT );  	

}


void DoTomahawk(s32 * animation, s8  framecount)
{
	 s32 org[16];
	 s32 *tomahawk_first_joint ;
	 
	 s32 frame = 0;

	// transform vertices according to current skeleton  	
  tomahawk_first_joint = animation + ((tomahawk_JOINTCOUNT * frame)<<4);  	
	
	// transform vertices according to current skeleton  
	Animate( tomahawk_Vertex, tomahawk_VERTEXCOUNT, tomahawk_first_joint );

  // moves the character in order to set its first joint (character hostspot) to (0,0,0)  
  matrix_loadIdentity(org);
  matrix_setInverseTranslation(org, tomahawk_first_joint+12);
  TransformVertices( tomahawk_Vertex, tomahawk_VERTEXCOUNT, org );
	
	//rotate-translate objects
	matrix_loadIdentity(org);
  matrix_setRotation(org, A_tomahawk);
  matrix_setTranslation(org, T_tomahawk);
  TransformVertices( tomahawk_Vertex, tomahawk_VERTEXCOUNT, org );

	//apply camera position
  TransformVertices( tomahawk_Vertex, tomahawk_VERTEXCOUNT, cam );	
	Perspective( tomahawk_Vertex, tomahawk_VERTEXCOUNT );  	

}

void Dof14(s32 * animation, s8  framecount)
{
	 s32 org[16];
	 s32 *f14_first_joint ;
	 
	 s32 frame = 0;

	// transform vertices according to current skeleton  	
  f14_first_joint = animation + ((f14_JOINTCOUNT * frame)<<4);  	
	
	// transform vertices according to current skeleton  
	Animate( f14_Vertex, f14_VERTEXCOUNT, f14_first_joint );

  // moves the character in order to set its first joint (character hostspot) to (0,0,0)  
  matrix_loadIdentity(org);
  matrix_setInverseTranslation(org, f14_first_joint+12);
  TransformVertices( f14_Vertex, f14_VERTEXCOUNT, org );
	
	//rotate-translate objects
	matrix_loadIdentity(org);
  matrix_setRotation(org, A_f14);
  matrix_setTranslation(org, T_f14);
  TransformVertices( f14_Vertex, f14_VERTEXCOUNT, org );

  //apply camera position
	TransformVertices( f14_Vertex, f14_VERTEXCOUNT, cam );	
	Perspective( f14_Vertex, f14_VERTEXCOUNT );  	

}



void DoB52(s32 * animation, s8  framecount)
{
	 s32 org[16];
	 s32 *b52_first_joint ;
	 s32 frame = 0;
	
	// transform vertices according to current skeleton  	
  b52_first_joint = animation + ((b52_JOINTCOUNT * frame)<<4);  
	
	// transform vertices according to current skeleton  
	Animate( b52_Vertex, b52_VERTEXCOUNT, b52_first_joint );

  // moves the character in order to set its first joint (character hostspot) to (0,0,0)  
  matrix_loadIdentity(org);
  matrix_setInverseTranslation(org, b52_first_joint+12);
  TransformVertices( b52_Vertex, b52_VERTEXCOUNT, org );

	
	//rotate-translate objects
	matrix_loadIdentity(org);
  matrix_setRotation(org, A_b52);
  matrix_setTranslation(org, T_b52);
  TransformVertices( b52_Vertex, b52_VERTEXCOUNT, org );

  //apply camera position
  TransformVertices( b52_Vertex, b52_VERTEXCOUNT, cam );	
	Perspective( b52_Vertex, b52_VERTEXCOUNT );  	

}

  
 
///////////////////////////////////////////////////////////////
//SOUND 
///////////////////////////////////////////////////////////////
void InterruptProcess(void)
{
	//Sample finished! Stop DMA mode Direct sound
		REG_TM0CNT_H=0;	//disable timer 0
		REG_DMA1CNT_H=0; //stop DMA
		
	//clear the interrupt(s)
	REG_IF |= REG_IF;
}

void DmaPlaySound (void)
{
	/*

	//Play a mono sound at 11khz in DMA mode Direct Sound
	//uses timer 0 as sampling rate source
	//uses timer 1 to count the samples played in order to stop the sound 
	REG_SOUNDCNT_L=0;
	REG_SOUNDCNT_H=0x0b0F;  //DS A&B + fifo reset + timer0 + max volume to L and R
	REG_SOUNDCNT_X=0x0080;  //turn sound chip on
	
	REG_DMA1SAD=(unsigned long) _trackone; //  _binary_lo1234_pcm_start;	//dma1 source
	REG_DMA1DAD=0x040000a0; //write to FIFO A address
	REG_DMA1CNT_H=0xb600;	//dma control: DMA enabled+ start on FIFO+32bit+repeat
	
	REG_TM1CNT_L= 0xA9DD; //0x7098;	//0xffff-the number of samples to play
	REG_TM1CNT_H=0xC4;		//enable timer1 + irq and cascade from timer 0

	REG_IE=0x10;	  	//enable irq for timer 1 overflow
	REG_IME=1;				//enable interrupt
	
	//Formula for playback frequency is: 0xFFFF-round(cpuFreq/playbackFreq)
	//REG_TM0CNT_L=0xFBE8;	//16khz playback freq
	REG_TM0CNT_L=0xFA10;		//11.025 khz
	REG_TM0CNT_H=0x0080; 	//enable timer at CPU freq 

	done = 0;
	*/
} 	 
void DrawText(s16 index, STRING *text)
{
	
	//pull index line from STRING array and print it out in 1-3 lines

	char line1[32];
	char line2[32];
  char line3[32];
	char temp[128];
		
	char *word=NULL;
  	
	
	line1[0]='\0';
	line2[0]='\0';
  line3[0]='\0';
	temp[0]='\0';

	strcpy(&temp,&text[index]);

	word=strtok(&temp,SEPCHAR);
	while (word != NULL)
	{
		strcat(&line1," ");
		strcat(&line1,word);
		word=strtok(NULL,SEPCHAR);		
		if (strlen(&line1)>20) goto line_2 ;
	}

line_2:
	
	while (word != NULL)
	{
		strcat(&line2," ");
		strcat(&line2,word);
		word=strtok(NULL,SEPCHAR);		
		if (strlen(&line2)>20) goto line_3 ;
	}
line_3:
	
	while (word != NULL)
	{
		strcat(&line3," ");
		strcat(&line3,word);		
		word=strtok(NULL,SEPCHAR);
	}

	prints(5,130, line1);
	prints(5,140, line2);
	prints(5,150, line3);
}

void getNextDataDate(void)
{
	//uses global datetick, and data_ptr_start and data_ptr_end
	//to scan forward till next new date. 
	s32 lookat_date = 100;
	while(lookat_date != datetick)
	{
		lookat_date = munition[data_ptr_start * 5];
		data_ptr_start++;
	}
	data_ptr_start--; //back up one

	lookat_date = datetick;
	data_ptr_end = data_ptr_start;
	while(lookat_date == datetick)
	{
		lookat_date = munition[data_ptr_end * 5];
		data_ptr_end++;
	}
	data_ptr_end--; //back up one

}