#include "portaudiocpp/Stream.hxx"

#include <cstddef>

#include "portaudiocpp/Exception.hxx"
#include "portaudiocpp/System.hxx"

namespace portaudio
{

	// -----------------------------------------------------------------------------------

	Stream::Stream() : stream_(NULL)
	{
	}

	Stream::~Stream()
	{
		// (can't call close here, 
		// the derived class should atleast call 
		// close() in it's deconstructor)
	}

	// -----------------------------------------------------------------------------------

	//////
	/// Closes the Stream if it's open, else does nothing.
	//////
	void Stream::close()
	{
		if (isOpen() && System::exists())
		{
			PaError err = Pa_CloseStream(stream_);
			stream_ = NULL;

			if (err != paNoError)
				throw PaException(err);
		}
	}

	//////
	/// Returns true if the Stream is open.
	//////
	bool Stream::isOpen() const
	{
		return (stream_ != NULL);
	}

	// -----------------------------------------------------------------------------------

	void Stream::setStreamFinishedCallback(PaStreamFinishedCallback *callback)
	{
		PaError err = Pa_SetStreamFinishedCallback(stream_, callback);

		if (err != paNoError)
			throw PaException(err);
	}

	// -----------------------------------------------------------------------------------

	void Stream::start()
	{
		PaError err = Pa_StartStream(stream_);

		if (err != paNoError)
			throw PaException(err);
	}

	void Stream::stop()
	{
		PaError err = Pa_StopStream(stream_);

		if (err != paNoError)
			throw PaException(err);
	}

	void Stream::abort()
	{
		PaError err = Pa_AbortStream(stream_);

		if (err != paNoError)
			throw PaException(err);
	}

	bool Stream::isStopped() const
	{
		PaError ret = Pa_IsStreamStopped(stream_);

		if (ret < 0)
			throw PaException(ret);

		return (ret == 1);
	}

	bool Stream::isActive() const
	{
		PaError ret = Pa_IsStreamActive(stream_);

		if (ret < 0)
			throw PaException(ret);

		return (ret == 1);
	}

	// -----------------------------------------------------------------------------------

	//////
	/// Returns the best known input latency for the Stream. This value may differ from the 
	/// suggested input latency set in the StreamParameters. Includes all sources of 
	/// latency known to PortAudio such as internal buffering, and Host API reported latency. 
	/// Doesn't include any estimates of unknown latency.
	//////
	PaTime Stream::inputLatency() const
	{
		const PaStreamInfo *info = Pa_GetStreamInfo(stream_);
		if (info == NULL)
		{
			throw PaException(paInternalError);
			return PaTime(0.0);
		}

		return info->inputLatency;
	}

	//////
	/// Returns the best known output latency for the Stream. This value may differ from the 
	/// suggested output latency set in the StreamParameters. Includes all sources of 
	/// latency known to PortAudio such as internal buffering, and Host API reported latency. 
	/// Doesn't include any estimates of unknown latency.
	//////
	PaTime Stream::outputLatency() const
	{
		const PaStreamInfo *info = Pa_GetStreamInfo(stream_);
		if (info == NULL)
		{
			throw PaException(paInternalError);
			return PaTime(0.0);
		}

		return info->outputLatency;
	}

	//////
	/// Returns the sample rate of the Stream. Usually this will be the 
	/// best known estimate of the used sample rate. For instance when opening a 
	/// Stream setting 44100.0 Hz in the StreamParameters, the actual sample 
	/// rate might be something like 44103.2 Hz (due to imperfections in the 
	/// sound card hardware).
	//////
	double Stream::sampleRate() const
	{
		const PaStreamInfo *info = Pa_GetStreamInfo(stream_);
		if (info == NULL)
		{
			throw PaException(paInternalError);
			return 0.0;
		}

		return info->sampleRate;
	}

	// -----------------------------------------------------------------------------------

	PaTime Stream::time() const
	{
		return Pa_GetStreamTime(stream_);
	}

	// -----------------------------------------------------------------------------------

	//////
	/// Accessor (const) for PortAudio PaStream pointer, useful for interfacing with 
	/// PortAudio add-ons such as PortMixer for instance. Normally accessing this 
	/// pointer should not be needed as PortAudioCpp aims to provide all of PortAudio's 
	/// functionality.
	//////
	const PaStream *Stream::paStream() const
	{
		return stream_;
	}

	//////
	/// Accessor (non-const) for PortAudio PaStream pointer, useful for interfacing with 
	/// PortAudio add-ons such as PortMixer for instance. Normally accessing this 
	/// pointer should not be needed as PortAudioCpp aims to provide all of PortAudio's 
	/// functionality.
	//////
	PaStream *Stream::paStream()
	{
		return stream_;
	}

	// -----------------------------------------------------------------------------------

} // namespace portaudio
