/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   SoundSystem
// Purpose:      Static class that provides a simple interface to directsound.
//               Allows Playback of sounds and loading of sounds. Only allows
//               one shot, non positional playback however. This is useful only
//               for non in game sounds such as those in menu systems and certain
//               types of sounds we want to always have heard.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "SoundSystem.h"

//------------------------------------------------------------------------------
// Static variable initialisation
//------------------------------------------------------------------------------

IDirectSound8 *                 SoundSystem::directSound    = 0;
IDirectSoundBuffer8 *           SoundSystem::primaryBuffer  = 0;
list<IDirectSoundBuffer8*> *    SoundSystem::soundsList     = 0;
bool							SoundSystem::soundEnabled	= true;

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::initialise()
Purpose:  Sets up the sound system and sets up directsound. must be called before
          any sounds are loaded or played.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::initialise()
{
    // If sound system already initialised then abort

    if ( directSound ) return;

    // Create the linked list of currently playing sounds:

    soundsList = new list<IDirectSoundBuffer8*>();

    // Create the direct sound object:

    DirectSoundCreate8
    (
        0,                  // playback device to use: null means use default system plaback device
        &directSound,       // pointer to the pointer of the direct sound object
        0                   // must be null
    );

    // If the direct sound object has been created then set the cooperative level to normal:

    if ( directSound )
    {
        // Set the cooperative level to priority level

        int result = directSound->SetCooperativeLevel
        (
            GLWindow::getWindowHandle(),    // handle to window
            DSSCL_PRIORITY                  // set priority level
        );

        // If the cooperative level was not set then shut down the sound system

        if ( result != DS_OK )
        {
            // Couldn't create the sound system. shut down.

            SoundSystem::shutdown();
        }
        else
        {
            // Create a direct sound buffer description:

            DSBUFFERDESC description = {0};

            description.dwSize          = sizeof(DSBUFFERDESC);     // set the size of this structure
            description.dwBufferBytes   = 0;                        // number of bytes in the buffer: must be 0 for primary buffer
            description.dwFlags        |= DSBCAPS_CTRLVOLUME;       // allow volume control
            description.dwFlags        |= DSBCAPS_PRIMARYBUFFER;    // this is the primary buffer
            description.lpwfxFormat     = 0;                        // must be null for primary buffers

            // Create the primary buffer:

            primaryBuffer = 0;

            int bufferResult = directSound->CreateSoundBuffer
            (
                &description,                           // buffer description
                (IDirectSoundBuffer**) &primaryBuffer,  // where to save the buffer pointer
                0                                       // must be null
            );

            // Make sure the primary buffer is null if the creation did not happen

            if ( bufferResult != DS_OK ) primaryBuffer = 0;
        }

    }   // end if direct sound initialised
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::shutdown()
Purpose:  Shuts down the sound system. Should be called on program end.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::shutdown()
{
    // Only do this if there is a direct sound object

    if ( directSound )
    {
        // Release the primary buffer if it exists

        if ( primaryBuffer )
        {
            // Release the primary buffer

            primaryBuffer->Release();

            // Set the primary buffers pointer to null again:

            primaryBuffer = 0;
        }

        // Stop all playing sounds:

        stopAll();

        // Release the direct sound object

        directSound->Release();

        // Set the direct sound object to null

        directSound = 0;
    }

    // Delete the linked list of currently playing sounds:

    delete soundsList; soundsList = 0;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::loadSound()
Purpose:  Loads the given sound file and returns a sound object pointer.
Params:   Name of the sound file to load.
Returns:  Sound object created.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

Sound * SoundSystem::loadSound( const char * fileName )
{
    // Create a sound object and return its pointer:

    return new Sound( directSound , fileName );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::releasedFinishedSounds()
Purpose:  Releases the duplicate buffers of any sounds that are finished playing.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::releasedFinishedSounds()
{
    // Get an iterator for the start and end of the sounds list:

    list<IDirectSoundBuffer8*>::iterator cur = soundsList->begin();
    list<IDirectSoundBuffer8*>::iterator end = soundsList->end();

    // Go through all the sounds in the list:

    while ( cur != end )
    {
        // Get the current sound buffer object:

        IDirectSoundBuffer8 * currentBuffer = * cur;

        // Save the status of the buffer here:

        DWORD status;

        // Get the status of the buffer

        currentBuffer->GetStatus(&status);

        // See if the sound has finished playing:

        if ( status != DSBSTATUS_PLAYING )
        {
            // Sound is finished playing: move on current pointer to next item in list

            list<IDirectSoundBuffer8*>::iterator temp = cur; cur++;

            // Remove the previous item from the list

            currentBuffer->Release(); soundsList->erase(temp);
        }
        else
        {
            // Sound still playing: move onto the next sound in the list

            cur++;
        }
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::stopAll()
Purpose:  Stops all currently playing sounds
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::stopAll()
{
    // Get an iterator for the start and end of the sounds list:

    list<IDirectSoundBuffer8*>::iterator cur = soundsList->begin();
    list<IDirectSoundBuffer8*>::iterator end = soundsList->end();

    // Go through all the sounds in the list:

    while ( cur != end )
    {
        // Get the current sound buffer object:

        IDirectSoundBuffer8 * currentBuffer = * cur;

        // Stop it from playing:

        currentBuffer->Stop();

        // Move onto the next sound in the list

        cur++;
    }

    // Remove all stoped sounds from the list of playing sounds:

    releasedFinishedSounds();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::playSound()
Purpose:  Plays a given sound
Params:   Sound to play
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::playSound( const Sound * sound )
{
	// Don't do anything if sound is disabled:
	
	if ( soundEnabled == false ) return;

    // Abort if the sound system is not up and running:

    if ( directSound == 0 ) return;

    // Make sure all finished sounds have been released and removed from the list:

    releasedFinishedSounds();

    // Abort if the sound given is null:

    if ( sound ==  0 ) return;

    // Abort if the given sound has a null buffer:

    if ( sound->buffer == 0 ) return;

    // Save a pointer to a duplicate buffer from buffer of original sound here:

    IDirectSoundBuffer8 * newBuffer = 0;

    // Create a duplicate of the given sounds sound buffer:

    int result = directSound->DuplicateSoundBuffer
    (
        (IDirectSoundBuffer*  )   sound->buffer,    // buffer to duplicate
        (IDirectSoundBuffer** ) & newBuffer         // save pointer of new buffer here
    );

    // Make sure the buffer was created ok:

    if ( result == DS_OK )
    {
        // Good stuff. Play this sound:

        newBuffer->Play
        (
            0,      // reserved: must be 0
            0,      // priority: dont care about this
            0       // dont care about flags
        );

        // Add this buffer into the list of currently playing sounds:

        soundsList->push_back(newBuffer);
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::isSoundEnabled()
Purpose:  Tells if sound is enabled or not
Params:   none
Returns:  True if sound is enabled

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
        
bool SoundSystem::isSoundEnabled()
{
	return soundEnabled;
}
        
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SoundSystem::setSoundEnabled()
Purpose:  Enables/disables sound
Params:   Whether sound is to be enabled or not
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SoundSystem::setSoundEnabled(bool enabled)
{
	soundEnabled = enabled;
	
	if (!soundEnabled)
	{
		// Stop all sounds if sound is not enabled
	
		stopAll();
	}
}
