//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/snd_music.c                                             $
// $Revision:: 4                                                               $
//   $Author:: Burgermac                                                       $
//     $Date:: 8/8/01 7:38 PM                                                  $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// music system
//

#include "client.h"
#include "snd_loc.h"

/*
==============================================================================

SIN MUSIC FILES

==============================================================================
*/

#define MAX_ARGS 16
#define MAX_ARG_LENGTH 64
#define MAX_SONGS 16

song_t music_songs[ MAX_SONGS ];
int music_numsongs;
int music_currentsong = -1;

/*
============
MUSIC_ExtractPath
============
*/
#if 0
static void MUSIC_ExtractPath(const char *in, char *out)
{
	const char *s;
	int len;

	s = in + strlen( in ) - 1;

	while (s != in && *s != '/' && *s != '\\')
		s--;

	len = s-in+1;
	if (len<2)
	{
		strcpy( out,"?song?" );
	}
	else
	{
		memcpy( out,in,len );
		out[len] = 0;
	}
}
#endif

/*
=================
MUSIC_LoadSountrackFile
=================
*/
qboolean MUSIC_LoadSoundtrackFile( const char * filename )
{
	song_t *psong;
	char args[MAX_ARGS][MAX_ARG_LENGTH];
	int numargs;
	int argnum;
	char com_token[MAX_STRING_CHARS];
	char alias[128];
	char file[128];
	char load_path[128];
	char *buffer;
	char path[MAX_QPATH];
	size_t sz;
	int i;
	void *data;

	//
	// check for extension
	//
	if (!strrchr(filename, '.'))
		Com_sprintf (path, sizeof(path), "%s/%s.mus", s_musicdir->string, filename);
	else
		Com_sprintf (path, sizeof(path), "%s/%s", s_musicdir->string, filename);

	if (FS_LoadFile(path, &data) < 0)
	{
		Com_DPrintf (DEVELOPER_MSG_SOUND, "Couldn't load %s\n", path);
		return false;
	}
	Com_DPrintf (DEVELOPER_MSG_SOUND, "SOUNDTRACK: Loading %s\n", path);

	// stop all current music
	MUSIC_StopAllSongs();
	// free all the current music
	MUSIC_FreeSongs();
	// set the buffer
	buffer = (char *)data;
	// initialize some variables
	music_numsongs = 0;

	load_path[0] = 0;
	// get the original path
	//MUSIC_ExtractPath ( path, load_path );

	psong = NULL;/* silence compiler */

	// read in all the commands
	while (1)
	{
		numargs = 0;
		// see if we reached the end of the file
		strcpy(com_token, COM_GetToken((const char **)&buffer, true));

		if (!com_token[0])
			break;

		if (strlen(com_token) >= MAX_ARG_LENGTH)
		{
			Com_Printf ("MUSIC_LoadSoundtrackFile: argument too long, truncating in %s\n", path);
			com_token[MAX_ARG_LENGTH - 1] = 0;
		}
		strcpy(args[numargs++], com_token);
		// get the rest of the line
		while (1)
		{
			strcpy(com_token, COM_GetToken((const char **)&buffer, false));
			if (!com_token[0])
				break;
			if (strlen(com_token) >= MAX_ARG_LENGTH)
			{
				Com_Printf ("MUSIC_LoadSoundtrackFile: argument too long, truncating in %s\n", path);
				com_token[MAX_ARG_LENGTH - 1] = 0;
			}
			strcpy(args[numargs++], com_token);
		}

		// now that we have the whole line, process it
		argnum = 0;
		// see if it is a special command
		if (!Q_stricmp(args[argnum], "path"))
		{
			strcpy(load_path, args[argnum+1]);
			sz = strlen(load_path);
			if (sz && (load_path[sz-1] != '/' || load_path[sz-1] != '\\'))
				strcat(load_path, "/");
			continue;
		}
		if (args[argnum][0] == '!')
		{
			for (i = 0; i < music_numsongs; i++)
			{
				psong = &music_songs[i];
				if (!Q_stricmp(psong->alias, &args[0][1]))
					break;
			}
			if (i == music_numsongs || /* silence compiler: */ !psong)
			{
				Com_Printf ("MUSIC_LoadSoundtrackFile: song %s not found, command skipped in %s.\n", &args[0][1], path);
			}
			else
			{
				// process the command
				if (!Q_stricmp(args[1], "volume"))
				{
					psong->volume = Sinatof(args[2]);
				}
				else if (!Q_stricmp(args[1], "fadetime"))
				{
					psong->fadetime = Sinatof(args[2]);
				}
				else if (!Q_stricmp(args[1], "loop"))
				{
					psong->flags |= SONG_LOOP;
				}
				else if (!Q_stricmp(args[1], "restart"))
				{
					psong->flags |= SONG_RESTART;
				}
				else if (!Q_stricmp(args[1], "interrupt"))
				{
					psong->flags |= SONG_INTERRUPT;
					psong->flags |= SONG_RESTART;
					psong->fadetime = 0;
				}
				else
				{
					Com_Printf ("MUSIC_LoadSoundtrackFile: unknown command %s for song %s in %s.\n", args[1], &args[0][1], path);
				}
			}
		}
		else
		{
			// it must be a sprite frame, grab and store them
			// it must be a skin, base model or anim declaration
			if (numargs > 1)
			{
				strcpy(alias, args[argnum]);
				strcpy(file, load_path);
				strcat(file, args[argnum + 1]);
			}
			else
			{
				strcpy(file, load_path);
				strcat(file, args[argnum + 1]);
				// copy the filename minus the extension
				strncpy(alias, args[argnum], strlen(args[argnum])-4);
				alias[strlen(args[argnum])-4] = 0;
			}
			if (music_numsongs < MAX_SONGS)
			{
				strcpy(music_songs[music_numsongs].alias, alias);
				strcpy(music_songs[music_numsongs].path, file);
				music_songs[music_numsongs].flags = 0;
				music_songs[music_numsongs].fadetime = 1;
				music_songs[music_numsongs].volume = 1;
				music_songs[music_numsongs].current_pos = 0;
				music_numsongs++;
			}
			else
			{
				Com_Printf ("MUSIC_LoadSoundtrackFile: Too many songs in %s, skipping %s.\n", path, alias);
			}
		}
	}
	if (!music_numsongs)
	{
		// these are too serious to continue loading the rest of the sprite file
		Com_Printf ("MUSIC_LoadSoundtrackFile: could not load %s, no songs defined.\n", path);
		FS_FreeFile(data);
		return false;
	}
	music_currentsong = -1;
	FS_FreeFile(data);
	return true;
}

/*
=================
MUSIC_RegisterSongs
=================
*/
void MUSIC_RegisterSongs( void )
{
	int i;
	song_t * psong;

	//
	// register the songs
	//
	for (i = 0; i < music_numsongs; i++)
	{
		psong = &music_songs[i];
		S_RegisterSound(psong->path);
	}
}

/*
=================
MUSIC_FreeSongs
=================
*/
void MUSIC_FreeSongs( void )
{
	int i;
	song_t * psong;

	//
	// register the songs
	//
	for (i = 0; i < music_numsongs; i++)
	{
		sfx_t * sfx;

		psong = &music_songs[i];
		sfx = S_RegisterSound(psong->path);
		S_FreeSound(sfx);
		memset(psong, 0, sizeof(song_t));
	}
}

/*
=================
MUSIC_FindRealSong
=================
*/
static song_t *MUSIC_FindRealSong(const char *name)
{
	int i;
	song_t *psong;

	for (i = 0; i < music_numsongs; i++)
	{
		psong = &music_songs[i];
		if (!Q_stricmp(psong->path, name))
			return psong;
	}
	return NULL;
}

/*
=================
MUSIC_FindSong
=================
*/
static song_t *MUSIC_FindSong(const char *name)
{
	int i;
	song_t *psong;

	for (i = 0; i < music_numsongs; i++)
	{
		psong = &music_songs[i];
		if (!Q_stricmp(psong->alias, name))
			return psong;
	}
	return NULL;
}

/*
=================
MUSIC_SongFaded
=================
*/
void MUSIC_SongFaded(channel_t *ch)
{
	song_t *song;

	song = MUSIC_FindRealSong(ch->sfx->name);
	// make it no longer a music channel
	ch->entnum = 0;
	ch->sfx = NULL;
	if (song)
	{
		if (song->flags & SONG_RESTART)
		{
			song->current_pos = 0;
			memset(&song->current_state, 0, sizeof(ImaState));
		}
		else
		{
			song->current_pos = ch->pos;
			song->current_state = ch->state;
		}
	}
}

/*
=================
MUSIC_SongEnded
=================
*/
qboolean MUSIC_SongEnded(channel_t *ch)
{
	song_t *song;

	// don't interrupt fading in or out
	//if (ch->flags & SOUND_FADING)
	//	return false;

	song = MUSIC_FindRealSong(ch->sfx->name);
	if (song)
	{
		int songnum;

		songnum = song - music_songs;
		if (songnum == music_currentsong && (song->flags & SONG_LOOP))
			return false;
		else
		{
			// make it no longer a music channel
			ch->entnum = 0;
			ch->sfx = NULL;
			song->current_pos = 0;
			memset(&song->current_state, 0, sizeof(ImaState));
			CL_SongEnded(song->alias);
			return true;
		}
	}
	return false;
}

/*
=================
MUSIC_CurrentSongChannel
=================
*/
static channel_t * MUSIC_CurrentSongChannel( void )
{
	int ch_idx;
	channel_t   *ch;

	ch = NULL;
	for (ch_idx = 0; ch_idx < MAX_CHANNELS; ch_idx++)
	{
		if (channels[ch_idx].entnum < 0 && channels[ch_idx].sfx)
		{
			if ((channels[ch_idx].flags & SOUND_FADING) && channels[ch_idx].fadestep < 0)
			{
				continue;
			}
			else
			{
				if (ch)
				{
					// zero out fading
					channels[ch_idx].flags &= ~SOUND_FADING;
					MUSIC_SongFaded (&channels[ch_idx]);
					Com_DPrintf (DEVELOPER_MSG_SOUND, "MUSIC_CurrentSongChannel: More than one music channel active.\n");
				}
				else
				{
					ch = &channels[ch_idx];
				}
			}
		}
	}
	return ch;
}

/*
=================
MUSIC_Fading
=================
*/
#if 0
static qboolean MUSIC_Fading(void)
{
	channel_t  *ch;

	ch = MUSIC_CurrentSongChannel();
	if (!ch)
		return false;

	if (ch->flags & SOUND_FADING)
		return true;
	return false;
}
#endif

/*
=================
MUSIC_StopAllSongs
=================
*/
void MUSIC_StopAllSongs( void )
{
	int ch_idx;

	for (ch_idx=0; ch_idx < MAX_CHANNELS; ch_idx++)
	{
		if (channels[ch_idx].entnum < 0 && channels[ch_idx].sfx)
			S_FadeSound(&channels[ch_idx], 0.5f, true);
	}
	music_currentsong = -1;
}

/*
=================
MUSIC_SongValid
=================
*/
qboolean MUSIC_SongValid(const char *alias)
{
	song_t *song;

	song = MUSIC_FindSong(alias);
	if (song)
		return true;
	return false;
}

/*
=================
MUSIC_PlaySong
=================
*/
qboolean MUSIC_PlaySong(const char *alias)
{
	channel_t *other;
	song_t *song;
	sfx_t   *sfx;
	int songnum;

	song = MUSIC_FindSong(alias);
	if (song)
	{
		songnum = song - music_songs;
		#if 0
		if (MUSIC_Fading ())
			return false;
		#endif
		if (MUSIC_Playing() && songnum == music_currentsong)
			return true;
		other = MUSIC_CurrentSongChannel();
		music_currentsong = songnum;
		if (song->flags & SONG_INTERRUPT)
		{
			if (other)
			{
				// zero out fading
				other->flags &= ~SOUND_FADING;
				MUSIC_SongFaded (other);
			}
		}
		else
		{
			// fade out
			if (other)
			{
				S_FadeSound(other, song->fadetime, true);
			}
		}
		sfx = S_RegisterSound(song->path);
		S_StartSoundEx(NULL, -1, 0, sfx, song->volume, song->volume, 0, 1.0f, song->fadetime, song->current_pos, (void *)&song->current_state, SOUND_SYNCH_FADE);
	}
	return true;
}

/*
=================
MUSIC_Playing
=================
*/
qboolean MUSIC_Playing(void)
{
	channel_t *other;

	other = MUSIC_CurrentSongChannel();
	if (other)
		return true;
	return false;
}

/*
=================
MUSIC_CurrentSong
=================
*/
const char *MUSIC_CurrentSong(void)
{
	if (music_currentsong < 0)
		return NULL;

	return music_songs[music_currentsong].alias;
}

/*
=================
MUSIC_Loaded
=================
*/
qboolean MUSIC_Loaded(void)
{
	return (music_numsongs > 0);
}

/*
=================
MUSIC_Shutdown
=================
*/
void MUSIC_Shutdown( void )
{
	MUSIC_StopAllSongs();
	// free all the current music
	//MUSIC_FreeSongs();
	// initialize some variables
	music_numsongs = 0;
}
