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

#define MP3_READ_SIZE 1024

Mp3Source::Mp3Source() : SoundSource()
{
	// Ensure no bad open causes a false success on reading
	_length = 0;
	mh = NULL;
	
	// Init mpg123
	mpg123_init();
}

Mp3Source::~Mp3Source()
{
	// Kill mpg123
	mpg123_exit();
	
	// Kill file
	close();
}

OpenStatus Mp3Source::openLocation(File *sourceFile)
{
	// Remmeber if we rendered sound or not
	rendered = 0;
	
	// Remember file source object
	_sourceFile = sourceFile;
	
	// Get file size
	sourceFile->seek(0, SEEK_END);
	fileLength = sourceFile->tell();
	sourceFile->seek(0, SEEK_SET);
	
	// Get bitrates
	const long *rates;
	size_t num_rates;
	
	mpg123_rates(&rates, &num_rates);
	
	// Get new mpg123 handle
	int error;
	mh = mpg123_new(NULL, &error);
	
	// Ensure we don't continue if something went wrong
	if(!mh) { return OPEN_FAILURE; }
	
	// Streaming mode
	mpg123_open_feed(mh);
	
	// Ensure that this output format will not change (it could, when we allow it).
	mpg123_format_none(mh);
	
	// Set all bitrates as ok
	for(int i = 0; i < (int)num_rates; ++i) 
	{
		mpg123_format(mh, rates[i], MPG123_STEREO, MPG123_ENC_SIGNED_16);
	}
	
	// Grab first frame to figure out parameters
	int result;
	
	// Allocate buffer
	unsigned char *buf = (unsigned char *)malloc(MP3_READ_SIZE);
	
	// Loop to get information
	do 
	{	
		// Read in
		_sourceFile->read(buf, 1, MP3_READ_SIZE);
		
		// Feed to mpg123
		size_t fakegot;
		result = mpg123_decode(mh, buf, MP3_READ_SIZE, NULL, 0, &fakegot);
	} while(result == MPG123_NEED_MORE);
	
	// Free buffer
	free(buf);

	if(result != MPG123_NEW_FORMAT)
	{
		// Failed to get data		
		mpg123_close(mh);
		mh = NULL;
		
		// Exit out of here, no recovery
		return OPEN_FAILURE;
	}

	// Unneeded value encoding
	int encoding;
	
	// Grab frequency to play back as well as number of channels
	result = mpg123_getformat(mh, &frequency, &channels, &encoding);
	
	// Set file length
	mpg123_fake_set_length(mh, fileLength);
	
	// Grab length
	samples = mpg123_length(mh);
	
	// Return success
	return OPEN_SUCCESS;
}

int Mp3Source::renderChunk(uint8_t *outbuf, int &renderSamples)
{	
	// Clear data to ensure no garbage bytes
	memset(outbuf, 0, renderSamples * 4);
	
	// Make sure we remember we rendered
	rendered = 1;
	
	// Start with assumption that we have enough data
	int result = MPG123_OK;
	
	// Remember end of file
	int over = 0;
	
	// Bookkeeping
	int need = renderSamples * channels * 2;
	int have_read = 0;
	
	// Allocate buffer
	unsigned char *buf = (unsigned char *)malloc(MP3_READ_SIZE);
	
	// Loop, grabbing enough data to get samples
	do
	{
		// Bookkeeping
		int dataIn = 0;
		size_t have_now = 0;
		
		// Figure out if we need more data
		if(result == MPG123_NEED_MORE)
		{
			if(over)
			{
				// We exhausted our file
				free(buf);
				
				// Ensure we don't create garbage audio
				memset(outbuf + have_read, 0, need);
				
				// More data next time
				return 0;
			}
			
			// Read in
			dataIn = _sourceFile->read(buf, 1, MP3_READ_SIZE);
			
			if(dataIn < MP3_READ_SIZE)
			{
				// End of file :<
				over = 1;
			}
		}
		
		// Grab data
		result = mpg123_decode(mh, buf, (size_t)dataIn, outbuf + have_read, need, &have_now);
		
		// Ensure we keep track of newly gotten data
		need -= have_now;
		have_read += have_now;
		
		// if we finished, then exit with success
		if(need == 0)
		{
			// Free buffer
			free(buf);
			
			// More data next time
			return 1;
		}
	} while(result == MPG123_NEED_MORE || result == MPG123_ERR);
	
	// Free buffer
	free(buf);
	
	// Abnormal exit
	return 0;
}

void Mp3Source::seekSeconds(int seconds)
{
	// Don't seek if not rendered yet
	if(!rendered) { return; }
	
	// Seek by samples ONLY if seconds = 0 (for restarting, to stop some blip of old sound)
	if(seconds == 0)
	{
		// Request seek
		off_t seekPos;
		mpg123_feedseek(mh, 0, SEEK_SET, &seekPos);
		
		// Seek to requested location
		_sourceFile->seek(seekPos, SEEK_SET);
	}
	else
	{		
		// Figure out seeking by seconds
		off_t seekoff = (int)((double)(fileLength) * ((double)seconds / (double)(samples / frequency)));
		
		// Seek to requested location
		_sourceFile->seek(seekoff, SEEK_SET);
	}
}

int Mp3Source::tellSeconds()
{
	// Calculate number of seconds in
	if(samples)
	{
		return (int)((double)(samples / frequency) * ((double)_sourceFile->tell() / (double)fileLength));
	}
	
	// No position
	return -1;
}

int Mp3Source::getLength()
{
	if(samples)
	{
		// Length in seconds to sound processor	
		return samples / frequency;
	}
	
	// No length
	return -1;
}

uint32_t Mp3Source::getFrequency()
{
	// Send back pre-fetched frequency of playback
	return frequency;
}

void Mp3Source::close()
{
	if(mh)
	{
		// Close the mpg123 handle
		mpg123_close(mh);
		mh = NULL;
	}
}

void Mp3Source::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 Mp3Source::supportsFormat(const char *ext)
{
	// Only one format
	if(strcmp(ext, "mp3") == 0) { return 1; }
	if(strcmp(ext, "mp2") == 0) { return 1; }
	if(strcmp(ext, "mp1") == 0) { return 1; }
	if(strcmp(ext, "mpa") == 0) { return 1; }
	
	// We support no other formats, so this function always fails
	return 0;
}

void Mp3Source::readAhead()
{
	// Read ahead some
	_sourceFile->readAhead();
}

