
.namespace SoundPlayer

VoiceAutoDisable = true
DialogScriptForVoices = true
g_interncall = nil
	
function OnEnable()
    OPSoundManager.SetGlobalVolume(1)
end

--est ce que ce tableau sert encore à quelque choses
ExtensionArray=
{
	voice='.wav',
	interface='.wav',
	loop='.wav',
	Cutscenes='.wav',
	Gameplay='.wav',
	Navigation='.wav',
	Music='.ogg',
	Ambiances='.ogg',
	Interface = '.wav',
}

--on commence par les voice :)
StreamedSound=
{
	"voice",
	--"loop",
	"Music",
	"Cutscenes",
	"Ambiances",
	"Gameplay",
	"Outils",
	"Navigation",
}

--à revoir peut être ?
Prefixevoice =
{
	"INFO",
	"CS",
	"CONCLUSION",
	"DESC",
	"CHARACTER",
	"TOOL",
	"PLACE",
	"RELIC",
	"SCORING",
	"ANSWER",
	"TOPIC",
	"INTRO",
	"HELP",
	"TUTO",
}

-- est-ce que c'est une musique ?
Prefixemusic =
{
	"w01",
	"w02",
	"w03",
	"music_",
}

Prefixeambiance =
{
	"amb_",
	"loop_",
}

Prefixeinterface =
{
	"amb_",
}



Speaker =
{

}

LoopMode=
{ --mode par défaut
	voice=false,
	interface=false,
	Gameplay=false,
	loop=true,
	Music=true,
}

g_interncall = false


SoundCache = --tableau pour savoir le type, le volume ... des sons 
{
}

function PrefixOk(Name)
	for u,pref in pairs(Prefixevoice) do
		if string.find(Name,pref) then
			return true
		end
	end
	
	return false
end


function MusicOK(Name)
	for u,pref in pairs(Prefixemusic) do
		if string.find(Name,pref) then
			return true
		end
	end
	
	return false
end


function AmbianceOK(Name)
	for u,pref in pairs(Prefixeambiance) do
		if string.find(Name,pref) then
			return true
		end
	end
	
	return false
end

function InterfaceOK(name)
	return string.find(name,"int_")
end

function Play(Name, Erase, Delay, Loop)
	--on part du principe que tous les noms des sons sont différents
	--il n'y a pas d'ambiguité
	AssertPopup(Name~="" and Name~=nil, "un nom de son vide a été passé en paramètre")
	--OPLog.Log("appel de playsound "..Name.." "..tostring(Erase))
    
	--OPLog.Log("on joue le son "..Name)
	if (Delay == nil) then Delay = 0 end
	 
	local _isSound = false
	
	local sound = Load(Name, 'SimpleSound',true)
    

	if sound== nil then
		if SoundCache[Name] ~= nil then -- on load à la volée pour les streaming
			g_interncall = true
			PreloadSoundFct(SoundCache[Name].Type,Name, SoundCache[Name].Volume,SoundCache[Name].Loop,nil)
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
			--si dans le cache on a un utnotify on l'indique
			if SoundCache[Name].utnotify ~= nil then
				sound.SetUserUnitNotify(SoundCache[Name].utnotify)
			end
		elseif PrefixOk(Name)  then --on regarde si c un voice
			g_interncall = true
			PreloadSoundFct("voice",Name, 1,false,nil)
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
			g_lastvoice = sound
		elseif MusicOK(Name) then
			g_interncall = true
			PreloadSoundFct("Music",Name,ProfilConfig.Get("VolumeMusic") / 100,Loop,nil)
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
		elseif AmbianceOK(Name) then
			g_interncall = true
			PreloadSoundFct("Ambiances",Name, ProfilConfig.Get("VolumeMusic") / 100 ,nil,nil)
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
		elseif InterfaceOK(Name) then
			g_interncall = true
			PreloadSoundFct("Interface",Name, ProfilConfig.Get("VolumeSound") / 100,nil,nil) --TODO volume à intégrer
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
		else -- alors c'est un bruitage (gameplay)
			g_interncall = true
			PreloadSoundFct("Gameplay",Name, ProfilConfig.Get("VolumeSound") / 100,Loop,nil) --TODO volume à intégrer
			g_interncall = false
			sound = Load(Name, 'SimpleSound',true)
			--AssertPopup( false, 'son pas préchargé erreur '..Name)
			--return
		end
	elseif Erase == true and sound.Type ~= "voice" then
		Close(sound.Name)
		Play(Name, Erase, Delay, Loop)
		return
	end
	
	--stat son
	Statistics.Set("TotalNbSoundPlayed",Statistics.Get("TotalNbSoundPlayed")+1)
	
	if (Erase == nil) then 
		--si c'est un voice, c'est "erase" par défaut
		if (sound.Type == "voice") then
			Erase = true
		else
			Erase = false 
		end		
	end
	
	local b,soundbis=CheckAnotherDialogPlaying(sound)
    
	--si le path est nul, on appelle direct le onsoundend
	if sound.Sound == "" then
		AssertPopup(false, "l'unit existe mais pas le fichier son ! ".. sound.Name)
		sound.Finished = true
		sound.OnEndSound()
		return false
	end
	
	--[[
	if sound.GetMoveCamOnly() then
		SetVariables('CurrentGamePhase', 'MoveCamOnly')
	end
	--]]
	local soundwillbeplayed = false
	
	if (Erase == true) then
		if sound.Type == 'voice' and b then
			soundbis.Stop() --on arrete l'autre dialogue
			soundbis.Finished = true
			OPUnitFactory.CloseUnit(soundbis)
			soundbis = nil
			if SubtitlesDisplay.Enabled then
				SubtitlesDisplay.HideSubtitles()
			end 
		end
		sound.Stop()
		sound.Play()
		sound.Finished = false
		soundwillbeplayed = true
	else
        --on ne joue le voice que si il n'y en a pas d'autres
		if sound.Finished and not b then --on attend que le son soit fini
			sound.Play()
			sound.Finished = false
			soundwillbeplayed = true
		end
	end
	
	if sound.Type=='voice' then

		Enable("SubtitlesDisplay","Unit")
		g_curdialsound = sound
		g_needtodisplaysubtitle = true

	end
	
	return true
	
end

function OnUpdate()
	if (g_needtodisplaysubtitle) then
		SubtitlesDisplay.DisplaySubtitlesForSound(g_curdialsound.Name,g_curdialsound.Length)
		g_needtodisplaysubtitle = false
	end
end

function StopCurrentVoice()	
	if g_lastvoice ~= nil and not g_lastvoice== nil then
		g_lastvoice.Stop()
		if SubtitlesDisplay.Enabled then
			SubtitlesDisplay.HideSubtitles(g_lastvoice.Name)
		end
		OPUnitFactory.CloseUnit(g_lastvoice)
		g_lastvoice = nil
	end
end

function CheckAnotherDialogPlaying(sound)

	if sound.Type~='voice' then return false end
		
	g_result = false
	g_resultsound = nil
			
	local function CheckOneSound(ss)
		if ss.Type=='voice' and ss.Finished==false and ss.Speaker==sound.Speaker then 
			--OPLog.Log('voix déjà présente '..ss.Name)
			g_result =true
			g_resultsound = ss
		end	
	end
	
	ApplyToClass('SimpleSound',CheckOneSound)
	

	return g_result,g_resultsound
end

function Stop( inName )
    local lSound = OPUnitFactory.GetUnit(inName, 'SimpleSound')
    if( lSound~= nil ) then
        lSound.Stop()
	 else
	 	  lSound = OPUnitFactory.GetUnit(inName, 'Sound')
		  if (lSound~= nil) then
		     lSound.Stop()
		  end
    end
end

function Close( inName )
    local lSound = OPUnitFactory.GetUnit(inName, 'SimpleSound')
    if( lSound~= nil ) then
        lSound.Stop()
		KillUnit(lSound)
	 else
	 	  lSound = OPUnitFactory.GetUnit(inName, 'Sound')
		  if (lSound~= nil) then
		     lSound.Stop()
			 KillUnit(lSound)
		  end
    end
end

function CloseAllSoundFct()
    local ss,b = OPUnitFactory.GetFirstUnit('SimpleSound', 1)
    while (ss~= nil) do
        OPUnitFactory.CloseUnit(ss)
        ss = OPUnitFactory.GetNextUnit(b)
    end
end

function SetVoiceAutoDisableFct(_bool)
	if (_bool ~= nil) then
		VoiceAutoDisable = _bool
	end
end

function GetVoiceAutoDisableFct()
	return VoiceAutoDisable
end

function SetDialogScriptForVoicesFct(_bool)
	if (_bool ~= nil) then
		DialogScriptForVoices = _bool
	end
end

function GetDialogScriptForVoicesFct()
	return DialogScriptForVoices
end

function PreloadSoundFct(Type,Name, Volume,Loop,UnitNotify)

	--OPLog.Log("preload "..Type.." "..Name.." "..Volume)
	-- si c'est un streamed, on preload pas
	AssertPopup(type(Type) == "string" and type(Name) == "string", "erreur param sur preloadsound ")
	
	if Volume ~= nil then
		AssertPopup(Volume >=0 and Volume <=1, "Erreur Volume pas dans la fourchette : "..Type..' '..Name..' '..Volume..' '..' '..' '..tostring(Loop))
	end
	
	if IsIn(StreamedSound,Type) and g_interncall ~= true then
		SoundCache[Name] = {}
		SoundCache[Name].Type = Type
		SoundCache[Name].Volume = Volume
		SoundCache[Name].Loop = Loop
		return
	end

    local ut=Load(Name,'SimpleSound',true)
    
    if ut ~= nil then
        return
    end
	
	if Name=='' then
		return
	end
    
    if Loop==nil then Loop=LoopMode[Type] end
    if Volume==nil then
        Volume = 1
    end

    ss = OPUnitFactory.CreateUnit('SimpleSound')
	
	--depuis la 1.5 cette assignation est automatique
    --ss.Script = 'Script/Framework/EndSimpleSound.lua'
    ss.AutoDisable = false
		
	if (UnitNotify ~= nil) then
		--OPLog.Log(' ...... On set le unit notify')
		ss.UnitNotify = UnitNotify
	end
	
    ss.Loop = Loop
	
	--tout est en streamed, c'est tranché !
	
	--pour l'instant true -> saccade et tps de chargement court
	-- false -> pas de saccade, tps de chargment long
	if OPCore.GetPlatform() == "PC" or Type == "Gameplay" or Type == "Outils"  then
		ss.Streamed = false
	else
		ss.Streamed = false --temporairement le onsoundend n'est pas appelé
	end
    	
	ss.Name = Name
	
    --ss.Volume = gf.ApplyCoeffOnVolume(Volume, opt_SoundVolume)
    ss.Volume=Volume
    ss.Type=Type --pour traiter les voix par la suite
    
	if ss.Type == "voice" then
		ss.Speaker = string.lower(string.sub(Name,0,string.len(Name)-4))
	end
	
	AssertPopup(ExtensionArray[Type] ~= nil , "Erreur type son")
	
	local extension
	if OPCore.GetPlatform() == "PC" then
		extension = ".wav"
	else
		extension = ".opsnd"
	end
	
	if Type == "voice" then
    	ss.Sound = 'media/'..Type..'/'..Name..ExtensionArray[Type]
	else
		ss.Sound = 'media/sound/'..Type..'/'..Name..ExtensionArray[Type]
	end
	
	-- vieux code je commente 
	--[[ --si le .ogg n'existe pas, on met le .wav à la place
	--c'est pour l'intégration
	if not CheckPath(ss.Sound) then
		ss.Sound = 'media/'..Type..'/'..Name..".wav"
		if not CheckPath(ss.Sound)  then
			ss.Sound = ""
		end
	end]]
	
    ss.Finished=true
end

function CheckPath(pathtocheck)
	--local path = OPFileManager.GetOPLPath("Main")
	--OPLog.Log("oplpath "..path)	
	local foundFiles = OPFileManager.FindFiles(pathtocheck, SORT_FIND_FILES_BY_DATE)
	local nbFiles = table.getn(foundFiles)
	if (nbFiles == 0) then
		return false
	end
	return true
end

function SetUserNotify(soundname,ut)

	SoundCache[soundname].utnotify = ut
end

function OnSoundEnd(name)
	if g_lastvoice ~= nil and name == g_lastvoice.Name then
		g_lastvoice = nil
	end
	
	sound = Load(name,"SimpleSound", true)
	--si c'est un streamed sound, on le vire
	if not sound== nil and IsIn(StreamedSound,sound.Type) then
		--TODO : clean des sons au changement d'écran
		--OPUnitFactory.CloseUnit(sound)
	end
end

--on a rset le jeu
function OnDisable()
	ApplyToClass("SimpleSound",OPUnitFactory.CloseUnit)
end

function GetInfo(soundname)
	return SoundCache[soundname]
end


function SetAmbianceInfos(soundName, volume, loop, firstDelay, replayDelayMin, replayDelayMax)
	local sound = OPUnitFactory.GetUnit(soundName, "SimpleSound")
	AssertPopup( sound.Type == "Ambiances", "Attention, appel de la fonction SetAmbianceInfos sur un son d'un mauvais type" )
	
	sound.Loop = loop
	sound.Volume = volume
	sound.FirstDelay = firstDelay
	sound.ReplayDelayMin = replayDelayMin
	sound.ReplayDelayMax = replayDelayMax
	sound.Timer = 0
end


function SetInfo(soundname, volume, loop)
	SoundCache[soundname].Volume = volume
	SoundCache[soundname].Loop = loop
end


function GetSoundCache()
	return SoundCache
end


function CleanSoundCache()
	local tabIdToRemove = {}
	local iter = 1
	
	for i, sound in pairs(SoundCache) do
		if sound == nil then
			tabIdToRemove[iter] = i
			iter = iter + 1
		end
	end
	
	for i, id in pairs(tabIdToRemove) do
		table.remove(SoundCache, id)
	end
end


------------------------------------------------------------------------------------
------------------------------ gestion ambiance ----------------------------------
------------------------------------------------------------------------------------

function PlayAmbiance( ambiance )
	if ambiance == nil then
		OPLog.Log("pas d'ambiance pour cet écran")
		return
	end
	
	AssertPopup( ambiance.soundTab ~= nil and table.maxn(ambiance.soundTab) > 0, "Erreur, aucun son présent dans le tableau" )
	
	local soundName = ambiance.soundTab[1]
	if table.maxn(ambiance.soundTab) > 1 then
		soundName = ambiance.soundTab[ math.random(1,table.maxn(ambiance.soundTab)) ]
	end
	
	local volume
	if ambiance.volume == nil then
		volume = 0.66 * ProfilConfig.Get("VolumeMusic") / 100		
	else
		volume = ambiance.volume * ProfilConfig.Get("VolumeMusic") / 100
	end
	
	OPLog.Log("volume "..volume.." nom "..soundName)
	PreloadSoundFct("Ambiances",soundName,0,ambiance.loop,nil)
	
	Play(soundName, false)
	SoundFader.Fade(unit(soundName),0.02,volume)
	
end

function StopAmbiance( ambianceName )
	if ambianceName == nil or ambianceName == "" then
		OPLog.Log("erreur, impossible d'arrêter une ambiance sans nom")
		return
	end
	
	AssertPopup( AmbianceOK(ambianceName) == true, "Erreur, vous essayez d'arrêter une ambiance qui n'en est pas une" )
	--Stop(ambianceName)
	SoundFader.Fade(unit(ambianceName),0.02,0,nil,true)
	--OPUnitFactory.CloseUnit(ambianceName,'SimpleSound')
end


function StopAllAmbiances()
	local unit,b=OPUnitFactory.GetFirstUnit("SimpleSound",1)
	while (unit~= nil) do
		if unit.Type == "Ambiances" then
			StopAmbiance(unit.Name)
		end
		unit=OPUnitFactory.GetNextUnit(b)
	end
end

-- fonction écrite pour lancer une musique (une seule musique à la fois)
function PlayMusic(sound, erase, loop, unitToNotify)
	if sound == nil or sound == "" then
		OPLog.Log("un son vide n'a pas pu être joué")
		return
	end
	
	AssertPopup( MusicOK(sound) == true, "Erreur, vous essayez de lancer une musique qui n'a pas le bon préfixe" )
	PlaySoundEffect(sound, erase, nil, unitToNotify, loop)
	SetVariables("CurrentMusic",sound)
	
	if unitToNotify ~= nil and OPUnitFactory.GetUnit(sound, 'SumpleSound', true)~= nil then
		OPUnitFactory.GetUnit(sound, 'SimpleSound').SetUserUnitNotify(unitToNotify)
	end
end

function StopMusic(sound)
	if sound == nil or sound == "" then
		OPLog.Log("erreur, impossible d'arrêter une musique sans nom")
		return
	end
	
	AssertPopup( MusicOK(sound) == true, "Erreur, vous essayez d'arrêter une musique qui n'en est pas une" )
	Stop(sound)
	OPUnitFactory.CloseUnit(sound,'SimpleSound')
	SetVariables('CurrentMusic',"")
end

function StopAllToolSounds()
	local unit,b=OPUnitFactory.GetFirstUnit("SimpleSound",1)
	while (unit~= nil) do
		if unit.Type == "Outils" then
			Stop(unit.Name)
			OPUnitFactory.CloseUnit(unit)
			OPLog.Log("Son outil arrêté")
		end
		unit=OPUnitFactory.GetNextUnit(b)
	end
end


--[[ Joue un son tiré aléatoirement dans le tableau tab
]]--
function PlayRandom(tab,erase,delay)
    AssertPopup(type(tab) == "table", "erreur params")
    local n = math.random(table.getn(tab))
    Play(tab[n],erase,delay)
    return Load(tab[n],'SimpleSound',true) -- On retourne l'unit pour pouvoir lui faire un setnotify
    
end

