// FMOD Sound Interface
// for the Build Engine
// by Jonathon Fowler (jf@jonof.id.au)

#include "fmod.h"
#include "fmod_errors.h"

#ifndef F_CALLBACKAPI
#define FMOD_PRE370
#define F_CALLBACKAPI _cdecl
#endif

// some Linux people may need this if GCC can't find FSOUND_Stream_Open
// #define FMOD_PRE370

#include "osd.h"
#include "compat.h"
#include "cache1d.h"

#define NUMCHANNELS 16
#define MAXWAVES 256


static int fmod_inited = 0;
static int musicstat = 0;

// general sample playback
static int numwaves;
static FSOUND_SAMPLE * samples[MAXWAVES];
static char instname[MAXWAVES][16];
static int channels[NUMCHANNELS];

void loadwaves(void);




static unsigned int F_CALLBACKAPI f_open(const char *name)
{
	return kopen4load((char *)name, 0) + 1;
}

static void F_CALLBACKAPI f_close(unsigned int handle)
{
	kclose(handle - 1);
}

static int F_CALLBACKAPI f_read(void *buffer, int size, unsigned int handle)
{
	return kread(handle - 1, buffer, size);
}

static int F_CALLBACKAPI f_seek(unsigned int handle, int pos, signed char mode)
{
	return klseek(handle - 1, pos, mode);
}

static int F_CALLBACKAPI f_tell(unsigned int handle)
{
	return ktell(handle - 1);
}


void initsb(char dadigistat, char damusistat, long dasamplerate, char danumspeakers, char dabytespersample, char daintspersec, char daquality)
{
	char *s;
	int i,j;
	
	if (fmod_inited) return;
	fmod_inited = 0;

	if (dasamplerate < 6000) dasamplerate = 6000;
	else if (dasamplerate > 48000) dasamplerate = 48000;

	musicstat = damusistat;
	
	printOSD("Initialising FMOD...\n");
	printOSD("  Linked version: %.02f\n", FMOD_VERSION);
	printOSD("  DLL version: %.02f\n", FSOUND_GetVersion());

	if (FSOUND_GetVersion() < FMOD_VERSION) {
		printOSD("  ... Failure: FMOD DLL too old! Sound disabled.\n");
		return;
	}
	
	printOSD("  Samplerate: %d hz\n", dasamplerate);

	//FSOUND_SetOutput(FSOUND_OUTPUT_ASIO);

	if (FSOUND_Init(dasamplerate, NUMCHANNELS, 0)) {
		printOSD("  ... Success\n");
		fmod_inited = 1;
	} else {
		printOSD("  ... Failure: %s\n", FMOD_ErrorString(FSOUND_GetError()));
	}

	switch (FSOUND_GetOutput()) {
		case FSOUND_OUTPUT_NOSOUND: s = "No Sound"; break;
		case FSOUND_OUTPUT_WINMM: s = "WINMM"; break;
		case FSOUND_OUTPUT_DSOUND: s = "DirectSound"; break;
		case FSOUND_OUTPUT_OSS: s = "OSS"; break;
		case FSOUND_OUTPUT_ESD: s = "ESound"; break;
		case FSOUND_OUTPUT_ALSA: s = "ALSA"; break;
		case FSOUND_OUTPUT_ASIO: s = "ASIO"; break;
		default: s = "Other"; break;
	}
	printOSD("Using FMOD \"%s\" output driver\n", s);

	FSOUND_File_SetCallbacks(
			(FSOUND_OPENCALLBACK)f_open,
			(FSOUND_CLOSECALLBACK)f_close,
			(FSOUND_READCALLBACK)f_read,
			(FSOUND_SEEKCALLBACK)f_seek,
			(FSOUND_TELLCALLBACK)f_tell);
	//FSOUND_SetMemorySystem(fmod_cache, fmod_cachelen, NULL, NULL, NULL);

	loadwaves();

	for (i=0; i<NUMCHANNELS; i++) channels[i] = -1;
}

void uninitsb(void)
{
	if (fmod_inited) {		
		FSOUND_Close();
		fmod_inited = 0;
	}
}


void setears(long daposx, long daposy, long daxvect, long dayvect)
{
}


void wsayfollow(char *dafilename, long dafreq, long davol, long *daxplc, long *dayplc, char followstat)
{
/*
	dafilename:
		filename

	dafreq:
		4096 = Middle C
		8192 = +1 8ve

	davol:
		0-256 = volume

	daxplc:
	dayplc:
		followstat: 0 = sound emination origin (fixed)
		followstat: 1 = pointer to sprite coordinates (dynamic)

	followstat:
		0 = position is fixed in space
		1 = position follows an object
*/
	unsigned char ch1, ch2;
	long i, wavnum, bad, oldest=0, free=-1, ox, oy, x, y, vo;
	unsigned int oldestpos=0;
	int chan;

	if (fmod_inited == 0) return;

	for (wavnum=numwaves-1;wavnum>=0;wavnum--) {
		bad = 0;

		i = 0;
		while ((dafilename[i] > 0) && (i < 16))
		{
			ch1 = dafilename[i]; if ((ch1 >= 97) && (ch1 <= 123)) ch1 -= 32;
			ch2 = instname[wavnum][i]; if ((ch2 >= 97) && (ch2 <= 123)) ch2 -= 32;
			if (ch1 != ch2) {bad = 1; break;}
			i++;
		}
		if (bad != 0) continue;

		for (i=0; i<NUMCHANNELS; i++) {
			if (!FSOUND_IsPlaying(channels[i])) {
				free = i;
				break;
			}
			if (i==0) continue;
			
			if (FSOUND_GetCurrentPosition(channels[i]) > oldestpos) {
				oldest = i;
				oldestpos = FSOUND_GetCurrentPosition(channels[i]);
			}
		}

		if (free < 0) {
			FSOUND_StopSound(channels[oldest]);
			free = oldest;
		}

		chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[wavnum], NULL, 1);
		if (chan == -1) return;
		FSOUND_SetFrequency(chan, dafreq*11025/4096);
		FSOUND_SetVolume(chan, davol);
		
		FSOUND_SetPaused(chan, 0);
		
		channels[free] = chan;
		return;
	}
}


void wsay(char *dafilename, long dafreq, long volume1, long volume2)
{
	unsigned char ch1, ch2;
	long i, j, bad, free=-1, oldest=0;
	unsigned int oldestpos=0;
	int chan;

	if (fmod_inited == 0) return;

	i = numwaves-1;
	do
	{
		bad = 0;

		j = 0;
		while ((dafilename[j] > 0) && (j < 16))
		{
			ch1 = dafilename[j]; if ((ch1 >= 97) && (ch1 <= 123)) ch1 -= 32;
			ch2 = instname[i][j]; if ((ch2 >= 97) && (ch2 <= 123)) ch2 -= 32;
			if (ch1 != ch2) {bad = 1; break;}
			j++;
		}
		if (bad == 0)
		{
			for (j=0; j<NUMCHANNELS; j++) {
				if (!FSOUND_IsPlaying(channels[j])) {
					free = j;
					break;
				}
				if (j==0) continue;
			
				if (FSOUND_GetCurrentPosition(channels[j]) > oldestpos) {
					oldest = j;
					oldestpos = FSOUND_GetCurrentPosition(channels[j]);
				}
			}

			if (free < 0) {
				FSOUND_StopSound(channels[oldest]);
				free = oldest;
			}

			chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[i], NULL, 1);
			if (chan == -1) return;
			FSOUND_SetFrequency(chan, dafreq*11025/4096);
			FSOUND_SetVolume(chan, (volume1*volume2)>>1);
			// set pan
			FSOUND_SetPaused(chan, 0);

			channels[free] = chan;
			
			return;
		}

		i--;
	} while (i >= 0);
}


void loadwaves(void)
{
	long fil, dawaversionum, i, tmp;
	long wavleng[MAXWAVES], repstart[MAXWAVES], repleng[MAXWAVES], finetune[MAXWAVES];
	char *p;

	fil = kopen4load("WAVES.KWV", 0);

	if (fil != -1) {
		kread(fil, &dawaversionum, 4); dawaversionum = B_LITTLE32(dawaversionum);
		if (dawaversionum != 0) { kclose(fil); return; }

		kread(fil, &numwaves, 4); numwaves = B_LITTLE32(numwaves);
		for (i=0; i<numwaves; i++) {
			kread(fil, &instname[i][0], 16);
			kread(fil, &wavleng[i], 4);  wavleng[i]  = B_LITTLE32(wavleng[i]);
			kread(fil, &repstart[i], 4); repstart[i] = B_LITTLE32(repstart[i]);
			kread(fil, &repleng[i], 4);  repleng[i]  = B_LITTLE32(repleng[i]);
			kread(fil, &finetune[i], 4); finetune[i] = B_LITTLE32(finetune[i]);
		}
	} else {
		dawaversionum = 0;
		numwaves = 0;
	}

	for (i=numwaves; i<MAXWAVES; i++) {
		memset(&instname[i][0], 0, 16);
		wavleng[i] = 0;
		repstart[i] = 0;
		repleng[i] = 0;
		finetune[i] = 0;
		samples[i] = NULL;
	}

	if (fil == -1) return;

	for (i=0; i<numwaves; i++) {
		if (repleng[i]) tmp = FSOUND_LOOP_NORMAL;
		else tmp = FSOUND_LOOP_OFF;
		samples[i] = FSOUND_Sample_Alloc(FSOUND_FREE, wavleng[i], tmp, 11025, 255, 128, 1);
		if (!samples[i]) continue;

		p = (char*)Bmalloc(wavleng[i]);
		kread(fil,p,wavleng[i]);
		FSOUND_Sample_Upload(samples[i], p, FSOUND_8BITS | FSOUND_MONO | FSOUND_UNSIGNED);
		Bfree(p);

		if (repleng[i]) FSOUND_Sample_SetLoopPoints(samples[i], repstart[i], repstart[i]+repleng[i]);
	}

	kclose(fil);

	printOSD("Loaded %d waves\n", numwaves);
}


#if 1
static FSOUND_STREAM *musicstream = NULL;
static int musicplaying = 0;

void loadsong(char *filename)
{
	if (!musicstat) return;
	if (musicstream) return;
#ifdef FMOD_PRE370
	musicstream = FSOUND_Stream_OpenFile(filename, FSOUND_LOOP_NORMAL, 0);
#else
	musicstream = FSOUND_Stream_Open(filename, FSOUND_LOOP_NORMAL, 0, 0);
#endif
}

void musicon(void)
{
	if (!musicstat) return;
	if (!musicstream || musicplaying) return;
	FSOUND_Stream_Play(FSOUND_FREE, musicstream);
	musicplaying = 1;
}

void musicoff(void)
{
	if (!musicstat) return;
	if (!musicstream || !musicplaying) return;
	FSOUND_Stream_Stop(musicstream);
	musicplaying = 0;
}
#endif

void refreshaudio(void)
{
}
