//	BackgroundMusic.cpp - Class to play looped background music.
//  ----------------------------------------------------------------------------
//	This file is part of 'Die, you Stupid Hurdlers!', a game for Ludum Dare 14.
//	Copyright (C) 2009  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "BackgroundMusic.h"

#include "SDL.h"
#include <string>
#include <sstream>
#include <iostream>
#include <vorbis/codec.h>

#ifdef WIN32
#include <windows.h>
#endif

#ifdef OSX
#include <CoreServices/CoreServices.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

using namespace std;

//------------------------------------------------------------------------------
BackgroundMusic::BackgroundMusic():
file(0),
filePosition(0),
level(0),
readBuffer(0),
readPosition(0),
timeToStop(false)
{
	int i;
	int result = 0;
	stringstream tempPath;

	for(i=0;i<NumBuffers;++i)
		clearedBuffers[i] = true;

	tempPath << getSoundsPath() << "music.ogg";

	file = fopen(tempPath.str().c_str(), "rb");
	if(!file)
		cout << "vorbisfile: fopen could not open file: " << tempPath.str() << endl;
	else
	{
		if(ov_open(file, &vorbisFile, NULL, 0) < 0)
			cout << "vorbisfile: Could not open file: " << tempPath.str() << endl;
		else
		{
			if(ov_streams(&vorbisFile) > 0)
			{
				cout << "vorbisfile: Loaded file: " << tempPath.str() << endl;

				//Fill the buffers.
				for(i=0;i<NumBuffers;++i)
					fillBuffer(i);

				//Get the length of the file.
				length = ov_pcm_total(&vorbisFile, 0);

				//Start the thread.
				startThread(this);
			}
			else
			{
				ov_clear(&vorbisFile);
				cout << "vorbisfile: There are no streams in this file?" << endl;
			}
		}
	}
}

//------------------------------------------------------------------------------
BackgroundMusic::~BackgroundMusic()
{
	timeToStop = true;
	SDL_Delay(600);

	if(file)
		ov_clear(&vorbisFile);
}

//------------------------------------------------------------------------------
void BackgroundMusic::audioBlock(float *buffer, int numSamples)
{
	int i;
	int tempint;

	if(!file)
		return;

	//If we've not reached the end of this buffer.
	if((BufferSize-readPosition) >= numSamples)
	{
		for(i=0;i<numSamples;++i)
		{
			buffer[(i*2)] += left[readBuffer][readPosition] * level * 0.75f;
			buffer[(i*2)+1] += right[readBuffer][readPosition] * level * 0.75f;
			if(level < 1.0f)
				level += 0.000001f;

			++readPosition;
		}
		if(readPosition >= length)
		{
			readPosition = 0;
			clearedBuffers[readBuffer] = true;
			readBuffer = (readBuffer+1)%NumBuffers;
		}
	}
	//If we need to read from the next buffer halfway through this audio block.
	else
	{
		tempint = BufferSize-readPosition;

		//Read the rest of the samples from the current buffer.
		for(i=0;i<tempint;++i)
		{
			buffer[(i*2)] += left[readBuffer][readPosition] * level * 0.75f;
			buffer[(i*2)+1] += right[readBuffer][readPosition] * level * 0.75f;
			if(level < 1.0f)
				level += 0.000001f;

			++readPosition;
		}
		//tempint = numSamples - tempint;

		//Move to the next buffer.
		readPosition = 0;
		clearedBuffers[readBuffer] = true;
		readBuffer = (readBuffer+1)%NumBuffers;

		//Read the remaining samples from the new buffer.
		for(i=tempint;i<numSamples;++i)
		{
			buffer[(i*2)] += left[readBuffer][readPosition] * level * 0.75f;
			buffer[(i*2)+1] += right[readBuffer][readPosition] * level * 0.75f;
			if(level < 1.0f)
				level += 0.000001f;

			++readPosition;
		}
	}
}

//------------------------------------------------------------------------------
bool BackgroundMusic::run()
{
	int i;

	while(!timeToStop)
	{
		for(i=0;i<NumBuffers;++i)
		{
			if(clearedBuffers[i])
			{
				fillBuffer(i);
				clearedBuffers[i] = false;
			}
		}

		//Wait a bit before looping.
		SDL_Delay(62);
	}

	return true;
}

//------------------------------------------------------------------------------
void BackgroundMusic::fillBuffer(int buf)
{
	int i;
	int result;
	float **tempBuf;
	int currentBitstream = 0;
	int samplesRead = 0;

	//Make sure we're at the correct point to read from.
	ov_pcm_seek(&vorbisFile, filePosition);

	//Read audio from disk into the current block.
	while(samplesRead < BufferSize)
	{
		result = ov_read_float(&vorbisFile,
							   &tempBuf,
							   (BufferSize-samplesRead),
							   &currentBitstream);
		if((currentBitstream > 0)||(result <= 0))
		{
			//We've either gone past the end of our bitstream, or there are
			//no other bitstreams in the file, and we've reached the end of
			//the file.
			filePosition = 0;
			ov_pcm_seek(&vorbisFile, 0);
		}
		else if(result <= BufferSize)
		{
			//Copy the audio data to the output buffer.
			for(i=0;i<result;++i)
			{
				if(samplesRead >= BufferSize)
					break;
				left[buf][samplesRead] = tempBuf[0][i];
				//right[buf][samplesRead] = tempBuf[0][(i*2)+1];
				//right[buf][samplesRead] = left[buf][samplesRead];
				right[buf][samplesRead] = tempBuf[1][i];
				++samplesRead;
				++filePosition;
			}
		}
	}
}

//------------------------------------------------------------------------------
string BackgroundMusic::getSoundsPath()
{
	string retval;

#ifdef WIN32
	char tempstr[MAX_PATH + 256];

	if(GetCurrentDirectory((MAX_PATH+256), tempstr) > 0)
	{
		stringstream tempStream;

		tempStream << tempstr << "\\sounds\\";

		retval = tempStream.str();
	}
	else
		cout << "Could not determine TheFutureWillOnlyBeDystopianIfCapitalismRemainsUndefeated.exe path." << endl;
#elif defined(LINUX)
	stringstream tempStream;

	tempStream << PACKAGE_DATA_DIR << "/TheFutureWillOnlyBeDystopianIfCapitalismRemainsUndefeated/sounds/";
	cout << tempStream.str() << endl;

	retval = tempStream.str();
#elif defined(OSX)
	FSRef fsRef;
	CFURLRef bundlePath;
	stringstream tempStream;

	bundlePath = CFBundleCopyBundleURL(CFBundleGetMainBundle());

	if(CFURLGetFSRef(bundlePath, &fsRef))
	{
		char tempstr[2048];

		FSRefMakePath(&fsRef, (Uint8 *)tempstr, 2048);

		tempStream << tempstr << "/Contents/Resources/sounds/";
		retval = tempStream.str();
	}
#endif

	return retval;
}
