-- music.lua
-- this will be where the serverside music enqueueing system will be housed.
-- the timer's name will be "songTimer"
-- has 2 levels of sound handling, see admin_playtoall documentation

local MusicList = {};

local soundPlayer = {};

/*
ambience1.mp3 - 3:20
ambience2.mp3 - 4:25
ambience3.mp3 - 3:23
ambience4.mp3 - 3:27
battle1.mp3 - 5:20
battle2.mp3 - 2:35
emotion1.mp3 - 2:29
emotion2.mp3 - 3:25
emotion3.mp3 - 4:36
radio1.mp3 - 4:14
radio2.mp3 - 2:37
radio3.mp3 - 1:37
maria.mp3 - 4:16
canada.mp3 - 1:13
piano.mp3 - 2:32
*/
-- song name, time in seconds
-- essentially a lookup table
MusicList["ambience1.mp3"] = 200;
MusicList["ambience2.mp3"] = 445;
MusicList["ambience3.mp3"] = 203;
MusicList["ambience4.mp3"] = 207;
MusicList["battle1.mp3"] = 320;
MusicList["battle2.mp3"] = 155;
MusicList["emotion1.mp3"] = 149;
MusicList["emotion2.mp3"] = 205;
MusicList["emotion3.mp3"] = 276;
MusicList["radio1.mp3"] = 254;
MusicList["radio2.mp3"] = 157;
MusicList["radio3.mp3"] = 97;
MusicList["maria.mp3"] = 256;
MusicList["canada.mp3"] = 73;
MusicList["piano.mp3"] = 152;

MusicList["Gundown.mp3"] = 144;
MusicList["Pyre.mp3"] = 143;
MusicList["yuma.mp3"] = 127;

-- table that acts as queue of songs, index of 1 is current song to use
local SongList = {};
-- string for the song being played, no longer in the songList
local currentSong = "";
-- if the timer for fading out the song exists this is true
local timerExists = false;
-- object that points to the CSoundPatch to modify its volume level
local theSound = nil;

-- empties the queue of songs
local function ClearSongs( ply, cmd, args )

	if( IsConsole(ply) or ply:IsAdmin() or ply:IsSuperAdmin()  ) then

		MusicList = {};
		CAKE.Notify( ply, "Song queue cleared.");
		
	end

end
concommand.Add("admin_clear_songs", ClearSongs);

-- Sends the currently playing song, then the entire music queue to the admin
-- used by gui
local function SendMusicQueue( ply, cmd, args )

	if( ply:IsAdmin() or ply:IsSuperAdmin() ) then
	
--Msg("Sending queue.\n");
	
		umsg.Start("AddPlayingItem", ply);
			umsg.String( currentSong );
		umsg.End();
		
		for k, v in pairs( SongList ) do
		
			umsg.Start("AddQueueSong",ply);
				umsg.String( v );
			umsg.End();
		
		end

	end

end
concommand.Add( "admin_get_song_list", SendMusicQueue );

-- Sends all known songs, should only be called once by admin per connect
-- used by gui
local function SendAllSongs( ply, cmd, args )

	if( ply:IsAdmin() or ply:IsSuperAdmin() ) then

		for k, v in pairs( MusicList ) do
		
			umsg.Start("AddMusicItem",ply);
				umsg.String(k);
			umsg.End();
		
		end
		
		-- done sending items, recall their menu, aren't I nice?
		umsg.Start("openAdminMenu",ply);
		umsg.End();
		
	end

end
concommand.Add( "admin_get_all_songs", SendAllSongs );

local fadeAmount = 0;
local function DoFadeOut()

--Msg("fadeout called.\n");

	if( timerExists and fadeAmount > .01 and theSound != nil ) then
	
		-- 10 seconds of .01
		if( fadeAmount < .10 ) then
			fadeAmount = fadeAmount - .01;
		-- 4 seconds of .1
		elseif( fadeAmount < .5 ) then
			fadeAmount = fadeAmount - .1;
		-- 6 seconds of .08333
		else
			fadeAmount = fadeAmount - .08333;
		end
		
		if( theSound[1] != nil ) then
			--reset sound volume to 100%
			theSound:ChangeVolume( fadeAmount );
		end
		
--Msg("Setting volume to: " .. fadeAmount .. "\n");
		
		-- fade in by 5 more every second, so we fade in to 100% after 20 seconds
		timer.Simple( 1, DoFadeOut );
	
	-- fading is done, start next song
	elseif( timerExists and fadeAmount < .01 ) then
	
		-- more songs to play
		if( SongList[1] != nil ) then
		
			timer.Destroy( "songTimer" );
			
			timerExists = false;
			
			-- get the next song going
			-- must use a concommand because function is defined after this fucntion, and they use each other
			-- what a pain it is.
			RunConsoleCommand( "admin_resumeMusic" );
		
		-- no songs left to play, switch to no song mode
		else
		
			timer.Destroy( "songTimer" );
			timerExists = false;
			currentSong = "";
			
			if( theSound[1] != nil ) then
				--reset sound volume to 100%
				theSound:ChangeVolume( 1 );
			end
			
			theSound = {};
			
		end	
	
	end

end

-- will change the volume for 20 seconds, working its way up to 100%
local function DoFadeIn()

	if( timerExists and fadeAmount < 1 and theSound != nil ) then
	
		-- 10 seconds of .01
		if( fadeAmount < .10 ) then
			fadeAmount = fadeAmount + .01;
		-- 4 seconds of .1
		elseif( fadeAmount < .5 ) then
			fadeAmount = fadeAmount + .1;
		-- 6 seconds of .08333
		else
			fadeAmount = fadeAmount + .08333;
		end
		
		if( theSound[1] != nil ) then
			--reset sound volume to 100%
			theSound:ChangeVolume( fadeAmount );
		end
		
--Msg("Setting volume to: " .. fadeAmount .. "\n");
		
		-- fade in by 5 more every second, so we fade in to 100% after 20 seconds
		timer.Simple( 1, DoFadeIn );
	
	end

end

-- Use this to start the music.
local function StartMusic()

/*
Msg("Resuming the music.\n");

if( timerExists ) then
	Msg("timer exists is: true \n");
else
	Msg("timer exists is: false \n");
end

for k, v in pairs( SongList ) do
	Msg("k is: " .. k .. " v is: " .. v .. "\n");
end
*/
	
	--entSoundPlayer();
	

	if( ! timerExists and SongList[1] != nil ) then
	
--Msg("Test passed.\n");
	
		timerExists = true;
		-- start fade out at 20 before end. 
		timer.Create( "songTimer", MusicList[ SongList[1] ] - 20, 1, DoFadeOut );
		
		-- Using the @ symbol from valve documentation, proved working with the play concommand
		-- FROM VALVE WIKI DOCUMENTATION http://developer.valvesoftware.com/wiki/Soundscripts#SoundLevel_Flags
		-- @ - CHAR_OMNI
		-- Non-directional; audible everywhere. "Default mono or stereo", whatever that means. 
		currentSong = SongList[1];
		
		fadeAmount = 0;
		theSound = {};
		for k, v in pairs( player.GetAll() ) do
		
			local tempSound = CreateSound( v, "@" .. SongList[1] );
			tempSound:SetSoundLevel(SNDLVL_180dB);
			tempSound:ChangeVolume( 0 );
			
			table.insert( theSound, tempSound );
		
		end
		
		-- attempt to minimize the echo effect
		for k, v in pairs( theSound ) do
		
			v:Play();
		
		end

		DoFadeIn();
		table.remove( SongList, 1 );
		
--Msg("reached end of resume code.\n");
			
	end

end
concommand.Add( "admin_resumeMusic", StartMusic);

-- stops sounds
-- if queue is loaded and playing, stop the timer, call fade out
-- if not run the usual stopsounds
local function ccStopAllSounds( ply, cmd, args )

	if(ply:IsAdmin() or ply:IsSuperAdmin() or ply:IsConsole()) then
	
		if( timerExists ) then
		
			currentSong = "";
			timer.Destroy( "songTimer" );
			timerExists = false;
			DoFadeOut();
		
		else
		
			for k, v in pairs( player.GetAll() ) do

				v:ConCommand( "stopsounds " );
			
			end
			
		end
		
	end
	
end
concommand.Add( "admin_stopsounds", ccStopAllSounds );

-- Use this to play sounds
-- if the sound is known in the MusicList{} then it will be enqueued
-- Otherwise it will stop current queue song, remove current song, pause queue playing, and play the entered sound
-- immediately. The queue must be unpaused later.
local function ccPlayAllSound( ply, cmd, args )
	
	if(ply:IsAdmin() or ply:IsSuperAdmin() or ply:IsConsole())then
	
		if( args[1] == nil ) then
			CAKE.Notify(ply, "Format: admin_playtoall <soundpath>");
			return;
		end
		
		-- we know this song, add it to the queue
		if( MusicList[args[1]] != nil ) then
		
			table.insert( SongList, args[1] );
			
			if( !timerExists ) then
			
				StartMusic();
				
			end
		
		-- don't know this song, stop current if we have one, pause queue, play input song
		else
			
			currentSong = "";
			timer.Destroy( "songTimer" );
			timerExists = false;
			ccStopAllSounds( ply, cmd, args );
			
			-- unknown song, unknown length, can't use fading on it, could fade in, but don't know its length
			-- use the old way for playing it.
			for k, v in pairs( player.GetAll() ) do

				v:ConCommand( "Play " .. args[1] );

			end
		
		end
	
	end

end
concommand.Add( "admin_playtoall", ccPlayAllSound );