///////////////////////////////////////////////////////////////////////////////
/// @file SoundFXManager.cpp
///
/// @brief Destiny Sound System Sound FX Manager
///
/// @author paradoxnj
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jan 2002
/// @author paradoxnj
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <dsound.h>

#include "Channels.hpp"
#include "deSound.hpp"

deSFXManager::deSFXManager()
{
	m_pBuffer = NULL;

	m_pGargle = NULL;
	m_pChorus = NULL;
	m_pFlanger = NULL;
	m_pEcho = NULL;
	m_pDistortion = NULL;
	m_pCompressor = NULL;
	m_pParamEq = NULL;
	m_pReverb = NULL;

	m_NumEffects = 0;
	m_NumActiveBufs = 0;
}

deSFXManager::~deSFXManager()
{
	Destroy();
}

deBoolean deSFXManager::Initialize(IDirectSoundBuffer *buffer)
{
	HRESULT				hres;

	hres = buffer->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&m_pBuffer);
	if (FAILED(hres))
		return deFALSE;
	
	for (DWORD i = DESFX_TYPE_CHORUS; i < DESFX_ENUM; i++)
		EnableFX((deSFX_Type)i);

	if (!ActivateFX())
		return deFALSE;

	m_pGargle->GetAllParameters(&m_GargleSettings);
	m_pChorus->GetAllParameters(&m_ChorusSettings);
	m_pFlanger->GetAllParameters(&m_FlangerSettings);
	m_pEcho->GetAllParameters(&m_EchoSettings);
	m_pDistortion->GetAllParameters(&m_DistortionSettings);
	m_pCompressor->GetAllParameters(&m_CompressorSettings);
	m_pParamEq->GetAllParameters(&m_ParamEqSettings);
	m_pReverb->GetAllParameters(&m_ReverbSettings);

	if (!DisableFX())
		return deFALSE;

	return deTRUE;
}

void deSFXManager::Destroy()
{
	DisableFX();
	DE3D_SAFE_RELEASE(m_pBuffer);
}

void deSFXManager::GetGargleSettings(deSFX_Gargle *gargle)
{
	ZeroMemory(gargle, sizeof(deSFX_Gargle));

	gargle->m_RateHz = m_GargleSettings.dwRateHz;
	gargle->m_WaveShape = m_GargleSettings.dwWaveShape;
}

void deSFXManager::GetChorusSettings(deSFX_Chorus *chorus)
{
	ZeroMemory(chorus, sizeof(deSFX_Chorus));

	chorus->m_WetDryMix = m_ChorusSettings.fWetDryMix;
	chorus->m_Depth = m_ChorusSettings.fDepth;
	chorus->m_Feedback = m_ChorusSettings.fFeedback;
	chorus->m_Frequency = m_ChorusSettings.fFrequency;
	chorus->m_WaveForm = m_ChorusSettings.lWaveform;
	chorus->m_Delay = m_ChorusSettings.fDelay;
	chorus->m_Phase = m_ChorusSettings.lPhase;
}

void deSFXManager::GetFlangerSettings(deSFX_Flanger *flanger)
{
	ZeroMemory(flanger, sizeof(deSFX_Flanger));

	flanger->m_WetDryMix = m_FlangerSettings.fWetDryMix;
	flanger->m_Depth = m_FlangerSettings.fDepth;
	flanger->m_FeedBack = m_FlangerSettings.fFeedback;
	flanger->m_Frequency = m_FlangerSettings.fFrequency;
	flanger->m_WaveForm = m_FlangerSettings.lWaveform;
	flanger->m_Delay = m_FlangerSettings.fDelay;
	flanger->m_Phase = m_FlangerSettings.lPhase;
}

void deSFXManager::GetEchoSettings(deSFX_Echo *echo)
{
	ZeroMemory(echo, sizeof(deSFX_Echo));

	echo->m_WetDryMix = m_EchoSettings.fWetDryMix;
	echo->m_Feedback = m_EchoSettings.fFeedback;
	echo->m_LeftDelay = m_EchoSettings.fLeftDelay;
	echo->m_RightDelay = m_EchoSettings.fRightDelay;
	echo->m_PanDelay = m_EchoSettings.lPanDelay;
}

void deSFXManager::GetDistortionSettings(deSFX_Distortion *distortion)
{
	ZeroMemory(distortion, sizeof(deSFX_Distortion));

	distortion->m_Gain = m_DistortionSettings.fGain;
	distortion->m_Edge = m_DistortionSettings.fEdge;
	distortion->m_PostEQCenterFrequency = m_DistortionSettings.fPostEQCenterFrequency;
	distortion->m_PostEQBandwidth = m_DistortionSettings.fPostEQBandwidth;
	distortion->m_PreLowPassCutoff = m_DistortionSettings.fPreLowpassCutoff;
}

void deSFXManager::GetCompressorSettings(deSFX_Compressor *compressor)
{
	ZeroMemory(compressor, sizeof(deSFX_Compressor));

	compressor->m_Gain = m_CompressorSettings.fGain;
	compressor->m_Attack = m_CompressorSettings.fAttack;
	compressor->m_Release = m_CompressorSettings.fRelease;
	compressor->m_Threshold = m_CompressorSettings.fThreshold;
	compressor->m_Ratio = m_CompressorSettings.fRatio;
	compressor->m_Predelay = m_CompressorSettings.fPredelay;
}

void deSFXManager::GetParamEqSettings(deSFX_ParamEQ *param_eq)
{
	ZeroMemory(param_eq, sizeof(deSFX_ParamEQ));

	param_eq->m_Center = m_ParamEqSettings.fCenter;
	param_eq->m_Bandwidth = m_ParamEqSettings.fBandwidth;
	param_eq->m_Gain = m_ParamEqSettings.fGain;
}

void deSFXManager::GetReverbSettings(deSFX_Reverb *reverb)
{
	ZeroMemory(reverb, sizeof(deSFX_Reverb));

	reverb->m_InGain = m_ReverbSettings.fInGain;
	reverb->m_ReverbMix = m_ReverbSettings.fReverbMix;
	reverb->m_ReverbTime = m_ReverbSettings.fReverbTime;
	reverb->m_HighFreqRTRatio = m_ReverbSettings.fHighFreqRTRatio;
}

void deSFXManager::SetGargleSettings(deSFX_Gargle *gargle)
{
	m_GargleSettings.dwRateHz = gargle->m_RateHz;
	m_GargleSettings.dwWaveShape = gargle->m_WaveShape;
}

void deSFXManager::SetChorusSettings(deSFX_Chorus *chorus)
{
	m_ChorusSettings.fWetDryMix = chorus->m_WetDryMix;
	m_ChorusSettings.fDepth = chorus->m_Depth;
	m_ChorusSettings.fFeedback = chorus->m_Feedback;
	m_ChorusSettings.fFrequency = chorus->m_Frequency;
	m_ChorusSettings.lWaveform = chorus->m_WaveForm;
	m_ChorusSettings.fDelay = chorus->m_Delay;
	m_ChorusSettings.lPhase = chorus->m_Phase;
}

void deSFXManager::SetFlangerSettings(deSFX_Flanger *flanger)
{
	m_FlangerSettings.fWetDryMix = flanger->m_WetDryMix;
	m_FlangerSettings.fDepth = flanger->m_Depth;
	m_FlangerSettings.fFeedback = flanger->m_FeedBack;
	m_FlangerSettings.fFrequency = flanger->m_Frequency;
	m_FlangerSettings.lWaveform = flanger->m_WaveForm;
	m_FlangerSettings.fDelay = flanger->m_Delay;
	m_FlangerSettings.lPhase = flanger->m_Phase;
}

void deSFXManager::SetEchoSettings(deSFX_Echo *echo)
{
	m_EchoSettings.fWetDryMix = echo->m_WetDryMix;
	m_EchoSettings.fLeftDelay = echo->m_LeftDelay;
	m_EchoSettings.fRightDelay = echo->m_RightDelay;
	m_EchoSettings.fFeedback = echo->m_Feedback;
	m_EchoSettings.lPanDelay = echo->m_PanDelay;
}

void deSFXManager::SetDistortionSettings(deSFX_Distortion *distortion)
{
	m_DistortionSettings.fGain = distortion->m_Gain;
	m_DistortionSettings.fEdge = distortion->m_Edge;
	m_DistortionSettings.fPostEQBandwidth = distortion->m_PostEQBandwidth;
	m_DistortionSettings.fPostEQCenterFrequency = distortion->m_PostEQCenterFrequency;
	m_DistortionSettings.fPreLowpassCutoff = distortion->m_PreLowPassCutoff;
}

void deSFXManager::SetCompressorSettings(deSFX_Compressor *compressor)
{
	m_CompressorSettings.fGain = compressor->m_Gain;
	m_CompressorSettings.fAttack = compressor->m_Attack;
	m_CompressorSettings.fRelease = compressor->m_Release;
	m_CompressorSettings.fThreshold = compressor->m_Threshold;
	m_CompressorSettings.fPredelay = compressor->m_Predelay;
	m_CompressorSettings.fRatio = compressor->m_Ratio;
}

void deSFXManager::SetParamEqSettings(deSFX_ParamEQ *param_eq)
{
	m_ParamEqSettings.fCenter = param_eq->m_Center;
	m_ParamEqSettings.fBandwidth = param_eq->m_Bandwidth;
	m_ParamEqSettings.fGain = param_eq->m_Gain;
}

void deSFXManager::SetReverbSettings(deSFX_Reverb *reverb)
{
	m_ReverbSettings.fInGain = reverb->m_InGain;
	m_ReverbSettings.fReverbMix = reverb->m_ReverbMix;
	m_ReverbSettings.fReverbTime = reverb->m_ReverbTime;
	m_ReverbSettings.fHighFreqRTRatio = reverb->m_HighFreqRTRatio;
}

deBoolean deSFXManager::EnableFX(deSFX_Type type)
{
	if (m_NumEffects >= DESFX_ENUM)
		return deFALSE;

	switch (type)
	{
	case DESFX_TYPE_GARGLE:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_GARGLE;
			
			m_RefID[m_NumEffects] = &IID_IDirectSoundFXGargle;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pGargle;

			break;
		}
	case DESFX_TYPE_CHORUS:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_CHORUS;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXChorus;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pChorus;

			break;
		}
	case DESFX_TYPE_FLANGER:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_FLANGER;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXFlanger;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pFlanger;

			break;
		}
	case DESFX_TYPE_ECHO:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_ECHO;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXEcho;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pEcho;

			break;
		}
	case DESFX_TYPE_DISTORTION:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_DISTORTION;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXDistortion;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pDistortion;

			break;
		}
	case DESFX_TYPE_COMPRESSOR:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_COMPRESSOR;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXCompressor;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pCompressor;

			break;
		}
	case DESFX_TYPE_PARAMEQ:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_STANDARD_PARAMEQ;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXParamEq;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pParamEq;

			break;
		}
	case DESFX_TYPE_REVERB:
		{
			ZeroMemory(&m_EffectDesc[m_NumEffects], sizeof(DSEFFECTDESC));

			m_EffectDesc[m_NumEffects].dwSize = sizeof(DSEFFECTDESC);
			m_EffectDesc[m_NumEffects].dwFlags = 0;
			m_EffectDesc[m_NumEffects].guidDSFXClass = GUID_DSFX_WAVES_REVERB;

			m_RefID[m_NumEffects] = &IID_IDirectSoundFXWavesReverb;
			m_pIntPtr[m_NumEffects] = (LPVOID*)&m_pReverb;

			break;
		}
	default:
		return deFALSE;
	}

	m_NumEffects++;

	return deTRUE;
}

deBoolean deSFXManager::ActivateFX()
{
	DWORD			results[DESFX_ENUM];
	HRESULT			hres;
	DWORD			i;

	if (m_NumEffects <= 0)
		return deFALSE;

	hres = m_pBuffer->SetFX(m_NumEffects, m_EffectDesc, results);
	if (FAILED(hres))
		return deFALSE;

	for (i = 0; i < m_NumEffects; i++)
	{
		hres = m_pBuffer->GetObjectInPath(m_EffectDesc[i].guidDSFXClass, 0, *m_RefID[i], m_pIntPtr[i]);
		if (FAILED(hres))
			return deFALSE;
	}

	if (m_pGargle)
	{
		m_pGargle->SetAllParameters(&m_GargleSettings);
	}

	if (m_pChorus)
	{
		m_pChorus->SetAllParameters(&m_ChorusSettings);
	}

	if (m_pFlanger)
	{
		m_pFlanger->SetAllParameters(&m_FlangerSettings);
	}

	if (m_pEcho)
	{
		m_pEcho->SetAllParameters(&m_EchoSettings);
	}

	if (m_pDistortion)
	{
		m_pDistortion->SetAllParameters(&m_DistortionSettings);
	}

	if (m_pCompressor)
	{
		m_pCompressor->SetAllParameters(&m_CompressorSettings);
	}

	if (m_pParamEq)
	{
		m_pParamEq->SetAllParameters(&m_ParamEqSettings);
	}

	if (m_pReverb)
	{
		m_pReverb->SetAllParameters(&m_ReverbSettings);
	}

	return deTRUE;
}

deBoolean deSFXManager::DisableFX()
{
	DE3D_SAFE_RELEASE(m_pGargle);
	DE3D_SAFE_RELEASE(m_pChorus);
	DE3D_SAFE_RELEASE(m_pFlanger);
	DE3D_SAFE_RELEASE(m_pEcho);
	DE3D_SAFE_RELEASE(m_pDistortion);
	DE3D_SAFE_RELEASE(m_pCompressor);
	DE3D_SAFE_RELEASE(m_pParamEq);
	DE3D_SAFE_RELEASE(m_pReverb);

	m_NumEffects = 0;

	if (IsPlaying())
		Stop();

	m_pBuffer->SetFX(0, NULL, NULL);
	return deTRUE;
}

deBoolean deSFXManager::IsPlaying()
{
	DWORD			status;
	
	if (FAILED(m_pBuffer->GetStatus(&status)))
		return deFALSE;

	if (status & DSBSTATUS_PLAYING)
		return deTRUE;
	else
		return deFALSE;
}

deBoolean deSFXManager::Play(deBoolean loop)
{
	if (m_pBuffer != NULL)
	{
		if (!ActivateFX())
			return deFALSE;
	
		if (loop)
			m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
		else
			m_pBuffer->Play(0, 0, 0);
	}

	return deTRUE;
}

void deSFXManager::Stop()
{
	m_pBuffer->Stop();
	m_pBuffer->SetCurrentPosition(0);
}

void deSFXManager::SetVolume(float vol)
{
	LONG			dsVol;

	dsVol = (LONG)(vol * (float)(DSBVOLUME_MAX - DSBVOLUME_MIN) + DSBVOLUME_MIN);

	m_pBuffer->SetVolume(dsVol);
}
