#include <string.h>
#include <malloc.h>
#include "modulesource.h"

static int dumb_skip(void *f, long n)
{
	return ((File *)f)->seek(n, SEEK_CUR);
}

static int dumb_getc(void *f)
{
	char c = 0;
	
	if(!((File *)f)->read(&c, 1, 1))
	{
		// Eof
		return -1;
	}
	
	return c;
}

static long dumb_getnc(char *ptr, long n, void *f)
{
	return ((File *)f)->read(ptr, 1, n);
}

ModuleSource::ModuleSource() : SoundSource()
{
	// Ensure registered modules
	MikMod_RegisterAllDrivers();
	MikMod_RegisterAllLoaders();
}

ModuleSource::~ModuleSource()
{
	// Ensure proper cleanup
	close();
}

OpenStatus ModuleSource::openLocation(File *sourceFile)
{
	// Remember no audio samples rendered
	rendered = 0;
	
	// Init dumb
	dumb_it_max_to_mix = 256;
	
	// Pointers should be null so we don't free random data
	duh = NULL;
	d = NULL;
	ds = NULL;
	module = NULL;
	
	// Try to open wav file
	_sourceFile = sourceFile;
	
	// Seek to start
	sourceFile->seek(0, SEEK_SET);
	
	// Initialize dumb
	DUMBFILE_SYSTEM stdfile_dfs = {	NULL,
									&dumb_skip,
									&dumb_getc,
									&dumb_getnc,
									NULL };
	
	// Open file
	d = dumbfile_open_ex(sourceFile, &stdfile_dfs);
	
	if(d)
	{	
		// Try to init
		duh = read_duh(d);
		if (!duh)
		{
			sourceFile->seek(0, SEEK_SET);
			duh = dumb_read_xm(d);
			
			if (!duh)
			{
				sourceFile->seek(0, SEEK_SET);
				duh = dumb_read_mod(d);
			}
		}
		
		if (duh)
		{
			// Start rendering engine
			ds = duh_start_sigrenderer(duh, 0, 2, 0);
			
			if(ds) 
			{
				// Make sure it files that end get terminated
				DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(ds);
				dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL);
				
				return OPEN_SUCCESS;
			}
			else
			{
				unload_duh(duh); 
				duh = NULL;
			}
		}
	}

	// Init mikmod
	md_mode = 0;
	md_mode |= DMODE_16BITS;
	md_mode |= DMODE_SOFT_MUSIC;
	md_mode |= DMODE_SOFT_SNDFX;
	//md_mode |= DMODE_STEREO;  this causes some modules (s3m mostly) to play back incorrectly on wii, i dont know why
	md_mode |= DMODE_HQMIXER;
	
	md_mixfreq = 48000;
	
	MikMod_Init((char *)"");
	
	// Grab file length
	sourceFile->seek(0, SEEK_END);
	vf.size = sourceFile->tell();
	sourceFile->seek(0, SEEK_SET);
	
	// Grab file to memory
	vf.buffer = (char *)malloc(vf.size);
	sourceFile->read(vf.buffer, 1, vf.size);
	
	vf.pos = 0;
	
	// Load module
	module = Player_LoadFP(&vf, 256, 0);
	module->wrap = 1;
	
	if(module == NULL)
		return OPEN_FAILURE;
	
	Player_Start(module);
	
	// Return success
	return OPEN_SUCCESS;
}

int ModuleSource::renderChunk(uint8_t *buf, int &renderSamples)
{
	// Safe to seek back now
	rendered = 1;
	
	// Ensure no blips from end
	memset(buf, 0, renderSamples * 4);
	
	// Make sure if it failed, we don't render
	if(ds) 
	{
		// Render data (16bit stereo, so 2 bytes per sample, 2 channels)
		int outSamples = duh_render(ds, 16, 0, 1.0f, 65536.0f / 48000.0f, renderSamples, buf);
		
		// More data is available?
		return (outSamples == renderSamples);
	}
	else if(module)
	{
		setBuffer((s16 *)buf, renderSamples);
		MikMod_Update();
		
		return Player_Active();
	}
	
	return 0;
}

void ModuleSource::seekSeconds(int seconds)
{
	// No seeking
	if(!rendered) { return; }
	
	// Seek to beginning
	openLocation(_sourceFile);
}

void ModuleSource::close()
{
	// Kill renderer if it exists
	if(ds) { duh_end_sigrenderer(ds); }
	if(duh) 
	{ 
		unload_duh(duh); 
		
		// Kill dumb
		dumb_exit();
	}
	
	// Kill module renderer if it exists
	if(module)
	{
		Player_Stop();
		Player_Free(module);
		
		module = NULL;
		
		MikMod_Exit();
	}
	
	// Pointers should be null so we don't free random data
	duh = NULL;
	d = NULL;
	ds = NULL;
	module = NULL;
}

void ModuleSource::getNowPlaying(const char *fmtString, char *output)
{
	// Grab the filename
	char tFile[MAXPATHLEN];
	_sourceFile->getCurrentUrl(tFile);
	
	// Find the filename only
	for(int i = strlen(tFile); i >= 0; i--)
	{
		if(tFile[i] == '/')
		{
			// Found it!
			strcpy(output, &tFile[i + 1]);
			
			// Exit
			return;
		}
	}
	
	// Default
	strcpy(output, "Nothing!");
}

int ModuleSource::supportsFormat(const char *ext)
{
	// Four formats
	if(strcmp(ext, "mod") == 0) { return 1; }
	if(strcmp(ext, "xm") == 0) { return 1; }
	if(strcmp(ext, "it") == 0) { return 1; }
	if(strcmp(ext, "s3m") == 0) { return 1; }
	
	// We support no other formats, so this function always fails
	return 0;
}
