//	SoundFileLoader.h - Base class for sound file loaders.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  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/>.
//	----------------------------------------------------------------------------

#ifndef SOUNDFILELOADER_H_
#define SOUNDFILELOADER_H_

#include "c0xHeaders.h"
#include "FilePath.h"

#include <stdint.h>
#include <map>

class SoundFileLoader;

typedef StdSharedPtr<SoundFileLoader>::type SoundFileLoaderPtr;

//------------------------------------------------------------------------------
///	Base class for sound file loaders.
class SoundFileLoader
{
  public:
	///	Constructor.
	SoundFileLoader(const FilePath& path) {};
	///	Destructor.
	virtual ~SoundFileLoader() {};

	///	Loads a chunk of the file into the passed-in buffer.
	/*!
		\param buffer A pointer to the buffer to write the sample data to.
		On return this will be filled with sample data from the buffer, and if
		there is more than 1 channel in the file, the samples will be
		interleaved (left, right, left, right...). The method will determine the
		number of samples to read for each channel from
		(bufferSize/getNumChannels()). Obviously, make sure the buffer is
		allocated before you call this method.
		\param position The (sample) position within the sound file to start
		reading from. It will be updated on return according to the number of
		samples that were read (and it will be 0 if the end of the file was
		reached).
		\param bufferSize The size of the buffer in samples. As sound files
		may have multiple channels, the number of samples read (per-channel)
		may well be less than this value.
		\param bufferOffset This is the sample offset into the buffer at which
		the method will start writing data. This is probably only useful if
		you're wanting to loop a sound file.

		\return This will return the number of samples that were actually
		written to buffer. It will be less than (bufferSize/getNumChannels())
		if the loader reached the end of the file, in which case you'll
		probably want to call loadChunk() a second time with an updated
		sampleOffset to fill the rest of your buffer (if you're looping,
		that is).

		If the method reaches the end of the file, it will stop writing data
		and reset the loader's position to 0 so that the next call to
		loadChunk() will start from the beginning again.
	 */
	virtual int loadChunk(float *buffer,
						  int64_t& position,
						  int bufferSize,
						  int bufferOffset = 0) = 0;

	///	Returns the length of the sound file in samples.
	virtual int64_t getFileLength() const = 0;
	///	Returns the sound file's samplerate.
	virtual float getSamplerate() const = 0;
	///	Returns the number of channels in the file.
	virtual int getNumChannels() const = 0;

	///	Returns the initialBuffers for this file.
	/*!
		This should be used to fill out 3 (or more, if you want) initial buffers
		worth of audio from the file, so you can cache them and ensure there's
		minimal delay when starting to play a sound file.
	 */
	std::vector<std::vector<float> >& getInitialBuffers() {return initialBuffers;};

	///	Returns the name of the sound file.
	/*!
		This can be used to identify the sound file.
	 */
	virtual const std::wstring getName() const = 0;

	///	Used to create SoundFileLoader subclasses.
	class Creator
	{
	  public:
		///	Constructor.
		Creator() {};
		///	Destructor.
		virtual ~Creator() {};

		///	Returns a new instance of a SoundFileLoader.
		virtual SoundFileLoaderPtr createLoader(const FilePath& path) const = 0;
	};
  private:
	///To prevent copying.
	SoundFileLoader(const SoundFileLoader& other) {};

	///	This is so we can cache the initial 3 buffers-worth of audio from the file (if we want to).
	std::vector<std::vector<float> > initialBuffers;
};

//------------------------------------------------------------------------------
///	Class which will return the correct SoundFileLoader to load a particular sound file.
class SoundFileFactory
{
  public:
	///	Constructor.
	SoundFileFactory();
	///	Destructor.
	~SoundFileFactory();

	///	Returns a SoundFileLoader to load the passed-in sound file.
	/*!
		\param path Path to the file to load.

		\return A pointer to the relevant SoundFileLoader. This will be empty
		if there were no Creators matching the passed-in file's extension.

		The factory determines which SoundFileLoader to create from the
		passed-in file's extension.
	 */
	SoundFileLoaderPtr getLoader(const FilePath& soundFile) const;

	///	Registers a SoundFileLoader Creator with the factory.
	void registerCreator(const std::wstring& extension,
						 SoundFileLoader::Creator *creator);
  private:
	///	The available SoundFileLoader Creators.
	std::map<std::wstring, SoundFileLoader::Creator *> creators;
};

#endif
