/* 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.
 *
 * $URL$
 * $Id$
 *
 */

/*
 * This code is based on Broken Sword 2.5 engine
 *
 * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
 *
 * Licensed under GNU GPL v2
 *
 */

// -----------------------------------------------------------------------------
// Logging
// -----------------------------------------------------------------------------

#define BS_LOG_PREFIX "FMODEXRESOURCE"

// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------

#include <memory>
#include "sword25/sfx/fmodexexception.h"
#include "sword25/sfx/fmodexchannel.h"
#include "sword25/package/packagemanager.h"
#include "sword25/sfx/fmodexresource.h"

// -----------------------------------------------------------------------------
// Konstanten
// -----------------------------------------------------------------------------

namespace {
const unsigned int MAX_SAMPLE_SIZE = 100 * 1024; // Die Dateigr��e in Byte ab der ein Sound als Stream abgespielt wird
}

// -----------------------------------------------------------------------------
// Konstruktion / Destruktion
// -----------------------------------------------------------------------------


BS_FMODExResource::BS_FMODExResource(const std::string &FileName, FMOD_SYSTEM *FMOD, bool &Success) :
	m_SoundPtr(0),
	m_SoundDataPtr(0),
	BS_Resource(FileName, BS_Resource::TYPE_SOUND) {
	BS_ASSERT(FMOD);

	// Von Misserfolg ausgehen
	Success = false;

	// Pointer auf den Package-Manager bekommen
	BS_PackageManager *PackagePtr = BS_Kernel::GetInstance()->GetPackage();
	if (!PackagePtr) {
		BS_LOG_ERRORLN("Package manager not found.");
		return;
	}

	// Datei laden
	unsigned int FileSize;
	char *FileDataPtr = (char *) PackagePtr->GetFile(GetFileName(), &FileSize);
	if (!FileDataPtr) {
		BS_LOG_ERRORLN("File \"%s\" could not be loaded.", GetFileName().c_str());
		return;
	}

	// Ob die Sounddatei als Sample oder als Stream behandelt wird, ist abh�ngig von der Dateigr��e.
	// Samples werden sofort intialisiert.
	// F�r Streams wird hingegen bei jedem Abspielen ein neuer Sound erstellt. Dieses Vorgehen ist notwendig, da FMOD Ex Samples beliebig oft
	// gleichzeitig abspielen kann, Streams jedoch nur ein mal.
	if (FileSize <= MAX_SAMPLE_SIZE) {
		FMOD_CREATESOUNDEXINFO ExInfo;
		memset(&ExInfo, 0, sizeof(ExInfo));
		ExInfo.cbsize = sizeof(ExInfo);
		ExInfo.length = FileSize;

		FMOD_RESULT Result = FMOD_System_CreateSound(FMOD, FileDataPtr,
		                     FMOD_CREATESAMPLE | FMOD_OPENMEMORY | FMOD_SOFTWARE | FMOD_2D | FMOD_LOOP_NORMAL,
		                     &ExInfo,
		                     &m_SoundPtr);
		if (Result != FMOD_OK) BS_FMODExException("FMOD_System_CreateSound()", Result).Log();

		Success = Result == FMOD_OK;

		delete FileDataPtr;
	} else {
		m_SoundDataPtr = FileDataPtr;
		m_SoundDataSize = FileSize;

		Success = true;
	}
}

// -----------------------------------------------------------------------------

BS_FMODExResource::~BS_FMODExResource() {
	// Sound freigeben, solange des Soundsystem noch l�uft.
	// Sollte das Soundsystem beendet worden sein m�ssen und k�nnen Sounds nicht mehr freigegeben werden.
	if (m_SoundPtr && BS_Kernel::GetInstance()->GetService("sfx")) FMOD_Sound_Release(m_SoundPtr);
	if (m_SoundDataPtr) delete [] m_SoundDataPtr;
}

// -----------------------------------------------------------------------------
// Abspielen
// -----------------------------------------------------------------------------

BS_FMODExChannel *BS_FMODExResource::StartSound(FMOD_SYSTEM *FMOD) {
	BS_ASSERT(FMOD);

	FMOD_CHANNEL *NewChannelPtr;
	FMOD_SOUND    *NewSoundPtr = 0;

	// Sample k�nnen sofort abgespielt werden.
	if (m_SoundPtr) {
		FMOD_RESULT Result = FMOD_System_PlaySound(FMOD, FMOD_CHANNEL_FREE, m_SoundPtr, 1, &NewChannelPtr);
		if (Result != FMOD_OK) {
			BS_FMODExException("FMOD_System_PlaySound()", Result).Log();
			return 0;
		}
	}
	// F�r Streams muss ein neuer Sound erstellt werden.
	else {
		FMOD_CREATESOUNDEXINFO ExInfo;
		memset(&ExInfo, 0, sizeof(ExInfo));
		ExInfo.cbsize = sizeof(ExInfo);
		ExInfo.length = m_SoundDataSize;

		FMOD_RESULT Result;
		Result = FMOD_System_CreateSound(FMOD,
		                                 m_SoundDataPtr,
		                                 FMOD_CREATESTREAM | FMOD_OPENMEMORY_POINT | FMOD_SOFTWARE | FMOD_2D | FMOD_LOOP_NORMAL,
		                                 &ExInfo,
		                                 &NewSoundPtr);
		if (Result != FMOD_OK) {
			BS_FMODExException("FMOD_System_CreateSound()", Result).Log();
			return 0;
		}

		Result = FMOD_System_PlaySound(FMOD, FMOD_CHANNEL_FREE, NewSoundPtr, 1, &NewChannelPtr);
		if (Result != FMOD_OK) {
			BS_FMODExException("FMOD_System_PlaySound()", Result).Log();
			return 0;
		}
	}

	// Der Channel und der Sound (bei Streams) werden an ein BS_FMODExChannel-Objekt �bergeben.
	// Dieses Sorgt auch daf�r, dass Channel und Sound korrekt zerst�rt werden.
	return new BS_FMODExChannel(NewChannelPtr, NewSoundPtr);
}