//	PortAudioDevice.cpp - Simple C++ wrapper for PortAudio.
//  ----------------------------------------------------------------------------
//	This file is part of 'GameTemplate', a game.
//	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 "PortAudioDevice.h"

#include <iostream>
#include <cmath>

using namespace std;

//------------------------------------------------------------------------------
PortAudioDevice::PortAudioDevice():
stream(0),
callback(0),
numInputs(0),
numOutputs(2),
samplerate(44100.0),
isRunning(false)
{
	
}

//------------------------------------------------------------------------------
PortAudioDevice::~PortAudioDevice()
{
	if(isRunning)
		stop();
}

//------------------------------------------------------------------------------
void PortAudioDevice::setCallback(PortAudioCallback *val)
{
	callback = val;
}

//------------------------------------------------------------------------------
void PortAudioDevice::setNumChannels(int inputs, int outputs)
{
	numInputs = inputs;
	numOutputs = outputs;
}

//------------------------------------------------------------------------------
void PortAudioDevice::setSamplerate(double rate)
{
	samplerate = rate;
}

//------------------------------------------------------------------------------
bool PortAudioDevice::start()
{
	bool retval = false;

	if(!isRunning)
	{
		if(Pa_Initialize() == paNoError)
		{
			const PaHostApiInfo *apiInfo;
			PaHostApiIndex apiIndex;
			PaStreamParameters inputParams, outputParams;
			PaError err;

#ifdef WIN32
			apiIndex = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
#elif defined(LINUX)
			apiIndex = Pa_HostApiTypeIdToHostApiIndex(paALSA);
#elif defined(OSX)
			apiIndex = Pa_HostApiTypeIdToHostApiIndex(paCoreAudio);
#endif
			apiInfo = Pa_GetHostApiInfo(apiIndex);
			/*deviceIndex = Pa_HostApiDeviceIndexToDeviceIndex(apiIndex,
															 apiInfo->defaultOutputDevice);*/

			inputParams.device = apiInfo->defaultInputDevice;
			inputParams.channelCount = numInputs;
			inputParams.sampleFormat = paFloat32;
			inputParams.suggestedLatency = 0.0;
			inputParams.hostApiSpecificStreamInfo = 0;

			outputParams.device = apiInfo->defaultOutputDevice;
			outputParams.channelCount = numOutputs;
			outputParams.sampleFormat = paFloat32;
#ifdef LINUX
			outputParams.suggestedLatency = 1.0; //My Linux laptop doesn't like 0.0 here?
#else
			outputParams.suggestedLatency = 0.0;
#endif
			outputParams.hostApiSpecificStreamInfo = 0;

			if(Pa_IsFormatSupported(NULL, &outputParams, samplerate) == paInvalidSampleRate)
			{
				cout << "44100Hz samplerate not supported, using 48000Hz." << endl;
				samplerate = 48000.0; //This is obviously a nasty hacky way of doing things.

				if(Pa_IsFormatSupported(NULL, &outputParams, samplerate) == paInvalidSampleRate)
					cout << "48000Hz not supported either?" << endl;
			}

			err = Pa_OpenStream(&stream,
								(numInputs < 1) ? 0 : &inputParams,
								(numOutputs < 1) ? 0 : &outputParams,
								samplerate,
								0,
								paNoFlag,
								portAudioCallback,
								(void *)callback);
			if(err != paNoError)
			{
				cout << "PortAudioDevice Error: Could not open stream." << endl;
				Pa_Terminate();
			}
			else if(Pa_StartStream(stream) == paNoError)
			{
				isRunning = true;
				retval = true;
			}
			else
			{
				cout << "PortAudioDevice Error: Could not start stream." << endl;
				Pa_CloseStream(stream);
				Pa_Terminate();
			}
		}
		else
			cout << "PortAudioDevice Error: Could not initialise." << endl;
	}

	return retval;
}

//------------------------------------------------------------------------------
void PortAudioDevice::stop()
{
	if(isRunning)
	{
		Pa_StopStream(stream);
		Pa_CloseStream(stream);
		Pa_Terminate();
	}
}

//------------------------------------------------------------------------------
int PortAudioDevice::portAudioCallback(const void *input,
									   void *output,
									   unsigned long frameCount,
									   const PaStreamCallbackTimeInfo *timeInfo,
									   PaStreamCallbackFlags statusFlags,
									   void *userData)
{
	PortAudioCallback *callback = (PortAudioCallback *)userData;

	if(callback)
		callback->getAudio((float *)input, (float *)output, frameCount);

	return 0;
}
