/*
	HESOUND.CPP

	Music (MOD, S3M, XM format modules, MIDI) and sound
	(RIFF WAVE sample) playing routines for wxWindows using SDL:

		hugo_playmusic
		hugo_stopmusic
		hugo_playsample
		hugo_stopsample

	for the Hugo Engine

	Copyright (c) 1995-2006 by Kent Tessman

	Both hugo_playmusic() and hugo_playsample() are called with the
	file hot--i.e., opened and positioned to the start of the resource.
	It must be closed before returning.

	Adaptation for SDL by Torbjorn Andersson.
*/

extern "C"
{
#include "heheader.h"

int CopyStreamToFile(FILE *stream, long reslength, char *namebuf);

int hugo_playmusic(FILE *infile, long reslength, char loop_flag);
void hugo_musicvolume(int vol);
void hugo_stopmusic(void);
int hugo_playsample(FILE *infile, long reslength, char loop_flag);
void hugo_samplevolume(int vol);
void hugo_stopsample(void);
}

#if !defined (NO_SOUND)

#include "hewx.h"
#include <stdio.h>
#include <SDL.h>
#include <SDL_mixer.h>

void SuspendAudio(void);
void ResumeAudio(void);

// I'm going to use tmpnam() to create up to two temporary files: one for
// sound and one for music. All documentation I've read tells me that for
// the love of God, don't ever user tmpname() and friends, presumably
// because the file name is only guaranteed to be unique when tmpnam() is
// called. Not when the file is opened a fraction of a second later. Maybe
// someone could sneak in a symbolic link pointing to some other file which
// would then be overwritten. They tell me I should use tempfile() instead.
//
// How on earth am I supposed to do that!? I need to create a file that I
// can refer to by name so that I can open it later!

static char music_filename[L_tmpnam];
static char sample_filename[L_tmpnam];

// I don't know why I should set sample_volume to 25, but if I do the
// sample volume seems to be about the same as with the MikMod version.
// At least when playing Tetris.

char no_audio = true;
Mix_Music *music = NULL;
int music_volume = 100;
Mix_Chunk *sample = NULL;
int sample_volume = 25;
char audio_open = false;

// Much of this code is taken from or inspired by the playmus/playwave
// programs that are included with SDL_mixer. I also glanced some at Bill
// Kendrick's "Circus Linux".

void CleanUp(void)
{
	hugo_stopsample();
	hugo_stopmusic();

	if (audio_open)
	{
		Mix_CloseAudio();
		audio_open = false;
	}

	SDL_Quit();
}

/* InitPlayer */

int InitPlayer(void)
{
	*music_filename = '\0';
	*sample_filename = '\0';

	if (SDL_Init(SDL_INIT_AUDIO) < 0)
		return false;

	atexit(CleanUp);

	if (Mix_OpenAudio(22050, AUDIO_S16, 2, 4096))
		return false;

	no_audio = false;
	audio_open = true;	

	/* Set the external music player, if any */
	Mix_SetMusicCMD(getenv("MUSIC_CMD"));

	return true;
}

void PrintAudioError(void)
{
	static char printed_audio_error = 0;

	if (!printed_audio_error && sound_enabled)
	{
		sprintf(line, "Unable to play sound/music:\n%s",
			SDL_GetError());
		wxMessageBox(wxString::FromAscii(line), wxT("Sound Error"), wxICON_EXCLAMATION | wxOK,
			frame);
		printed_audio_error = 1;
	}
}

// ISO C considers BUFSIZ to be the appropriate buffer length for reading
// and writing files, and who am I to argue with that?

int CopyStreamToFile(FILE *stream, long reslength, char *namebuf)
{
	FILE *file;
	char buf[BUFSIZ];
	int wanted_length, read_length;

	if (tmpnam(namebuf) == NULL)
		return 0;

	file = fopen(namebuf, "wb");
	if (file == NULL)
	{
		*buf = '\0';
		return 0;
	}

	wanted_length = reslength % BUFSIZ;
	read_length = fread(buf, 1, wanted_length, stream);
	if (read_length > 0)
		fwrite(buf, 1, read_length, file);

	for (reslength -= wanted_length; reslength > 0; reslength -= BUFSIZ)
	{
		read_length = fread(buf, 1, BUFSIZ, stream);
		if (read_length <= 0)
			break;
		fwrite(buf, 1, read_length, file);
	}

	fclose(file);
	return 1;
}

/* hugo_playmusic

	Returns false if it fails because of an ERROR.
*/

int hugo_playmusic(FILE *f, long reslength, char loop_flag)
{
	int result;

	if (no_audio)
	{
		PrintAudioError();
		fclose(f);
		return true;	/* not an error */
	}

	hugo_stopmusic();
	result = CopyStreamToFile(f, reslength, music_filename);
	fclose(f);
	
	if (!result)
		return false;

	music = Mix_LoadMUS(music_filename);

	if (music)
	{
		Mix_PlayMusic(music, (loop_flag ? -1 : 0));
		hugo_musicvolume(music_volume);
		return true;
	}
#if defined (DEBUGGER)
	else
		DebugMessageBox("Sound Library Error", SDL_GetError());
#endif
	return false;
}


/* hugo_musicvolume */

void hugo_musicvolume(int vol)
{
	Mix_VolumeMusic((vol * SDL_MIX_MAXVOLUME) / 100);
	music_volume = vol;
}

/* hugo_stopmusic */

void hugo_stopmusic(void)
{
	if (no_audio) return;

	if (Mix_PlayingMusic())
		Mix_HaltMusic();

	if (music)
	{
		Mix_FreeMusic(music);
		music = NULL;
	}

	if (*music_filename)
	{
		remove(music_filename);
		*music_filename = '\0';
	}
}


/* hugo_playsample

	Returns false if it fails because of an ERROR.
*/

int hugo_playsample(FILE *f, long reslength, char loop_flag)
{
	int result;

	if (no_audio)
	{
		PrintAudioError();
		fclose(f);
		return true;	/* not an error */
	}

	hugo_stopsample();
	result = CopyStreamToFile(f, reslength, sample_filename);
	fclose(f);

	if (!result)
		return false;

	sample = Mix_LoadWAV(sample_filename);

	if (sample)
	{
		Mix_PlayChannel(-1, sample, (loop_flag ? -1 : 0));
		hugo_samplevolume(sample_volume);
		return true;
	}
#if defined (DEBUGGER)
	else
		DebugMessageBox("Sound Library Error", MikMod_strerror(MikMod_errno));
#endif
	return false;
}


/* hugo_samplevolume */

void hugo_samplevolume(int vol)
{
	Mix_Volume(-1, (vol * SDL_MIX_MAXVOLUME) / 100);
	sample_volume = vol;
}


/* hugo_stopsample */

void hugo_stopsample(void)
{
	if (no_audio) return;

	// Apparently freeing the chunk will make sure it isn't playing.
	// If not... well, at least it stopped crashing when I removed
	// these two lines.
#if 0
	if (Mix_Playing(-1))
		Mix_HaltChannel(-1);
#endif

	if (sample)
	{
		Mix_FreeChunk(sample);
		sample = NULL;
	}

	if (*sample_filename)
	{
		remove(sample_filename);
		*sample_filename = '\0';
	}
}

int suspended_music_volume, suspended_sample_volume;

void SuspendAudio(void)
{
	suspended_music_volume = music_volume;
	suspended_sample_volume = sample_volume;
	hugo_musicvolume(0);
	hugo_samplevolume(0);
	if (music)
		Mix_PauseMusic();
	if (sample)
		Mix_Pause(-1);
}

void ResumeAudio(void)
{
	hugo_musicvolume(suspended_music_volume);
	hugo_samplevolume(suspended_sample_volume);
	if (music)
		Mix_ResumeMusic();
	if (sample)
		Mix_Resume(-1);
}

#else	/* NO_SOUND */

extern "C"
{

int hugo_playmusic(FILE *f, long reslength, char loop_flag)
{
	fclose(f);
	return true;	/* not an error */
}

void hugo_musicvolume(int vol)
{}

void hugo_stopmusic(void)
{}

int hugo_playsample(FILE *f, long reslength, char loop_flag)
{
	fclose(f);
	return true;	/* not an error */
}

void hugo_samplevolume(int vol)
{}

void hugo_stopsample(void)
{}

}	// extern "C"

#endif 	/* NO_SOUND */
