class FlashbangEffect
{
	protected const float 						ALPHA_MIN = 0.0;
	protected const float 						ALPHA_MAX = 1.0;
	
	protected const float 						SOUND_DEFER_TIME = 0.4;			//! SFX will be played ~0.5s AFTER VFX 

	protected float 							m_HitDuration;
	protected float 							m_BreakPoint;
	protected float 							m_TimeActive;
	protected float								m_DayTimeToggle;
	
	protected float								m_AlphaMaxActual; //actual max alpha of the effect
	protected float								m_SoundMaxActual; //actual max volume of the sound
	protected float								m_ProgressMultiplier; 
	
	protected bool								m_Visual;
	protected bool 								m_Initialized;
	
	protected PlayerBase 						m_Player;
	protected EffectSound 						m_FlashbangEffectSound;
	protected float 							m_SoundStopTime;
	
	protected ref Timer							m_DeferAttenuation;
	
	protected PPERequester_FlashbangEffects 	m_Requester;
	
	void FlashbangEffect(PlayerBase player, bool visual = true)
	{
		m_Player = player;
		m_Visual = visual;
		m_Initialized = false;

		m_HitDuration = 8.0;
		m_BreakPoint = 2.5;
		m_AlphaMaxActual = ALPHA_MAX;
		m_SoundMaxActual = 1.0;
		m_ProgressMultiplier = 1.0;
		
		m_FlashbangEffectSound = null;
		
		if (m_Visual)
		{
			Class.CastTo(m_Requester,PPERequesterBank.GetRequester(PPERequester_FlashbangEffects));
			m_Requester.Start();
		}
		
		m_DeferAttenuation = new Timer();
		m_DeferAttenuation.Run(SOUND_DEFER_TIME, this, "PlaySound", null, false);
		
		//! naive time of the day selector
		m_DayTimeToggle = 5; //! -1: night; 1: day
		if ( g_Game.GetDayTime() >= 22.0 || g_Game.GetDayTime() < 7.0 )
		{
			m_DayTimeToggle = 10;
		}
	}
	
	void ~FlashbangEffect()
	{
		if ( m_Visual )
		{
			ClearVisual();
		}
		
		if ( m_Player )
		{
			m_Player.OnPlayerReceiveFlashbangHitEnd();
		}

		if ( m_DeferAttenuation.IsRunning() )
		{
			m_DeferAttenuation.Stop();
		}
		
		m_DeferAttenuation = null;
		SEffectManager.DestroyEffect(m_FlashbangEffectSound);
	}
	
	void SetupFlashbangValues(float progress_mult = 1.0, float visual_value_max = 1.0, float sound_value_max = 1.0)
	{
		if ( !m_Initialized )
		{
			m_Initialized = true;
			m_ProgressMultiplier = progress_mult;
			m_AlphaMaxActual = visual_value_max;
			m_SoundMaxActual = sound_value_max;
			
			m_HitDuration *= m_ProgressMultiplier;
			m_BreakPoint *= m_ProgressMultiplier;
		}
	}
	
	protected void PlaySound()
	{
		if ( !m_Initialized )
		{
			Error("" + this + " not initialized");
			return;
		}
		
		vector pos;
		MiscGameplayFunctions.GetHeadBonePos(m_Player, pos);
		
		if (!m_FlashbangEffectSound)
		{
			m_FlashbangEffectSound = SEffectManager.CreateSound("Tinnitus_SoundSet", pos);
		}
		
		if (!m_FlashbangEffectSound.IsPlaying())
		{
			m_FlashbangEffectSound.SetParent(m_Player);
			m_FlashbangEffectSound.SetAttachedLocalPos(m_Player.WorldToModel(pos));
			m_FlashbangEffectSound.SetSoundWaveKind(WaveKind.WAVEEFFECTEX);
			m_FlashbangEffectSound.SetSoundFadeIn(4 * Math.Clamp(m_ProgressMultiplier,0.5,1.0)); //TODO
			m_SoundStopTime = 2 * Math.Clamp(m_ProgressMultiplier,0.5,1.0);
			m_FlashbangEffectSound.SetSoundFadeOut(m_SoundStopTime); //TODO
			m_FlashbangEffectSound.SetSoundMaxVolume(Math.Clamp(m_SoundMaxActual,0.1,1.0)); //TODO
			m_FlashbangEffectSound.SetSoundLoop(true);
			m_FlashbangEffectSound.SoundPlay();
			m_FlashbangEffectSound.SetAutodestroy(true);
			
			SetAttenuationFilter();
		}
	}

	protected void SetAttenuationFilter()
	{
		if ( !m_DeferAttenuation.IsRunning() || m_Player.GetMasterAttenuation() != "FlashbangAttenuation" )
		{
			m_Player.SetMasterAttenuation("FlashbangAttenuation");
		}
	}
	
	protected void ResetAttenuationFilter()
	{
		m_Player.SetMasterAttenuation("");
	}
	
	protected void StopSound()
	{
		if (m_FlashbangEffectSound)
		{
			m_FlashbangEffectSound.SoundStop();
			SEffectManager.DestroyEffect(m_FlashbangEffectSound);
		}
	}
	
	protected void ClearVisual()
	{
		if (m_Requester)
		{
			m_Requester.Stop();
		}
	}
	
	protected void SetVisual(float val)
	{
		if (m_Requester && m_Requester.IsRequesterRunning())
		{
			m_Requester.SetFlashbangIntensity(val, m_DayTimeToggle);
		}
	}
	
	void Stop()
	{
		StopSound();
	}
	
	void Update(float deltatime)
	{
		if ( !m_Initialized )
		{
			Error("" + this + " not initialized");
		}
		else if ( m_Visual )
		{
			float value;

			if ( m_TimeActive <= m_BreakPoint )
			{
				value = m_AlphaMaxActual;
				//Print("Flashbango | m_AlphaMaxActual: " + value);
			}
			else
			{
				value = Math.InverseLerp(m_HitDuration - m_BreakPoint, m_HitDuration, m_TimeActive);
				value = Math.Clamp(value,0.0,1.0);
				value = m_AlphaMaxActual - value * m_AlphaMaxActual;
				//Print("Flashbango | tmp_value: " + value);
			}
			SetVisual(value);
		}

		m_TimeActive += deltatime;

		if (m_TimeActive >= m_HitDuration - m_SoundStopTime)
		{
			StopSound();
		}
		
		if (m_TimeActive >= m_HitDuration)
		{
			ResetAttenuationFilter();
			delete this;
		}
	}
}