/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * Additional copyright for this file:
 * Copyright (C) 1994-1998 Revolution Software Ltd.
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*****************************************************************************
 *		SOUND.H		Sound engine
 *
 *		SOUND.CPP Contains the sound engine, fx & music functions
 *		Some very 'sound' code in here ;)
 *
 *		(16Dec96 JEL)
 *
 ****************************************************************************/

#ifndef SWORD2_SOUND_H
#define SWORD2_SOUND_H

#include "common/file.h"
#include "common/mutex.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"

// Max number of sound fx
#define MAXMUS 2

// Max number of fx in queue at once
#define FXQ_LENGTH 32

#define BUFFER_SIZE 4096

namespace Sword2 {

enum {
	kCLUMode = 1,
	kMP3Mode,
	kVorbisMode,
	kFLACMode
};

enum {
	kLeadInSound,
	kLeadOutSound
};

enum {
	// These three types correspond to types set by the scripts
	FX_SPOT		= 0,
	FX_LOOP		= 1,
	FX_RANDOM	= 2,

	// These are used for FX queue bookkeeping
	FX_SPOT2	= 3,
	FX_LOOPING	= 4
};

// Sound defines

enum {
	RDSE_SAMPLEFINISHED		= 0,
	RDSE_SAMPLEPLAYING		= 1,
	RDSE_FXTOCLEAR			= 0,		// Unused
	RDSE_FXCACHED			= 1,		// Unused
	RDSE_FXSPOT			= 0,
	RDSE_FXLOOP			= 1,
	RDSE_FXLEADIN			= 2,
	RDSE_FXLEADOUT			= 3,
	RDSE_QUIET			= 1,
	RDSE_SPEAKING			= 0
};

class CLUInputStream : public Audio::AudioStream {
private:
	Common::File *_file;
	bool _firstTime;
	uint32 _file_pos;
	uint32 _end_pos;
	int16 _outbuf[BUFFER_SIZE];
	byte _inbuf[BUFFER_SIZE];
	const int16 *_bufferEnd;
	const int16 *_pos;

	uint16 _prev;

	void refill();

	inline bool eosIntern() const {
		return !_file->isOpen() || _pos >= _bufferEnd;
	}

public:
	CLUInputStream(Common::File *file, int size);
	~CLUInputStream();

	int readBuffer(int16 *buffer, const int numSamples);

	bool endOfData() const	{ return eosIntern(); }
	bool isStereo() const	{ return false; }
	int getRate() const	{ return 22050; }
};

struct SoundFileHandle {
	Common::File file;
	uint32 *idxTab;
	uint32 idxLen;
	int32 fileSize;
	uint32 fileType;
	volatile bool inUse;
};

class MusicInputStream : public Audio::AudioStream {
private:
	int _cd;
	SoundFileHandle *_fh;
	uint32 _musicId;
	Audio::AudioStream *_decoder;
	int16 _buffer[BUFFER_SIZE];
	const int16 *_bufferEnd;
	const int16 *_pos;
	bool _remove;
	uint32 _numSamples;
	uint32 _samplesLeft;
	bool _looping;
	int32 _fading;
	int32 _fadeSamples;

	void refill();

	inline bool eosIntern() const {
		if (_looping)
			return false;
		return _remove || _pos >= _bufferEnd;
	}

public:
	MusicInputStream(int cd, SoundFileHandle *fh, uint32 musicId, bool looping);
	~MusicInputStream();

	int readBuffer(int16 *buffer, const int numSamples);

	bool endOfData() const	{ return eosIntern(); }
	bool isStereo() const	{ return _decoder->isStereo(); }
	int getRate() const	{ return _decoder->getRate(); }

	int getCD()		{ return _cd; }

	void fadeUp();
	void fadeDown();

	bool isReady()		{ return _decoder != NULL; }
	int32 isFading()	{ return _fading; }

	bool readyToRemove();
	int32 getTimeRemaining();
};

class Sound : public Audio::AudioStream {
private:
	Sword2Engine *_vm;

	Common::Mutex _mutex;

	Audio::SoundHandle _mixerSoundHandle;
	Audio::SoundHandle _leadInHandle;
	Audio::SoundHandle _leadOutHandle;

	struct FxQueueEntry {
		Audio::SoundHandle handle;	// sound handle
		uint32 resource;		// resource id of sample
		byte *data;			// pointer to WAV data
		uint32 len;			// WAV data length
		uint16 delay;			// cycles to wait before playing (or 'random chance' if FX_RANDOM)
		uint8 volume;			// sound volume
		int8 pan;			// sound panning
		uint8 type;			// FX_SPOT, FX_RANDOM, FX_LOOP
	};

	FxQueueEntry _fxQueue[FXQ_LENGTH];

	void triggerFx(uint8 i);

	bool _reverseStereo;

	bool _speechMuted;
	bool _fxMuted;
	bool _musicMuted;

	bool _speechPaused;
	bool _fxPaused;
	bool _musicPaused;

	int32 _loopingMusicId;

	Audio::SoundHandle _soundHandleSpeech;

	MusicInputStream *_music[MAXMUS];
	SoundFileHandle _musicFile[MAXMUS];
	SoundFileHandle _speechFile[MAXMUS];

	int16 *_mixBuffer;
	int _mixBufferLen;

public:
	Sound(Sword2Engine *vm);
	~Sound();

	// AudioStream API

	int readBuffer(int16 *buffer, const int numSamples);
	bool isStereo() const { return false; }
	bool endOfData() const;
	int getRate() const { return Sword2Engine::isPsx() ? 11025 : 22050; }

	// End of AudioStream API

	void clearFxQueue(bool killMovieSounds);
	void processFxQueue();

	void setReverseStereo(bool reverse);
	bool isReverseStereo() const { return _reverseStereo; }

	void muteSpeech(bool mute);
	bool isSpeechMute() const { return _speechMuted; }

	void muteFx(bool mute);
	bool isFxMute() const { return _fxMuted; }

	void muteMusic(bool mute) { _musicMuted = mute; }
	bool isMusicMute() const { return _musicMuted; }

	void setLoopingMusicId(int32 id) { _loopingMusicId = id; }
	int32 getLoopingMusicId() const { return _loopingMusicId; }

	void pauseSpeech();
	void unpauseSpeech();

	void pauseFx();
	void unpauseFx();

	void pauseMusic();
	void unpauseMusic();

	void playMovieSound(int32 res, int type);
	void stopMovieSounds();

	void queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan);
	int32 playFx(FxQueueEntry *fx);
	int32 playFx(Audio::SoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, Audio::Mixer::SoundType soundType);
	int32 stopFx(int32 i);
	int32 setFxIdVolumePan(int32 id, int vol, int pan = 255);

	int32 getSpeechStatus();
	int32 amISpeaking();
	int32 playCompSpeech(uint32 speechId, uint8 vol, int8 pan);
	int32 stopSpeech();

	int32 streamCompMusic(uint32 musicId, bool loop);
	void stopMusic(bool immediately);
	int32 musicTimeRemaining();

	void printFxQueue();
};

} // End of namespace Sword2

#endif
