// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   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 2 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, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

// SoundManager class - A simple OpenAL sound player with OGG music
// support. You may blame Cliff for this one, with help from Brian.

#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H

#include <string>

#ifdef WIN32
// Under Windows we assume that the headers for OpenAL are pointed to
// directly.
#include <al.h>
#include <alc.h>
#else
// Linux OpenAL headers go in the "AL" directory under the normal
// include directory.
#include <AL/al.h>
#include <AL/alc.h>
#endif

#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>

namespace Sound {

	// Music buffer size. Larger Means more memory used and more
	// decoding done at once, but less frequent calls to updateMusic
	// are needed.
	#define MUSIC_BUF_SIZE 65536
	
	// Max sound buffer size.
	#define SOUND_MAX_SIZE 16777216
	
	// Number of sound sources. This is how many sounds that can play
	// at once. The Windows OpenAL implementation seems to freak out
	// if you go beyond 25 (including one extra for music).
	#define MAX_SOUND_SOURCES 24
	
	/** @brief Simple OpenAL sound player with OGG Vorbis music
	 * support.
	*/
	class SoundManager {
		public:
			
			SoundManager(void);
			virtual ~SoundManager(void);
		
			/** Loads an Ogg Vorbis file. 
				@note For music, use startMusic() instead!
				@param fileName The name of the file to load.
				@return A number corresponding to the "snd" parameter in playSound() and freeSound() or -1 on error.
			*/
			unsigned int loadOgg(const std::string &fileName);

			/** Loads an Ogg Vorbis file from a memory buffer.
				@note Does not free the buffer, but it may be safely freed after this operation.
				@param data Pointer to the beginning of the buffer in memory.
				@param length Length of the buffer in bytes.
			 */
			unsigned int loadOggFromMemory(const void *data, int length);
		
			/** Unloads and frees up resources from a sound buffer.
				@param snd The sound number returned from loadWAV() when the sound was loaded.
			*/
			void freeSound(unsigned int snd);
		
			/** Plays a sound.
				@param snd The sound number returned from loadWAV() when the sound was loaded.
				@param x x coordinate for 3D sound.
				@param y y coordinate for 3D sound.
				@param z z coordinate for 3D sound.
				@param loop true if the sound should loop until stopAllSounds() is called.
			*/
			
			// TODO: Add volume control to this function.

			// TODO: Add volume control for the whole thing. (Then
			// multiply volumes together to get final.)

            void playSound(unsigned int snd, double x = 0, double y = 0, double z = 0, bool loop = false, float volume = 1);
			
			/** Stops all sounds currently being played. This does not stop music. */
			void stopAllSounds(void);
		
			/** Starts streaming an OGG Vorbis file from the disk and playing it. Any music being played will be stopped and it will loop until being stopped.
				@param fileName The name of the file to play.
			*/
			void startMusic(const std::string &fileName);
			
			/** Starts streaming an OGG Vorbis file from memory and playing it. Loops until stopped.
				@note Unlike loadOggFromMemory, you actually DO need to keep the buffer for this one around, or just set freeBufferWhenDone to true and forget about it.
				@param buffer Pointer to the start of the buffer (an OGG file copied completely into memory).
				@param length Length of the buffer in bytes.
				@param freeBufferWhenDone If set to true, SoundManager will delete[] the buffer when it's done using it.
			 */
			void startMusicFromBuffer(char *buffer, int length, bool freeBufferWhenDone);

			/** Stops all music being played. */
			void stopMusic(void);
			
			/** Checks to see if music is playing.
				@return true if music is playing, false otherwise.
			*/
			bool getMusicPlaying(void);
			
			/** Run the music buffer decode/update loop. Do this at least a few times a second or the music buffers will have an underrun.
			*/
			void updateMusic(void);
		
            /// Set the music volume. 0 = silence, 1 = full volume.
            void setMusicVolume(float v);

            /// Get the music volume.
            float getMusicVolume(void);

            /// Set the normal sound volume. 0 = silence, 1 = full volume.
            void setSoundVolume(float v);

            /// Get the normal sound volume.
            float getSoundVolume(void);

		private:
			
			// I'm not bothering with the javadoc style silliness for
			// private/protected stuff.
		
			ALuint getFreeSource(void);    // Find a sound source that isn't already making noise.
			void fillMusicBuffer(unsigned int buf); // Fill up a specified music buffer with some more music data.
		
			std::string musicFilename;     // File name of the music currently playing.
			bool musicPlaying;             // true if music is playing, false otherwise. Use getMusicPlaying to check this!
			OggVorbis_File musicFile;      // Ogg Vorbis file information.
			ALuint musicBuffers[2];        // Audio buffers to alternate between.
			unsigned int currentMusicBuffer;        // Which music buffer is currently being played.
		
			ALCdevice *alcdev;             // OpenAL device.
			ALCcontext *alccon;            // OpenAL context.
			ALuint sources[MAX_SOUND_SOURCES]; // OpenAL sound sources list.
			ALuint musicSource;            // An OpenAL sound source specifically for music.

			char *musicFileBuffer;         // Loaded music file as a memory buffer, still encoded.
			int  musicFileSize;
			int  musicFilePos;
			bool musicFileBufferMine;      // If true, free the music buffer when it's done playing.

            float normalSoundVolume;       // Volume level for normal sounds is multiplied against this.

			friend size_t oggReadMusicFromMemory(void *ptr, size_t size, size_t nmemb, void *datasource);
	};
};
	
#endif
