/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1994  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <stdlib.h>

#include "pm.h"
#include "init.h"
#include "../util/error.h"
#include "../util/memory.h"
#include "../util/audio.h"

#define sounds_IMPORT
#include "sounds.h"

// BEWARE: the index of this array must match the sounds_SoundID enum values.
static char *files[] =
{
	"sounds/DUMMY-ARRAY-ENTRY.wav", // never really loaded
	"sounds/generic-piston-engine.wav",
	"sounds/generic-jet-engine.wav",
	"sounds/generic-rocket-engine.wav",
	"sounds/crash.wav",
	"sounds/gear_up.wav",
	"sounds/gear_dn.wav",
	"sounds/missile.wav",
	"sounds/cannon.wav",
	"sounds/crash.wav",
	"sounds/screetch.wav",
	"sounds/warning.wav",
	"sounds/stall.wav",
	"sounds/rwr.wav",
	"sounds/apglock.wav"
};


typedef struct {
	
	/** If sound playing is enabled (that is, not muted). */
	int isEnabled;
	
	/** Engine sound ID. */
	sounds_SoundID engine_sound;
	
	/** Cached loaded sounds. */
	audio_Type *samples[sounds_NumberOfSounds];
	
} sounds_Type;


/**
 * Returns the current instance of this module bound to the aircraft.
 * @param c
 * @return Current instance bound to the craft, or NULL if no current instance
 * exists or the aircraft has no viewer.
 */
static sounds_Type * sounds_getCurrentInstance(craft *c)
{
	if( c->vl == NULL )
		return NULL;
	return c->vl->sounds;
}


static audio_Type *sounds_loadAndCacheSample(sounds_Type *this, sounds_SoundID id)
{
	if( !(0 <= id && id < sounds_NumberOfSounds) )
		error_internal("sound ID out of the range: %d", id);
	
	if( this->samples[id] == NULL ){
		this->samples[id] = audio_new( init_findFile(files[id]) );
	}
	return this->samples[id];
}


static void sounds_destruct(void *p)
{
	sounds_Type *this = p;
	int i;
	for(i = 0; i < sounds_NumberOfSounds; i++)
		memory_dispose(this->samples[i]);
}


static sounds_Type * sounds_new(craft *c)
{
	viewer *v = c->vl;
	if( v == NULL )
		error_internal("no viewer associated to craft", 0);
	
	sounds_Type *this = memory_allocate(sizeof(sounds_Type), sounds_destruct);
	
	this->isEnabled = 0;
	
	switch(v->c->cinfo->engineType){
		
	case inventory_NoEngine:
		this->engine_sound = sounds_None; break;
		
	case inventory_GenericPistonEngine:
		this->engine_sound = sounds_GenericPistonEngine; break;
		
	case inventory_GenericJetEngine:
		this->engine_sound = sounds_GenericJetEngine; break;
		
	case inventory_GenericRocketEngine:
		this->engine_sound = sounds_GenericRocketEngine; break;
		
	default:
		this->engine_sound = sounds_None; break;
	}
	
	int i;
	for (i = 0; i < sounds_NumberOfSounds; ++i) {
		
		// Sound samples are loaded only if needed:
		this->samples[i] = NULL;
		
		// ... but check all the sounds are there to avoid surprises later :-)
		if( i > 0 )
			/* ignore = */ init_findFile( files[i] );
	}
	
	// Avoid delay playing the "screech" sound at touchdown, cache right now:
	/* ignore = */ sounds_loadAndCacheSample(this, sounds_Touchdown);
	
	v->sounds = this;
	return this;
}


void sounds_enable(craft *c, int enable)
{
	sounds_Type *this = sounds_getCurrentInstance(c);
	
	if( enable ){
		if( this == NULL )
			this = sounds_new(c);
		this->isEnabled = 1;
		
	} else {
		if( this == NULL || ! this->isEnabled )
			return;
		this->isEnabled = 0;
		// Stop any sound we are currently playing:
		int i;
		for(i = sounds_NumberOfSounds-1; i >= 0; i--){
			if( this->samples[i] != NULL )
				sounds_stopSound(c, i);
		}
	}
}


int sounds_isEnabled(craft * c)
{
	sounds_Type *this = sounds_getCurrentInstance(c);
	if( this == NULL )
		return 0;
	else
		return this->isEnabled;
}


void
sounds_playSound(craft * c, sounds_SoundID id, int loop)
{
	sounds_Type *this = sounds_getCurrentInstance(c);
	if( this == NULL || ! this->isEnabled )
		return;
	
	audio_Type *sample = sounds_loadAndCacheSample(this, id);
	if( ! loop && ! audio_isPlaying(sample) )
		audio_setCurrentPositionMilliseconds(sample, 0);
	audio_loop(sample, loop);
	audio_play(sample);
}


void
sounds_stopSound(craft * c, sounds_SoundID id)
{
	if( !(0 <= id && id < sounds_NumberOfSounds) )
		error_internal("sound ID out of the range: %d", id);
	sounds_Type *this = sounds_getCurrentInstance(c);
	if( this == NULL )
		return;
	audio_Type *sample = this->samples[id];
	if( sample == NULL )
		return;
	audio_pause(sample);
	audio_setCurrentPositionMilliseconds(sample, 0);
}


void
sounds_setBackgroundSound(craft * c, double rpm_rate,
	int afterburner, double dynamicPressure)
{
	sounds_Type *this = sounds_getCurrentInstance(c);
	if( this == NULL )
		return;
	
	if( this->engine_sound == sounds_None
	|| ( ! this->isEnabled && this->samples[this->engine_sound] == NULL ) )
		return;
	
	audio_Type *sample = sounds_loadAndCacheSample(this, this->engine_sound);
	if( ! this->isEnabled || rpm_rate < 0.1 ){
		audio_pause(sample);
	} else {
		audio_setCurrentSamplesPerSecond(sample, rpm_rate * audio_getOriginalSamplesPerSecond(sample));
		audio_loop(sample, 1);
		audio_play(sample);
	}
}


void
sounds_update(craft * c)
{
}
