/*
	HESOUND.CPP

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

		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.
*/

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

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);

// From heres.c:
long FindResource(char *filename, char *resname);
}

#if !defined (NO_SOUND)

#include "hewx.h"
#include "mikmod.h"

#ifdef __WXMAC__
int mac_playmusic(FILE *infile, long reslength, char loop_flag, int volume);
int mac_stopmusic(void);
bool music_is_quicktime = false;
#endif

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

#define MIKMOD_TIMER_MILLISECONDS 20

#if wxUSE_THREADS
bool use_sound_thread = false;
#endif

char no_audio = false;
MODULE *modfile;
char music_is_playing = false;
int music_volume = 100, player_volume = 100;
SAMPLE *sample;
char sample_is_playing = false;
int sample_volume = 100;
int sample_channel = 0;
char audio_suspended = false;

#define MAXCHANNELS 32			/* for MikMod */

#if wxUSE_THREADS

#include <wx/thread.h>

// The thread class to run the player:
class MikModThread : public wxThread
{
public:
	virtual void *Entry();
};

void *MikModThread::Entry()
{
	while (IsRunning())
	{
		if (TestDestroy())
			break;
		MikMod_Update();
		Sleep(MIKMOD_TIMER_MILLISECONDS);
	}
	return 0;
}

MikModThread *mmthread;

#endif	// wxUSE_THREADS

// The timer class to run the player:
class MikModTimer : public wxTimer
{
public:
    virtual void Notify();
};

void MikModTimer::Notify()
{
	MikMod_Update();
}

MikModTimer mmtimer;


/* InitPlayer */

int InitPlayer(void)
{
	MikMod_RegisterLoader(&load_mod);	/* module loaders */
	MikMod_RegisterLoader(&load_s3m);
	MikMod_RegisterLoader(&load_xm);

#ifdef USE_REGISTERALLDRIVERS
	MikMod_RegisterAllDrivers();		/* valid audio drivers */
#else
#if defined (__WXMAC__)
	MikMod_RegisterDriver(&drv_mac);
	MikMod_RegisterDriver(&drv_nos);
#elif defined (__WXMSW__)
	MikMod_RegisterDriver(&drv_ds);
	MikMod_RegisterDriver(&drv_win);
	MikMod_RegisterDriver(&drv_nos);
#else
//	MikMod_RegisterDriver(&drv_raw);
//	MikMod_RegisterDriver(&drv_stdout);
//	MikMod_RegisterDriver(&drv_wav);
	MikMod_RegisterDriver(&drv_ultra);
	MikMod_RegisterDriver(&drv_sam9407);
	MikMod_RegisterDriver(&drv_AF);
	MikMod_RegisterDriver(&drv_aix);
	MikMod_RegisterDriver(&drv_alsa);
	MikMod_RegisterDriver(&drv_esd);
	MikMod_RegisterDriver(&drv_hp);
	MikMod_RegisterDriver(&drv_oss);
	MikMod_RegisterDriver(&drv_sgi);
	MikMod_RegisterDriver(&drv_sun);
	MikMod_RegisterDriver(&drv_pipe);
	MikMod_RegisterDriver(&drv_nos);
#endif
#endif
	md_mixfreq = 22050;			/* standard mixing frequency */
md_mixfreq = 44100;
	md_mode = DMODE_16BITS | DMODE_STEREO |
		// software mixing
		DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
	md_device = 0;				/* standard device: autodetect */

	// This is 6 by default, but that's too much
	md_reverb = 0;

	if (MikMod_Init((char *)""))		/* initialize driver */
	{
		no_audio = true;
		return false;
	}

	/* Start the player here and keep it running globally */
	MikMod_SetNumVoices(MAXCHANNELS, MAXCHANNELS);
	MikMod_EnableOutput();
	
	// Start notification
#if wxUSE_THREADS
	if (use_sound_thread)
	{
		mmthread = new MikModThread;
		if (mmthread->Create()==wxTHREAD_NO_ERROR)
		{
			if (mmthread->Run()!=wxTHREAD_NO_ERROR)
			{
				fprintf(stderr, "Error calling mmthread->Run()\n");
				use_sound_thread = false;
			}
		}
		else
		{
			fprintf(stderr, "Error calling mmthread->Create()\n");
			use_sound_thread = false;
		}
	}
	if (!use_sound_thread)
	{
		mmtimer.Start(MIKMOD_TIMER_MILLISECONDS);
	}
#else
	mmtimer.Start(MIKMOD_TIMER_MILLISECONDS);
#endif

	return true;
}


/* ExitPlayer */

void ExitPlayer(void)
{
#if wxUSE_THREADS
	if (use_sound_thread && mmthread)
	{
		mmthread->Delete();
		mmthread = NULL;
	}
	else
		mmtimer.Stop();
#else
	mmtimer.Stop();
#endif
}


/* PrintAudioError */

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

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


/* hugo_playmusic

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

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

	if (music_is_playing)
	{
		hugo_stopmusic();
	}
	
#ifdef __WXMAC__
	if ((int)resource_type==MP3_R || (int)resource_type==MIDI_R)
	{
		music_is_quicktime = true;
		if (mac_playmusic(f, reslength, loop_flag, music_volume))
		{
			music_is_playing = true;
			return true;
		}
		else
		{
			music_is_playing = false;
			return false;
		}		
	}
	else
		music_is_quicktime = false;
#endif	
	
	// The file is already positioned
	modfile = Player_LoadFP(f, MAXCHANNELS, 0);
	fclose(f);

	if (modfile)
	{		
		// Need to tickle the player if we've got it suspended
		if (audio_suspended) Player_TogglePause();
		Player_Start(modfile);
		if (audio_suspended) Player_TogglePause();

		if (loop_flag)
			modfile->wrap = 1;
		else
			modfile->wrap = 0;

		if (audio_suspended)
			Player_SetVolume(0);
		else
		{
			// Convert from 0-100
			Player_SetVolume(player_volume = (music_volume*120/100));
			
			// Need to do this, too, because this seems to be where
			// mplayer.c resets the volume from initvolume:
			modfile->initvolume = player_volume;
		}

		music_is_playing = true;
		
		return true;
	}
#if defined (DEBUGGER)
	else
		DebugMessageBox("Sound Library Error", MikMod_strerror(MikMod_errno));
#endif
	return false;
}


/* hugo_musicvolume */

void hugo_musicvolume(int vol)
{
	music_volume = vol;
}


/* hugo_stopmusic */

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

	if (music_is_playing)
	{
		music_is_playing = false;
#ifdef __WXMAC__
		if (music_is_quicktime)
		{
			mac_stopmusic();
			return;
		}
#endif
		modfile->numpos = 0;
		Player_Free(modfile);
	}
}


/* hugo_playsample

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

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

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

	if (sample_is_playing) hugo_stopsample();

	sample = Sample_LoadFP(f);
	fclose(f);

	if (sample)
	{
		/* Always play a sample on the last/highest channel */
		channel = MAXCHANNELS;

		// Convert volume from 0-100
		Voice_SetVolume(channel, 
			(!audio_suspended)?sample_volume*255/100:0);
		Voice_SetPanning(channel, 128);
		Voice_SetFrequency(channel, sample->speed);

		if (loop_flag)
		{
			sample->flags|=SF_LOOP;
			sample->loopstart = 0;
			sample->loopend = sample->length;
			Voice_Play(channel, sample, 0);
		}
		else
		{
			sample->flags&=~SF_LOOP;
			Voice_Play(channel, sample, 0);
		}

		sample_is_playing = true;
		
		return true;
	}
#if defined (DEBUGGER)
	else
		DebugMessageBox("Sound Library Error", MikMod_strerror(MikMod_errno));
#endif
	return false;
}


/* hugo_samplevolume */

void hugo_samplevolume(int vol)
{
	sample_volume = vol;
}


/* hugo_stopsample */

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

	if (sample_is_playing)
	{
		sample_is_playing = false;
		sample->length = 0;
		sample->flags&=~SF_LOOP;

		Sample_Free(sample);
	}
}

int suspended_music_volume;

void SuspendAudio(void)
{
	if (audio_suspended || no_audio) return;

	if (sample_is_playing) Voice_SetVolume(MAXCHANNELS, 0);
	
	suspended_music_volume = player_volume;
	Player_SetVolume(player_volume = 0);
	Player_TogglePause();
	audio_suspended = true;
}

void ResumeAudio(void)
{
	if (!audio_suspended || no_audio) return;

	if (sample_is_playing) Voice_SetVolume(MAXCHANNELS, sample_volume*256/100);

	player_volume = suspended_music_volume;
	Player_SetVolume(player_volume);
	Player_TogglePause();
	audio_suspended = false;	
}

#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 */
