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

#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "audio/decoders/raw.h"
#include "common/system.h"

#include "chewy/resource.h"
#include "chewy/sound.h"

namespace Chewy {

Sound::Sound(Audio::Mixer *mixer) {
	_mixer = mixer;
	_speechRes = new SoundResource("speech.tvp");
	_soundRes = new SoundResource("details.tap");
}

Sound::~Sound() {
	delete _soundRes;
	delete _speechRes;
}

void Sound::playSound(int num, bool loop, uint channel) {
	SoundChunk *sound = _soundRes->getSound(num);
	byte *data = (byte *)malloc(sound->size);
	memcpy(data, sound->data, sound->size);

	playSound(data, sound->size, loop, channel);

	delete[] sound->data;
	delete sound;
}

void Sound::playSound(byte *data, uint32 size, bool loop, uint channel, DisposeAfterUse::Flag dispose) {
	Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
		Audio::makeRawStream(data,
		size, 22050, Audio::FLAG_UNSIGNED,
		dispose),
		loop ? 0 : 1);

	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[channel], stream);
}

void Sound::pauseSound(uint channel) {
	assert(channel < MAX_SOUND_EFFECTS);
	_mixer->pauseHandle(_soundHandle[channel], true);
}

void Sound::resumeSound(uint channel) {
	assert(channel < MAX_SOUND_EFFECTS);
	_mixer->pauseHandle(_soundHandle[channel], false);
}

void Sound::stopSound(uint channel) {
	assert(channel < MAX_SOUND_EFFECTS);
	_mixer->stopHandle(_soundHandle[channel]);
}

bool Sound::isSoundActive(uint channel) {
	assert(channel < MAX_SOUND_EFFECTS);
	return _mixer->isSoundHandleActive(_soundHandle[channel]);
}

void Sound::setSoundVolume(uint volume) {
	_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
}

void Sound::setSoundChannelVolume(uint channel, uint volume) {
	assert(channel < MAX_SOUND_EFFECTS);
	_mixer->setChannelVolume(_soundHandle[channel], volume);
}

void Sound::setSoundChannelBalance(uint channel, uint balance) {
	assert(channel < MAX_SOUND_EFFECTS);
	_mixer->setChannelBalance(_soundHandle[channel], balance);
}

void Sound::playMusic(int num, bool loop) {
	uint32 musicNum = _soundRes->getChunkCount() - 1 - num;
	Chunk *chunk = _soundRes->getChunk(musicNum);
	byte *data = _soundRes->getChunkData(musicNum);

	playMusic(data, chunk->size, loop);

	delete[] data;
	delete chunk;
}

void Sound::playMusic(byte *data, uint32 size, bool loop, DisposeAfterUse::Flag dispose) {
	byte *modData = nullptr;
	uint32 modSize;

	// TODO: TMF music files are similar to MOD files. With the following
	// incorrect implementation, the PCM parts of these files can be played
	warning("The current music playing implementation is wrong");
	modSize = size;
	modData = (byte *)malloc(modSize);
	memcpy(modData, data, size);
	//convertTMFToMod(data, size, modData, modSize);

	Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
		Audio::makeRawStream(modData,
		modSize, 22050, Audio::FLAG_UNSIGNED,
		dispose),
		loop ? 0 : 1);

	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream);
}

void Sound::pauseMusic() {
	_mixer->pauseHandle(_musicHandle, true);
}

void Sound::resumeMusic() {
	_mixer->pauseHandle(_musicHandle, false);
}

void Sound::stopMusic() {
	_mixer->stopHandle(_musicHandle);
}

bool Sound::isMusicActive() {
	return _mixer->isSoundHandleActive(_musicHandle);
}

void Sound::setMusicVolume(uint volume) {
	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
}

void Sound::playSpeech(int num) {
	SoundChunk *sound = _speechRes->getSound(num);
	byte *data = (byte *)malloc(sound->size);
	memcpy(data, sound->data, sound->size);

	Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
		Audio::makeRawStream(data,
		sound->size, 22050, Audio::FLAG_UNSIGNED,
		DisposeAfterUse::YES),
		1);

	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);

	delete[] sound->data;
	delete sound;
}

void Sound::pauseSpeech() {
	_mixer->pauseHandle(_speechHandle, true);
}

void Sound::resumeSpeech() {
	_mixer->pauseHandle(_speechHandle, false);
}

void Sound::stopSpeech() {
	_mixer->stopHandle(_speechHandle);
}

bool Sound::isSpeechActive() {
	return _mixer->isSoundHandleActive(_speechHandle);
}

void Sound::setSpeechVolume(uint volume) {
	_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
}

void Sound::stopAll() {
	_mixer->stopAll();
}

void Sound::convertTMFToMod(byte *tmfData, uint32 tmfSize, byte *modData, uint32 &modSize) {
	// Convert the TMF stream back to a regular Protracker MOD stream
	const int maxInstruments = 31;
	// Extra bytes needed: 20 bytes song name, 31 * 22 instrument names, 4 magic bytes "M.K."
	modSize = tmfSize + 20 + maxInstruments * 22 + 4;
	modData = (byte *)malloc(modSize);
	byte *tmfPtr = tmfData;
	byte *modPtr = modData;

	const byte songName[20] = {
		'S', 'C', 'U', 'M', 'M',
		'V', 'M', ' ', 'M', 'O',
		'D', 'U', 'L', 'E', '\0',
		'\0', '\0', '\0', '\0', '\0'
	};
	const byte instrumentName[22] = {
		'S', 'C', 'U', 'M', 'M',
		'V', 'M', ' ', 'I', 'N',
		'S', 'T', 'R', 'U', 'M',
		'E', 'N', 'T', '\0', '\0',
		'\0', '\0'
	};

	if (READ_BE_UINT32(tmfPtr) != MKTAG('T', 'M', 'F', '\0'))
		error("Corrupt TMF resource");
	tmfPtr += 4;

	memcpy(modPtr, songName, 20);
	modPtr += 20;

	byte fineTune, instVolume;
	uint16 repeatPoint, repeatLength, sampleLength;

	for (int i = 0; i < maxInstruments; i++) {
		fineTune = *tmfPtr++;
		instVolume = *tmfPtr++;
		repeatPoint = READ_BE_UINT16(tmfPtr);	tmfPtr += 2;
		repeatLength = READ_BE_UINT16(tmfPtr);	tmfPtr += 2;
		sampleLength = READ_BE_UINT16(tmfPtr);	tmfPtr += 2;

		// Instrument name
		memcpy(modPtr, instrumentName, 18);	modPtr += 18;
		*modPtr++ = ' ';
		*modPtr++ = i / 10;
		*modPtr++ = i % 10;
		*modPtr++ = '\0';

		WRITE_BE_UINT16(modPtr, sampleLength / 2);	modPtr += 2;
		*modPtr++ = fineTune;
		*modPtr++ = instVolume;
		WRITE_BE_UINT16(modPtr, repeatPoint / 2);	modPtr += 2;
		WRITE_BE_UINT16(modPtr, repeatLength / 2);	modPtr += 2;
	}

	*modPtr++ = *tmfPtr++;	// song length
	*modPtr++ = *tmfPtr++;	// undef
	memcpy(modPtr, tmfPtr, 128);
	modPtr += 128;
	tmfPtr += 128;
	WRITE_BE_UINT32(modPtr, MKTAG('M', '.', 'K', '.'));	modPtr += 4;
	// TODO: 31 bytes instrument positions

	// TODO: notes

	free(modData);
}

} // End of namespace Chewy