/* 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.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "mohawk/myst_sound.h"

#include "mohawk/myst.h"
#include "mohawk/resource.h"
#include "mohawk/sound.h"

#include "common/debug.h"

#include "audio/audiostream.h"
#include "audio/decoders/wave.h"

namespace Mohawk {

MystSound::MystSound(MohawkEngine_Myst *vm) :
		_vm(vm),
		_effectId(0),
		_speechSamplesPerSecond(0),
		_backgroundId(0) {
}

MystSound::~MystSound() {
	stopEffect();
	stopBackground();
}

Audio::RewindableAudioStream *MystSound::makeAudioStream(uint16 id, CueList *cueList) {
	if (_vm->isGameVariant(GF_ME)) {
		Common::SeekableReadStream *stream = _vm->getResource(ID_MSND, convertMystID(id));
		return Audio::makeWAVStream(stream->readStream(stream->size()), DisposeAfterUse::YES);
	} else {
		return makeMohawkWaveStream(_vm->getResource(ID_MSND, id), cueList);
	}
}

void MystSound::playEffect(uint16 id, bool loop) {
	debug (0, "Replacing sound %d", id);

	// The original engine also forces looping for those sounds
	switch (id) {
		case 2205:
		case 2207:
		case 5378:
		case 7220:
		case 9119: 	// Elevator engine sound in mechanical age is looping.
		case 9120:
		case 9327:
			loop = true;
			break;
		default:
			break;
	}

	stopEffect();

	Audio::RewindableAudioStream *rewindStream = makeAudioStream(id);
	if (!rewindStream) {
		warning("Unable to open sound '%d'", id);
		return;
	}

	_effectId = id;

	// Set the stream to loop here if it's requested
	Audio::AudioStream *audStream = rewindStream;
	if (loop)
		audStream = Audio::makeLoopingAudioStream(rewindStream, 0);

	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_effectHandle, audStream);
}

void MystSound::stopEffect() {
	_vm->_mixer->stopHandle(_effectHandle);
	_effectId = 0;
	_effectHandle = Audio::SoundHandle();
}

bool MystSound::isEffectPlaying() {
	return _vm->_mixer->isSoundHandleActive(_effectHandle);
}

uint MystSound::getSpeechNumSamplesPlayed() {
	if (isSpeechPlaying()) {
		return (_vm->_mixer->getSoundElapsedTime(_speechHandle) * _speechSamplesPerSecond) / 1000;
	}

	return 0;
}

uint16 MystSound::convertMystID(uint16 id) {
	// Myst ME is a bit more efficient with sound storage than Myst
	// Myst has lots of sounds repeated. To overcome this, Myst ME
	// has MJMP resources which provide a link to the actual MSND
	// resource we're looking for. This saves a lot of space from
	// repeated data.
	if (_vm->hasResource(ID_MJMP, id)) {
		Common::SeekableReadStream *mjmpStream = _vm->getResource(ID_MJMP, id);
		id = mjmpStream->readUint16LE();
		delete mjmpStream;
	}

	return id;
}

void MystSound::playBackground(uint16 id, uint16 volume) {
	debug(0, "Replacing background sound with %d", id);

	stopEffect();

	Common::String name = _vm->getResourceName(ID_MSND, convertMystID(id));

	// Only the first eight characters need to be the same to have a match
	Common::String prefix;
	if (name.size() >= 8)
		prefix = Common::String(name.c_str(), name.c_str() + 8);
	else
		prefix = name;

	// Check if sound is already playing
	if (_vm->_mixer->isSoundHandleActive(_backgroundHandle)
			&& _vm->getResourceName(ID_MSND, convertMystID(_backgroundId)).hasPrefix(prefix)) {
		// The sound is already playing, just change the volume
		changeBackgroundVolume(volume);
		return;
	}

	// Stop old background sound
	stopBackground();

	// Play new sound
	Audio::RewindableAudioStream *rewindStream = makeAudioStream(id);

	if (rewindStream) {
		_backgroundId = id;

		// Set the stream to loop
		Audio::AudioStream *audStream = Audio::makeLoopingAudioStream(rewindStream, 0);

		_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_backgroundHandle, audStream, -1, volume >> 8);
	}
}

void MystSound::stopBackground() {
	_vm->_mixer->stopHandle(_backgroundHandle);
	_backgroundId = 0;
	_backgroundHandle = Audio::SoundHandle();
}

void MystSound::pauseBackground() {
	_vm->_mixer->pauseHandle(_backgroundHandle, true);
}

void MystSound::resumeBackground() {
	stopEffect();
	_vm->_mixer->pauseHandle(_backgroundHandle, false);
}

void MystSound::changeBackgroundVolume(uint16 volume) {
	_vm->_mixer->setChannelVolume(_backgroundHandle, volume >> 8);
}

void MystSound::playSpeech(uint16 id, CueList *cueList) {
	debug (0, "Playing speech %d", id);

	Audio::RewindableAudioStream *rewindStream = makeAudioStream(id, cueList);
	if (!rewindStream) {
		warning("Unable to open sound '%d'", id);
		return;
	}

	_speechSamplesPerSecond = rewindStream->getRate();
	_vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, rewindStream);
}

bool MystSound::isSpeechPlaying() {
	return _vm->_mixer->isSoundHandleActive(_speechHandle);
}

void MystSound::stopSpeech() {
	_vm->_mixer->stopHandle(_speechHandle);
	_speechHandle = Audio::SoundHandle();
	_speechSamplesPerSecond = 0;
}

} // End of namespace Mohawk
