//
// dummysound.cpp
//
// a bunch of dummy implementations of the sound interfaces, for use without a
// sound card.
//

#include "pch.h"
#include "soundbase.h"
#include "soundutil.h"

namespace SoundEngine {


//
// A stubbed version of the ISoundInstance interface
//
class DummySoundInstance : public ISoundInstance, public ISoundTweakable3D
{
    bool m_bLooping;
    bool m_bPlaying;
    TRef<ISoundPositionSource> m_psource;
    TRef<EventSourceImpl> m_peventsourceFinished;

public:
    
    DummySoundInstance(bool bLooping, ISoundPositionSource* psource) 
        :   m_bLooping(bLooping), 
            m_bPlaying(true),
            m_psource(psource) 
    {
        m_peventsourceFinished = new EventSourceImpl();
    };


    // Stops the sound.  If bForceNow is true the sound will stop ASAP, 
    // possibly popping.  If it is false some sounds may play a trail-off 
    // sound or fade away.  
    HRESULT Stop(bool bForceNow = false)
    {
        m_bLooping = false;
        return S_OK;
    };

    // returns S_OK if the sound is currently playing
    HRESULT IsPlaying()
    {
        return m_bPlaying ? S_OK : S_FALSE;
    };

    // Gets an event which fires when the sound finishes playing (for any 
    // reason)
    IEventSource* GetFinishEventSource()
    {
        return m_peventsourceFinished;
    };

    // Gets an interface for tweaking the sound, if supported, NULL otherwise.
    virtual TRef<ISoundTweakable> GetISoundTweakable()
    {
        return this;
    };

    // Gets an interface for tweaking the sound, if supported, NULL otherwise.
    virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
    {
        return this;
    };

    // update the sound, return true if we should keep it around.
    bool Update()
    {
        if (m_bPlaying)
        {
            m_bPlaying = m_bLooping && (!m_psource || m_psource->IsPlaying());

            if (!m_bPlaying)
                m_peventsourceFinished->Trigger();
        }

        return m_bPlaying;
    };


    //
    // ISoundTweakable
    //

    // Sets the gain, from 0 to -100 dB
    HRESULT SetGain(float fGain)
    {
        return S_OK;
    }

    // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed, 
    // and 2.0 is twice normal speed.  
    HRESULT SetPitch(float fPitch)
    {
        return S_OK;
    }

    // sets the priority - used as a addition to volume when choosing which 
    // sounds are most important to play.
    virtual HRESULT SetPriority(float fPriority)
    {
        return S_OK;
    }


    //
    // ISoundTweakable3D 
    //

    // toggles 3D Positioning on and off for the given sound.
    HRESULT Set3D(bool b3D)
    {
        return S_OK;
    }

    // Sets the distance at which the sound will be at max volume.  This
    // effects how quickly the sound drops off with distance.  
    HRESULT SetMinimumDistance(float fMinimumDistance)
    {
        return S_OK;
    }

    // Sets a sound cone of size fInnerAngle (in degrees) where the volume is at 
    // normal levels, outside of which it fades down by fOutsideGain 
    // (range of 0 to -100 db) at fOuterAngle (degrees) and beyond.  
    HRESULT SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
    {
        return S_OK;
    }
};


//
// A stubbed version of the ISoundEngine interface
//
class DummySoundEngineImpl : public ISoundEngine, public ISoundBufferSource
{
    TList<TRef<DummySoundInstance> > m_listPlayingSounds;
    TRef<IntegerEventSourceImpl> m_peventsourceUpdate;
    DWORD m_dwLastUpdateTime;
    TRef<ISoundBufferSource> m_pBufferSourceDelegate;

public:

    // constructor
    DummySoundEngineImpl()
    {
        m_peventsourceUpdate = new IntegerEventSourceImpl();
        m_dwLastUpdateTime = timeGetTime(); 
        m_pBufferSourceDelegate = new SoundBufferSourceDelegate(this);
    }

    // destructor
    ~DummySoundEngineImpl()
    {
    }

    // Rebuild the sound stage to reflect any recent changes in sound
    virtual HRESULT Update()
    {
        DWORD dwUpdateTime = timeGetTime();
        DWORD dwUpdatePeriod = dwUpdateTime - m_dwLastUpdateTime; 

        m_peventsourceUpdate->Trigger(dwUpdatePeriod);

        // loop through the sounds, deleting those that are no longer playing
        TList<TRef<DummySoundInstance> >::Iterator iterSound(m_listPlayingSounds);

        while (!iterSound.End())
        {
            if (iterSound.Value()->Update())
            {
                iterSound.Next();
            }
            else
            {
                iterSound.Remove();
            }
        }

        m_dwLastUpdateTime = dwUpdateTime;

        return S_OK;
    };

    // Gets a buffer source for this object (not guarenteed to keep the sound 
    // engine alive due to circular reference problems)
    virtual ISoundBufferSource* GetBufferSource()
    {
        return m_pBufferSourceDelegate;
    };

    // Gets the number of virtual sound buffers that are playing at a given 
    // moment.  (no guarantees on how this number changes - it's just a perf.
    // number to use.)
    virtual HRESULT GetNumPlayingVirtualBuffers(int& nBuffers)
    {
        nBuffers = m_listPlayingSounds.GetCount();

        return S_OK;
    };

    //
    // General environment functions
    //

    // Sets the listener to use for the current sounds
    HRESULT SetListener(ISoundListener* plistener)
    {
        TRef<ISoundListener> plistenerRef = plistener; // cycle the ref count
        return S_OK;
    };

    // Sets a general quality of playback (CPU time vs. fidelity)
    HRESULT SetQuality(Quality quality)
    {
        return S_OK;
    };

    // Allows/disallows hardware acceleration.
    HRESULT EnableHardware(bool bEnable)
    {
        return S_OK;
    }

    // Sets the conversion from game units to meters
    HRESULT SetDistanceFactor(float fMetersPerUnit)
    {
        return S_OK;
    }

    // Sets the rolloff factor, where 1.0 is the real world attenuation with 
    // distance, 2.0 is twice the attenuation of the real world, etc..
    HRESULT SetRolloffFactor(float fRolloffFactor)
    {
        return S_OK;
    }

    // Sets the doppler factor, where 1.0 is real-world doppler
    HRESULT SetDopplerFactor(float fDopplerFactor)
    {
        return S_OK;
    }


    //
    // ISoundBufferSource
    //

    // Creates a static sound buffer of the given wave file.  If bLooping is 
    // true, the sound will loop until stopped.
    virtual HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew, 
        ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource = NULL)
    {
        DummySoundInstance* dummySound = new DummySoundInstance(bLooping, psource);
        psoundNew = (ISoundInstance*)dummySound;
        m_listPlayingSounds.PushEnd(dummySound);
        return S_OK;
    };

    // Creates a sound buffer with a loop in the middle.  The sound will play
    // the start sound, play the loop sound until it gets a soft stop, then
    // play the rest of the sound.  
    virtual HRESULT CreateASRBuffer(TRef<ISoundInstance>& psoundNew, 
        ISoundPCMData* pcmdata, unsigned uLoopStart, unsigned uLoopLength, 
        ISoundPositionSource* psource = NULL)
    {
        DummySoundInstance* dummySound = new DummySoundInstance(true, psource);
        psoundNew = (ISoundInstance*)dummySound;
        m_listPlayingSounds.PushEnd(dummySound);
        return S_OK;
    };

    // Gets an event which fires each time update is called.  This can be used
    // for some of the trickier sounds that change with time.
    virtual IIntegerEventSource* GetUpdateEventSource()
    {
        return m_peventsourceUpdate;
    };
};


// create a dummy sound engine
HRESULT CreateDummySoundEngine(TRef<ISoundEngine>& psoundengine)
{
    psoundengine = new DummySoundEngineImpl();

    return S_OK;
}

}