
#include <xtl.h>

void handle_error( const char* );

#include "Nsf_Emu.h"
#include "Gbs_Emu.h"
#include "Spc_Emu.h"
#include "Vgm_Emu.h"
#include "Gym_Emu.h"
//#include "Gzip_File.h"
//#include "Wave_Writer.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>



#include "gme.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

extern EnableVideoAudio;
extern bool getPlaying();
extern int vgmusic;
int playingMusic = 0; 
int currentSong = 0;
const char* filename = "D:\\test.spc";
Music_Emu* emu;

track_info_t info;

//Used to keep track of the Play and write cursor DirectSound uses.
DWORD vgCurrentPos = 0;
DWORD vgWritePos = 0;

int vgLastUsedPos; //Pointer to where the last byte was copied into the stream
//The number of bytes between vgLastUsedPos and vgCurrentPos. This is where data is written to. 
int vgDataNeeded = 0;    

extern LPDIRECTSOUND8  m_pDSound;
extern int musicType;
LPDIRECTSOUNDBUFFER     vgPrimaryBuffer = NULL;
DSBUFFERDESC vgdsbd;

LPVOID vglpvPtr1;
DWORD vgdwBytes1;
LPVOID vglpvPtr2;
DWORD vgdwBytes2;
int vgSoundBufferSize = 0;
WAVEFORMATEX vgwfx;
extern int inGame;
extern "C" int dprintf(char *format, ...);
// Create appropriate emulator and load file
const char* load_music_file( const char* path, long sample_rate, Music_Emu** emu_out )
{
	//dprintf( "VG load_music_file\n");
	// extract file extension
	const int ext_len = 4;
	char ext [ext_len];
	ext [0] = 0;
	const char* p = strrchr( (char*) path, '.' );
	if ( p && strlen( p ) == ext_len )
	{
		for ( int i = 0; i < ext_len; i++ )
			ext [i] = toupper( p [i + 1] );
	}

	//Gzip_File_Reader in;
	//handle_error( in.open( "test.nsf.gz" ) );
	// create emulator based on extension
	int vgz = 0;
	Music_Emu* emu = NULL;
	emu = NULL;
	if ( !strcmp( ext, "NSF" ) )
		emu = new Nsf_Emu;
	else if ( !strcmp( ext, "GBS" ) )
		emu = new Gbs_Emu;
	else if ( !strcmp( ext, "SPC" ) )
		emu = new Spc_Emu;
	else if ( !strcmp( ext, "VGM" ) )
		emu = new Vgm_Emu;
	else if ( !strcmp( ext, "GYM" ) )
		emu = new Gym_Emu;
	else if ( !strcmp( ext, "VGZ" ) ) {
		vgz = 1;
		emu = new Vgm_Emu;
	}
	else
		return NULL;

	if ( !emu )
		return "Out of memory";


	// setup and load file
	const char* error = emu->set_sample_rate( sample_rate );
	if ( !error ) {
		if(vgz == 0) {
			error = emu->load_file( path );
		}
		else {
	    	Gzip_File_Reader in;
	      handle_error( in.open( path ) );
			emu->load( in );
		}
	}

	*emu_out = emu;
	
	int track = 0;
	track_info_t info;
	handle_error( gme_track_info( emu, &info, track ) );
	/*dprintf( "System   : %s\n", info.system );
	dprintf( "Game     : %s\n", info.game );
	dprintf( "Author   : %s\n", info.author );
	dprintf( "Copyright: %s\n", info.copyright );
	dprintf( "Comment  : %s\n", info.comment );
	dprintf( "Dumper   : %s\n", info.dumper );
	dprintf( "Tracks   : %d\n", (int) info.track_count );
	dprintf( "\n" );
	dprintf( "Track    : %d\n", (int) track + 1 );
	dprintf( "Name     : %s\n", info.song );
	dprintf( "Length   : %ld:%02ld",
		(long) info.length / 1000 / 60, (long) info.length / 1000 % 60 );
	if ( info.loop_length != 0 )
		dprintf( " (endless)" );*/
	//dprintf( "\n\n" );

	return error;
}

int playing = 0;
void resetVGStream() {
	//dprintf( "VG Reset VG Stream\n");

	if(vgPrimaryBuffer) {
		vgPrimaryBuffer->Stop();
		vgPrimaryBuffer->Release();
		vgPrimaryBuffer = NULL;
	}
	//Wave format of the stream.
	vgwfx.wFormatTag = WAVE_FORMAT_PCM;
	vgwfx.nSamplesPerSec =44100;
	vgSoundBufferSize=(44100*4);
	vgwfx.nChannels = 2;
	vgwfx.nBlockAlign = 4;
	vgwfx.wBitsPerSample = 16;
	vgwfx.nAvgBytesPerSec = vgwfx.nSamplesPerSec * vgwfx.nBlockAlign;
	vgwfx.cbSize=0;

	memset(&vgdsbd, 0, sizeof(DSBUFFERDESC));
	vgdsbd.dwSize = sizeof(DSBUFFERDESC);
	vgdsbd.dwBufferBytes = vgSoundBufferSize;
	vgdsbd.lpwfxFormat = &vgwfx;

	if(m_pDSound == NULL) {  return ; }

	m_pDSound->CreateSoundBuffer(&vgdsbd, &vgPrimaryBuffer, NULL);

			DSMIXBINVOLUMEPAIR dsmbvp[8] = {
			{DSMIXBIN_FRONT_LEFT, DSBVOLUME_MAX},   // left channel
			{DSMIXBIN_FRONT_RIGHT, DSBVOLUME_MAX},  // right channel
			{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // left channel
			{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // right channel
			{DSMIXBIN_BACK_LEFT, DSBVOLUME_MAX},    // left channel
			{DSMIXBIN_BACK_RIGHT, DSBVOLUME_MAX},   // right channel
			{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX},    // left channel
			{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX}};   // right channel
			DSMIXBINS dsmb;
			dsmb.dwMixBinCount = 8;
			dsmb.lpMixBinVolumePairs = dsmbvp;
			vgPrimaryBuffer->SetMixBins(&dsmb);
	//Zero out the buffer
	void *mema=NULL,*memb=NULL;
	DWORD sizea=0,sizeb=0;
	vgPrimaryBuffer->Lock(0,vgSoundBufferSize, &mema, &sizea, &memb,&sizeb, 0);
	memset(mema,0,vgSoundBufferSize);
	vgPrimaryBuffer->Unlock(mema,sizea, memb,sizeb);

	//Start playback
	vgPrimaryBuffer->Play(0,0,DSBPLAY_LOOPING);
}

void closeWave() {}
int vgplaying = 0;
//Copies data to the stream
extern bool videoRender;
void updateVGStream() {
	//dprintf( "VG UpdateVGStream\n");
	if(vgplaying == 0 || inGame == 1 || (videoRender && EnableVideoAudio)) return;

	if(emu->track_ended()) emu->start_track(currentSong);  

	vgPrimaryBuffer->GetCurrentPosition(&vgCurrentPos,&vgWritePos);
	if (vgLastUsedPos <= vgCurrentPos)
		vgDataNeeded=vgCurrentPos-vgLastUsedPos;
	else
		vgDataNeeded=vgSoundBufferSize - vgLastUsedPos + vgCurrentPos;

	vgDataNeeded /=(4); 
	vgDataNeeded *=(4);

	while (vgDataNeeded>0)
	{
		LPVOID lpvPtr1;
		DWORD dwBytes1;
		LPVOID lpvPtr2;
		DWORD dwBytes2;

		int bufferAmount = 4;
		Music_Emu::sample_t Buffer[4];
		emu->play(bufferAmount, Buffer);

		unsigned char* t = (unsigned char*) Buffer;

		vgPrimaryBuffer->Lock(vgLastUsedPos,(bufferAmount*2), &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
		memcpy(lpvPtr1, t, dwBytes1); 
		if (NULL != lpvPtr2) 
			memcpy(lpvPtr2, t+dwBytes1, dwBytes2);
		vgLastUsedPos+=(bufferAmount*2);
		
		if (vgLastUsedPos > vgSoundBufferSize) 
			vgLastUsedPos= vgLastUsedPos - vgSoundBufferSize;
		vgDataNeeded-=(bufferAmount*2);
	}
}
int songStop  = 1;
int songOver = 1;
HANDLE hThreadA; 


void songIsOver() {
	//dprintf( "VG songIsOver\n");
	if(vgplaying == 0 || inGame == 1 || (videoRender && EnableVideoAudio)) return;

	if(emu->track_ended())
		emu->start_track(currentSong);  
	
	return;
	
//	LPVOID lpvPtr1;
//	DWORD dwBytes1;
//	LPVOID lpvPtr2;
//	DWORD dwBytes2;
//
//	vgPrimaryBuffer->Lock(0,(44100*4), &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
//
//	bool over = true;
//
////	dprintf("Locked: %d\n", dwBytes1); 
//	for(int i = 0; i < dwBytes1; i++) {
//		if(((unsigned char*)lpvPtr1)[i] != 0 && ((unsigned char*)lpvPtr1)[i] != 255 && ((unsigned char*)lpvPtr1)[i] != 254) {
//			//dprintf("Found Audio: %d\n", ((unsigned char*)lpvPtr1)[i]);
//			over = false;
//			break;
//		}
//	}
//	if(over)
//		emu->start_track(currentSong);  
}
//Callback function for the thread that handles the stream.
DWORD WINAPI WAVFileStreamThreadProc( VOID* pParameter )
{
	//dprintf( "VG Callback\n");
	for(;;)
	{
		//if(songStop == 1){
			//songOver = 1;
		//	return 0;
		//}
		if(!videoRender || !EnableVideoAudio) 
			updateVGStream();
	}
	return 0;
}


/*
case SDLK_LEFT: // prev track
					if ( track > 1 )
						track--;
					restart_track = true;
					break;
				
				case SDLK_RIGHT: // next track
					if ( track < player->tracks() )
					{
						track++;
						restart_track = true;
					}
					break;
*/

void nextTrack() {
	//dprintf( "VG nextTrack\n");
	currentSong++;
	if(currentSong > emu->track_count() - 1)
		currentSong = 0;
	emu->start_track( currentSong );
}

void previousTrack() {
	//dprintf( "VG previousTrack\n");
	currentSong--;
	if(currentSong <  0)
		currentSong = emu->track_count() - 1;
	emu->start_track( currentSong );
}

void pauseVGMusic() {
	//dprintf( "VG Pause Music\n");
	vgPrimaryBuffer->Stop();
}

void resumeVGMusic() {
	//dprintf( "VG Resume Music\n");
	//resetVGStream() ;
	 vgPrimaryBuffer->Play(0,0,DSBPLAY_LOOPING);
}

void stopMusic() {
	//dprintf( "VG Stop Music\n");
	if(vgplaying == 0) return;
	
	if(emu)
	{
		delete emu;
		emu = NULL;
	}
	
	playingMusic = 0;
	vgplaying = 0;
	//if(vgPrimaryBuffer) {
		vgPrimaryBuffer->Stop();
		//vgPrimaryBuffer->Release();
	//}

}
//Called when a song is started.
int InitVGMusic(char *path)
{
	//dprintf( "VG Load Music\n");
	//while(songOver != 1) {
	
	//}
	//if(hThreadA)
	//	CloseHandle( hThreadA );

	currentSong = 0;
	if(emu)
	{
		delete emu;
		emu = NULL;
	}
	handle_error( load_music_file( path, 44100, &emu ) );
	if ( !emu ) {
		handle_error( "Unsupported game music type" );
		return -1;
	}


	//Spc_Emu::header_t  a = ((Spc_Emu*)emu)->header();
	//dprintf("Header: 
	
	
	playingMusic = 1;
	resetVGStream();

   


	emu->start_track( currentSong );

	handle_error( gme_track_info( emu, &info, currentSong) );
	/*dprintf( "System   : %s\n", info.system );
	dprintf( "Game     : %s\n", info.game );
	dprintf( "Song     : %s\n", info.song );
	dprintf( "Author   : %s\n", info.author );
	dprintf( "Copyright: %s\n", info.copyright );
	dprintf( "Comment  : %s\n", info.comment );
	dprintf( "Dumper   : %s\n", info.dumper );
	dprintf( "Tracks   : %d\n", info.track_count );
	dprintf( "Length   : %ld seconds\n", info.length / 1000 );
	dprintf( "Intro    : %ld seconds\n", info.intro_length / 1000 );
	dprintf( "Loop     : %ld seconds\n\n\n", info.loop_length / 1000 );*/


	vgplaying = 1;
	// Create worker thread to process audio
	// CreateThread( NULL, 0, WAVFileStreamThreadProc, &vgPrimaryBuffer, 0, NULL );
	return 0;
}

void handle_error( const char* str )
{
	if ( str )
	{
		dprintf( "Blarrg Error: %s\n", str );
		//exit( EXIT_FAILURE );
	}
}