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

#ifdef ENABLE_LOL

#include "kyra/engine/lol.h"
#include "kyra/sound/sound.h"
#include "kyra/resource/resource.h"

#include "common/system.h"

#include "audio/audiostream.h"

namespace Kyra {

bool LoLEngine::snd_playCharacterSpeech(int id, int8 speaker, int) {
	if (!speechEnabled())
		return false;

	if (speaker < 65) {
		if (_characters[speaker].flags & 1)
			speaker = (int)_characters[speaker].name[0];
		else
			speaker = 0;
	}

	if (_lastSpeechId == id && speaker == _lastSpeaker)
		return true;

	_lastSpeechId = id;
	_lastSpeaker = speaker;
	_nextSpeechId = _nextSpeaker = -1;

	Common::String pattern1;
	Common::String file1;
	Common::String file2;
	Common::String file3;

	SpeechList newSpeechList;

	Common::String pattern2 = Common::String::format("%02d", id & 0x4000 ? 0 : _curTlkFile);

	if (id & 0x4000) {
		pattern1 = Common::String::format("%03X", id & 0x3FFF);
	} else if (id < 1000) {
		pattern1 = Common::String::format("%03d", id);
	} else {
		file3 = Common::String::format("@%04d%c.%s", id - 1000, (char)speaker, pattern2.c_str());
		if (_sound->isVoicePresent(file3.c_str()))
			newSpeechList.push_back(_sound->getVoiceStream(file3.c_str()));
	}

	if (file3.empty()) {
		for (char i = 0; ; i++) {
			char symbol = '0' + i;
			file1 = Common::String::format("%s%c%c.%s", pattern1.c_str(), (char)speaker, symbol, pattern2.c_str());
			file2 = Common::String::format("%s%c%c.%s", pattern1.c_str(), '_', symbol, pattern2.c_str());
			if (_sound->isVoicePresent(file1.c_str()))
				newSpeechList.push_back(_sound->getVoiceStream(file1.c_str()));
			else if (_sound->isVoicePresent(file2.c_str()))
				newSpeechList.push_back(_sound->getVoiceStream(file2.c_str()));
			else
				break;
		}
	}

	if (newSpeechList.empty())
		return false;

	while (_sound->voiceIsPlaying(&_speechHandle))
		delay(_tickLength, true);

	while (_sound->allVoiceChannelsPlaying())
		delay(_tickLength);

	for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end(); ++i)
		delete *i;
	_speechList.clear();
	_speechList = newSpeechList;

	_activeVoiceFileTotalTime = 0;
	for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end();) {
		// Just in case any file loading failed: Remove the bad streams here.
		if (!*i)
			i = _speechList.erase(i);
		else
			_activeVoiceFileTotalTime += (*i++)->getLength().msecs();
	}

	_sound->playVoiceStream(*_speechList.begin(), &_speechHandle);
	_speechList.pop_front();

	if (!_activeVoiceFileTotalTime)
		return false;

	_tim->_abortFlag = 0;

	return true;
}

int LoLEngine::snd_updateCharacterSpeech() {
	if (_sound->voiceIsPlaying(&_speechHandle))
		return 2;

	if (_speechList.begin() != _speechList.end()) {
		_sound->playVoiceStream(*_speechList.begin(), &_speechHandle);
		_speechList.pop_front();
		return 2;

	} else if (_nextSpeechId != -1) {
		_lastSpeechId = _lastSpeaker = -1;
		_activeVoiceFileTotalTime = 0;
		if (snd_playCharacterSpeech(_nextSpeechId, _nextSpeaker, 0))
			return 2;
	}

	_lastSpeechId = _lastSpeaker = -1;
	_activeVoiceFileTotalTime = 0;

	return 0;
}

void LoLEngine::snd_stopSpeech(bool setFlag) {
	if (!_sound->voiceIsPlaying(&_speechHandle))
		return;

	//_dlgTimer = 0;
	_sound->voiceStop(&_speechHandle);
	_activeVoiceFileTotalTime = 0;
	_nextSpeechId = _nextSpeaker = -1;

	for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end(); ++i)
		delete *i;
	_speechList.clear();

	if (setFlag)
		_tim->_abortFlag = 1;
}

void LoLEngine::snd_playSoundEffect(int track, int volume) {
	if ((track == 1 && (_lastSfxTrack == -1 || _lastSfxTrack == 1)) || shouldQuit())
		return;

	_lastSfxTrack = track;
	if (track == -1 || track >= _ingameSoundIndexSize / 2)
		return;

	volume &= 0xFF;
	int16 prIndex = _ingameSoundIndex[track * 2 + 1];
	uint16 priority = (prIndex > 0) ? (prIndex * volume) >> 8 : -prIndex;

	static const uint8 volTable1[] = { 223, 159, 95, 47, 15, 0 };
	static const uint8 volTable2[] = { 255, 191, 127, 63, 30, 0 };

	for (int i = 0; i < 6; i++) {
		if (volTable1[i] < volume) {
			volume = volTable2[i];
			break;
		}
	}

	int16 vocIndex = _ingameSoundIndex[track * 2];

	bool hasVocFile = false;
	if (vocIndex != -1) {
		if (!_ingameSoundList[vocIndex].equalsIgnoreCase("EMPTY"))
			hasVocFile = true;
	}

	if (hasVocFile) {
		if (_sound->isVoicePresent(_ingameSoundList[vocIndex].c_str()))
			_sound->voicePlay(_ingameSoundList[vocIndex].c_str(), 0, volume, priority, true);
	} else if (_flags.platform == Common::kPlatformDOS) {
		if (_sound->getSfxType() == Sound::kMidiMT32)
			track = (track < _ingameMT32SoundIndexSize) ? (_ingameMT32SoundIndex[track] - 1) : -1;
		else if (_sound->getSfxType() == Sound::kMidiGM)
			track = (track < _ingameGMSoundIndexSize) ? (_ingameGMSoundIndex[track] - 1) : -1;
		else if (_sound->getSfxType() == Sound::kPCSpkr)
			track = (track < _ingamePCSpeakerSoundIndexSize) ? (_ingamePCSpeakerSoundIndex[track] - 1) : -1;

		if (track == 168)
			track = 167;

		if (track != -1)
			KyraEngine_v1::snd_playSoundEffect(track, volume);
	}
}

bool LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) {
	if (!KyraRpgEngine::snd_processEnvironmentalSoundEffect(soundId, block))
		return false;

	if (block != _currentBlock) {
		static const int8 blockShiftTable[] = { -32, -31, 1, 33, 32, 31, -1, -33 };
		uint16 cbl = _currentBlock;

		for (int i = 3; i > 0; i--) {
			int dir = calcMonsterDirection(cbl & 0x1F, cbl >> 5, block & 0x1F, block >> 5);
			cbl = (cbl + blockShiftTable[dir]) & 0x3FF;
			if (cbl == block)
				break;
			if (testWallFlag(cbl, 0, 1))
				_environmentSfxVol >>= 1;
		}
	}

	if (!soundId || _sceneUpdateRequired)
		return false;

	return snd_processEnvironmentalSoundEffect(0, 0);
}

void LoLEngine::snd_queueEnvironmentalSoundEffect(int soundId, int block) {
	if (_envSfxUseQueue && _envSfxNumTracksInQueue < 10) {
		_envSfxQueuedTracks[_envSfxNumTracksInQueue] = soundId;
		_envSfxQueuedBlocks[_envSfxNumTracksInQueue] = block;
		_envSfxNumTracksInQueue++;
	} else {
		snd_processEnvironmentalSoundEffect(soundId, block);
	}
}

void LoLEngine::snd_playQueuedEffects() {
	for (int i = 0; i < _envSfxNumTracksInQueue; i++)
		snd_processEnvironmentalSoundEffect(_envSfxQueuedTracks[i], _envSfxQueuedBlocks[i]);
	_envSfxNumTracksInQueue = 0;
}

void LoLEngine::snd_loadSoundFile(int track) {
	if (!_sound->musicEnabled() || _flags.platform != Common::kPlatformDOS)
		return;
	snd_stopMusic();
	int t = (track - 250) * 3;
	if (t < 0 || (_curMusicFileIndex == _musicTrackMap[t] && _curMusicFileExt == (char)_musicTrackMap[t + 1]))
		return;
	_sound->loadSoundFile(Common::String::format("LORE%02d%c", _musicTrackMap[t], (char)_musicTrackMap[t + 1]));
	_curMusicFileIndex = _musicTrackMap[t];
	_curMusicFileExt = (char)_musicTrackMap[t + 1];
}

int LoLEngine::snd_playTrack(int track) {
	if (track == -1)
		return _lastMusicTrack;

	int res = _lastMusicTrack;
	_lastMusicTrack = track;

	if (_sound->musicEnabled()) {
		if (_flags.platform == Common::kPlatformDOS) {
			snd_loadSoundFile(track);
			int t = (track - 250) * 3;
			_sound->playTrack(_musicTrackMap[t + 2]);
		} else {
			_sound->playTrack(track - 249);
		}
	}

	return res;
}

int LoLEngine::snd_stopMusic() {
	if (_sound->musicEnabled()) {
		if (_sound->isPlaying()) {
			_sound->beginFadeOut();
			_system->delayMillis(3 * _tickLength);
		}

		_sound->haltTrack();
	}
	return snd_playTrack(-1);
}

int LoLEngine::convertVolumeToMixer(int value) {
	value -= 2;
	return (value * Audio::Mixer::kMaxMixerVolume) / 100;
}

int LoLEngine::convertVolumeFromMixer(int value) {
	return (value * 100) / Audio::Mixer::kMaxMixerVolume + 2;
}

} // End of namespace Kyra

#endif // ENABLE_LOL
