bool SoundManager::loadMusic(const std::string& name,
                             const std::string& fileName)

#include "audio/SoundManager.hpp"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
}

#include "audio/Sound.hpp"
#include "audio/SoundBuffer.hpp"
#include "audio/SoundBufferStreamed.hpp"
#include "audio/SoundSource.hpp"
#include "audio/alCheck.hpp"
#include "engine/GameData.hpp"
#include "engine/GameWorld.hpp"
#include "render/ViewCamera.hpp"

#include <rw/types.hpp>

Sound& SoundManager::getSfxBufferRef(size_t name) {
    auto ref = buffers.find(name);
    if (ref != buffers.end()) {
        return ref->second;
    } else {
        createSfxInstance(name);
    }
    return buffers[name];
}

Sound& SoundManager::getSfxSourceRef(size_t name) {
    return sfx[name];
}

Sound& SoundManager::getSoundRef(const std::string& name) {
    return sounds[name];  // @todo reloading, how to check is it wav/mp3?
}

SoundManager::SoundManager() {
    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::SoundManager(GameWorld* engine) : _engine(engine) {
    auto sdtPath = _engine->data->index.findFilePath("audio/sfx.SDT");
    auto rawPath = _engine->data->index.findFilePath("audio/sfx.RAW");
    sdt.load(sdtPath, rawPath);

    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::~SoundManager() {
    deinitializeOpenAL();
}

bool SoundManager::initializeOpenAL() {
    alDevice = alcOpenDevice(nullptr);
    if (!alDevice) {
        RW_ERROR("Could not find OpenAL device!");
        return false;
    }

    alContext = alcCreateContext(alDevice, nullptr);
    if (!alContext) {
        RW_ERROR("Could not create OpenAL context!");
        return false;
    }

    if (!alcMakeContextCurrent(alContext)) {
        RW_ERROR("Unable to make OpenAL context current!");
        return false;
    }

    // Needed for max distance
    alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);

    return true;
}

void SoundManager::initializeAVCodec() {
#if defined(RW_DEBUG) && defined(RW_VERBOSE_DEBUG_MESSAGES)
    av_log_set_level(AV_LOG_WARNING);
#else
    av_log_set_level(AV_LOG_ERROR);
#endif

    // Some older versions of FFmpeg require it
    // before calling  avformat_alloc_context()
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
    av_register_all();
#endif
}

void SoundManager::deinitializeOpenAL() {
    // Buffers have to been removed before openAL is deinitialized
    sounds.clear();
    buffers.clear();

    // De-initialize OpenAL
    if (alContext) {
        alcMakeContextCurrent(nullptr);
        alcDestroyContext(alContext);
    }
    alContext = nullptr;

    if (alDevice) {
        alcCloseDevice(alDevice);
    }
    alDevice = nullptr;
}

bool SoundManager::loadSound(const std::string& name,
                             const std::string& fileName, bool streamed) {
    Sound* sound = nullptr;
    auto sound_iter = sounds.find(name);

    if (sound_iter != sounds.end()) {
        sound = &sound_iter->second;
    } else {
        auto [it, emplaced] = sounds.emplace(std::piecewise_construct,
                                             std::forward_as_tuple(name),
                                             std::forward_as_tuple());
        sound = &it->second;

        sound->source = std::make_shared<SoundSource>();
        sound->buffer = streamed ? std::make_unique<SoundBufferStreamed>() : std::make_unique<SoundBuffer>();

        sound->source->loadFromFile(fileName, streamed);
        sound->isLoaded = sound->buffer->bufferData(*sound->source);
    }

    return sound->isLoaded;
}

void SoundManager::loadSound(size_t index) {
    Sound* sound = nullptr;

    auto [it, emplaced] =
        sfx.emplace(std::piecewise_construct, std::forward_as_tuple(index),
                    std::forward_as_tuple());
    sound = &it->second;

    sound->source = std::make_shared<SoundSource>();
    sound->source->loadSfx(sdt, index);
}

size_t SoundManager::createSfxInstance(size_t index) {
    Sound* sound = nullptr;
    auto soundRef = sfx.find(index);

    if (soundRef == sfx.end()) {
        // Sound source is not loaded yet
        loadSound(index);
        soundRef = sfx.find(index);
    }

    // Try to reuse first available buffer
    // (aka with stopped state)
    for (auto& [id, sound] : buffers) {
        if (sound.buffer && sound.isStopped()) {
            // Let's use this buffer
            sound.buffer = std::make_unique<SoundBuffer>();
            sound.source = soundRef->second.source;
            sound.isLoaded = sound.buffer->bufferData(*sound.source);
            return id;
        }
    }
    // There's no available free buffer, so
    // we should create a new one.
    auto [it, emplaced] = buffers.emplace(std::piecewise_construct,
                                          std::forward_as_tuple(bufferNr),
                                          std::forward_as_tuple());
    sound = &it->second;

    sound->id = bufferNr;
    sound->buffer = std::make_unique<SoundBuffer>();
    sound->source = soundRef->second.source;
    sound->isLoaded = sound->buffer->bufferData(*sound->source);
    bufferNr++;

    return sound->id;
}

bool SoundManager::isLoaded(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isLoaded;
    }
    return false;
}

bool SoundManager::isPlaying(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPlaying();
    }
    return false;
}

bool SoundManager::isStopped(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isStopped();
    }
    return false;
}

bool SoundManager::isPaused(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPaused();
    }
    return false;
}

void SoundManager::playSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        auto vol = getCalculatedVolumeOfMusic();
        sound->second.setGain(vol);
        sound->second.play();
    }
}

void SoundManager::eraseSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sounds.erase(sound);
    } else {
        RW_MESSAGE("Tried to erase no existing sound " << name);
    }
}

void SoundManager::playSfx(size_t name, const glm::vec3& position, bool looping,
                           int maxDist) {
    auto buffer = buffers.find(name);
    if (buffer != buffers.end()) {
        buffer->second.setPosition(position);
        if (looping) {
            buffer->second.setLooping(looping);
        }

        buffer->second.setPitch(1.f);
        buffer->second.setGain(getCalculatedVolumeOfEffects());
        if (maxDist != -1) {
            buffer->second.setMaxDistance(static_cast<float>(maxDist));
        }
        buffer->second.play();
    }
}

void SoundManager::pauseAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
}

void SoundManager::resumeAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
}

bool SoundManager::playBackground(const std::string& fileName) {
    if (this->loadSound(fileName, fileName)) {
        backgroundNoise = fileName;
        auto& sound = getSoundRef(fileName);
        sound.play();
        return true;
    }

    return false;
}

{
    return loadSound(name, fileName);
}

void SoundManager::playMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.play();
    }
}

void SoundManager::stopMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.stop();
    }
}

void SoundManager::pause(bool p) {
    if (backgroundNoise.length() > 0) {
        if (p) {
            pauseAllSounds();
        } else {
            resumeAllSounds();
        }
    }
}

void SoundManager::updateListenerTransform(const ViewCamera& cam) {
    // Orientation
    auto up = cam.rotation * glm::vec3(0.f, 0.f, 1.f);
    auto at = cam.rotation * glm::vec3(1.f, 0.f, 0.f);
    float orientation[6] = {at.x, at.y, at.z, up.x, up.y, up.z};
    alListenerfv(AL_ORIENTATION, orientation);

    // Position
    float position[3] = {cam.position.x, cam.position.y, cam.position.z};
    alListenerfv(AL_POSITION, position);

    // @todo ShFil119 it should be implemented
    // Velocity
}

void SoundManager::setSoundPosition(const std::string& name,
                                    const glm::vec3& position) {
    if (sounds.find(name) != sounds.end()) {
        alCheck(alSource3f(sounds[name].buffer->source, AL_POSITION, position.x,
                           position.y, position.z));
    }
}

void SoundManager::setVolume(float vol) {
    _volume = vol;
}

float SoundManager::getCalculatedVolumeOfEffects() const {
    return _volume * _effectsVolume;
}

float SoundManager::getCalculatedVolumeOfMusic() const {
    return _volume * _musicVolume;
}


{
    if (backgroundNoise.length() > 0) {
        if (p) {
            pauseAllSounds();
        } else {
            resumeAllSounds();
        }
    }
}

#include "audio/SoundManager.hpp"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
}

#include "audio/Sound.hpp"
#include "audio/SoundBuffer.hpp"
#include "audio/SoundBufferStreamed.hpp"
#include "audio/SoundSource.hpp"
#include "audio/alCheck.hpp"
#include "engine/GameData.hpp"
#include "engine/GameWorld.hpp"
#include "render/ViewCamera.hpp"

#include <rw/types.hpp>

Sound& SoundManager::getSfxBufferRef(size_t name) {
    auto ref = buffers.find(name);
    if (ref != buffers.end()) {
        return ref->second;
    } else {
        createSfxInstance(name);
    }
    return buffers[name];
}

Sound& SoundManager::getSfxSourceRef(size_t name) {
    return sfx[name];
}

Sound& SoundManager::getSoundRef(const std::string& name) {
    return sounds[name];  // @todo reloading, how to check is it wav/mp3?
}

SoundManager::SoundManager() {
    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::SoundManager(GameWorld* engine) : _engine(engine) {
    auto sdtPath = _engine->data->index.findFilePath("audio/sfx.SDT");
    auto rawPath = _engine->data->index.findFilePath("audio/sfx.RAW");
    sdt.load(sdtPath, rawPath);

    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::~SoundManager() {
    deinitializeOpenAL();
}

bool SoundManager::initializeOpenAL() {
    alDevice = alcOpenDevice(nullptr);
    if (!alDevice) {
        RW_ERROR("Could not find OpenAL device!");
        return false;
    }

    alContext = alcCreateContext(alDevice, nullptr);
    if (!alContext) {
        RW_ERROR("Could not create OpenAL context!");
        return false;
    }

    if (!alcMakeContextCurrent(alContext)) {
        RW_ERROR("Unable to make OpenAL context current!");
        return false;
    }

    // Needed for max distance
    alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);

    return true;
}

void SoundManager::initializeAVCodec() {
#if defined(RW_DEBUG) && defined(RW_VERBOSE_DEBUG_MESSAGES)
    av_log_set_level(AV_LOG_WARNING);
#else
    av_log_set_level(AV_LOG_ERROR);
#endif

    // Some older versions of FFmpeg require it
    // before calling  avformat_alloc_context()
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
    av_register_all();
#endif
}

void SoundManager::deinitializeOpenAL() {
    // Buffers have to been removed before openAL is deinitialized
    sounds.clear();
    buffers.clear();

    // De-initialize OpenAL
    if (alContext) {
        alcMakeContextCurrent(nullptr);
        alcDestroyContext(alContext);
    }
    alContext = nullptr;

    if (alDevice) {
        alcCloseDevice(alDevice);
    }
    alDevice = nullptr;
}

bool SoundManager::loadSound(const std::string& name,
                             const std::string& fileName, bool streamed) {
    Sound* sound = nullptr;
    auto sound_iter = sounds.find(name);

    if (sound_iter != sounds.end()) {
        sound = &sound_iter->second;
    } else {
        auto [it, emplaced] = sounds.emplace(std::piecewise_construct,
                                             std::forward_as_tuple(name),
                                             std::forward_as_tuple());
        sound = &it->second;

        sound->source = std::make_shared<SoundSource>();
        sound->buffer = streamed ? std::make_unique<SoundBufferStreamed>() : std::make_unique<SoundBuffer>();

        sound->source->loadFromFile(fileName, streamed);
        sound->isLoaded = sound->buffer->bufferData(*sound->source);
    }

    return sound->isLoaded;
}

void SoundManager::loadSound(size_t index) {
    Sound* sound = nullptr;

    auto [it, emplaced] =
        sfx.emplace(std::piecewise_construct, std::forward_as_tuple(index),
                    std::forward_as_tuple());
    sound = &it->second;

    sound->source = std::make_shared<SoundSource>();
    sound->source->loadSfx(sdt, index);
}

size_t SoundManager::createSfxInstance(size_t index) {
    Sound* sound = nullptr;
    auto soundRef = sfx.find(index);

    if (soundRef == sfx.end()) {
        // Sound source is not loaded yet
        loadSound(index);
        soundRef = sfx.find(index);
    }

    // Try to reuse first available buffer
    // (aka with stopped state)
    for (auto& [id, sound] : buffers) {
        if (sound.buffer && sound.isStopped()) {
            // Let's use this buffer
            sound.buffer = std::make_unique<SoundBuffer>();
            sound.source = soundRef->second.source;
            sound.isLoaded = sound.buffer->bufferData(*sound.source);
            return id;
        }
    }
    // There's no available free buffer, so
    // we should create a new one.
    auto [it, emplaced] = buffers.emplace(std::piecewise_construct,
                                          std::forward_as_tuple(bufferNr),
                                          std::forward_as_tuple());
    sound = &it->second;

    sound->id = bufferNr;
    sound->buffer = std::make_unique<SoundBuffer>();
    sound->source = soundRef->second.source;
    sound->isLoaded = sound->buffer->bufferData(*sound->source);
    bufferNr++;

    return sound->id;
}

bool SoundManager::isLoaded(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isLoaded;
    }
    return false;
}

bool SoundManager::isPlaying(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPlaying();
    }
    return false;
}

bool SoundManager::isStopped(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isStopped();
    }
    return false;
}

bool SoundManager::isPaused(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPaused();
    }
    return false;
}

void SoundManager::playSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        auto vol = getCalculatedVolumeOfMusic();
        sound->second.setGain(vol);
        sound->second.play();
    }
}

void SoundManager::eraseSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sounds.erase(sound);
    } else {
        RW_MESSAGE("Tried to erase no existing sound " << name);
    }
}

void SoundManager::playSfx(size_t name, const glm::vec3& position, bool looping,
                           int maxDist) {
    auto buffer = buffers.find(name);
    if (buffer != buffers.end()) {
        buffer->second.setPosition(position);
        if (looping) {
            buffer->second.setLooping(looping);
        }

        buffer->second.setPitch(1.f);
        buffer->second.setGain(getCalculatedVolumeOfEffects());
        if (maxDist != -1) {
            buffer->second.setMaxDistance(static_cast<float>(maxDist));
        }
        buffer->second.play();
    }
}

void SoundManager::pauseAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
}

void SoundManager::resumeAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
}

bool SoundManager::playBackground(const std::string& fileName) {
    if (this->loadSound(fileName, fileName)) {
        backgroundNoise = fileName;
        auto& sound = getSoundRef(fileName);
        sound.play();
        return true;
    }

    return false;
}

bool SoundManager::loadMusic(const std::string& name,
                             const std::string& fileName) {
    return loadSound(name, fileName);
}

void SoundManager::playMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.play();
    }
}

void SoundManager::stopMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.stop();
    }
}

void SoundManager::pause(bool p)

void SoundManager::updateListenerTransform(const ViewCamera& cam) {
    // Orientation
    auto up = cam.rotation * glm::vec3(0.f, 0.f, 1.f);
    auto at = cam.rotation * glm::vec3(1.f, 0.f, 0.f);
    float orientation[6] = {at.x, at.y, at.z, up.x, up.y, up.z};
    alListenerfv(AL_ORIENTATION, orientation);

    // Position
    float position[3] = {cam.position.x, cam.position.y, cam.position.z};
    alListenerfv(AL_POSITION, position);

    // @todo ShFil119 it should be implemented
    // Velocity
}

void SoundManager::setSoundPosition(const std::string& name,
                                    const glm::vec3& position) {
    if (sounds.find(name) != sounds.end()) {
        alCheck(alSource3f(sounds[name].buffer->source, AL_POSITION, position.x,
                           position.y, position.z));
    }
}

void SoundManager::setVolume(float vol) {
    _volume = vol;
}

float SoundManager::getCalculatedVolumeOfEffects() const {
    return _volume * _effectsVolume;
}

float SoundManager::getCalculatedVolumeOfMusic() const {
    return _volume * _musicVolume;
}


#include "audio/SoundManager.hpp"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
}

#include "audio/Sound.hpp"
#include "audio/SoundBuffer.hpp"
#include "audio/SoundBufferStreamed.hpp"
#include "audio/SoundSource.hpp"
#include "audio/alCheck.hpp"
#include "engine/GameData.hpp"
#include "engine/GameWorld.hpp"
#include "render/ViewCamera.hpp"

#include <rw/types.hpp>

Sound& SoundManager::getSfxBufferRef(size_t name) {
    auto ref = buffers.find(name);
    if (ref != buffers.end()) {
        return ref->second;
    } else {
        createSfxInstance(name);
    }
    return buffers[name];
}

Sound& SoundManager::getSfxSourceRef(size_t name) {
    return sfx[name];
}

Sound& SoundManager::getSoundRef(const std::string& name) {
    return sounds[name];  // @todo reloading, how to check is it wav/mp3?
}

SoundManager::SoundManager() {
    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::SoundManager(GameWorld* engine) : _engine(engine) {
    auto sdtPath = _engine->data->index.findFilePath("audio/sfx.SDT");
    auto rawPath = _engine->data->index.findFilePath("audio/sfx.RAW");
    sdt.load(sdtPath, rawPath);

    initializeOpenAL();
    initializeAVCodec();
}

SoundManager::~SoundManager() {
    deinitializeOpenAL();
}

bool SoundManager::initializeOpenAL() {
    alDevice = alcOpenDevice(nullptr);
    if (!alDevice) {
        RW_ERROR("Could not find OpenAL device!");
        return false;
    }

    alContext = alcCreateContext(alDevice, nullptr);
    if (!alContext) {
        RW_ERROR("Could not create OpenAL context!");
        return false;
    }

    if (!alcMakeContextCurrent(alContext)) {
        RW_ERROR("Unable to make OpenAL context current!");
        return false;
    }

    // Needed for max distance
    alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);

    return true;
}

void SoundManager::initializeAVCodec() {
#if defined(RW_DEBUG) && defined(RW_VERBOSE_DEBUG_MESSAGES)
    av_log_set_level(AV_LOG_WARNING);
#else
    av_log_set_level(AV_LOG_ERROR);
#endif

    // Some older versions of FFmpeg require it
    // before calling  avformat_alloc_context()
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
    av_register_all();
#endif
}

void SoundManager::deinitializeOpenAL() {
    // Buffers have to been removed before openAL is deinitialized
    sounds.clear();
    buffers.clear();

    // De-initialize OpenAL
    if (alContext) {
        alcMakeContextCurrent(nullptr);
        alcDestroyContext(alContext);
    }
    alContext = nullptr;

    if (alDevice) {
        alcCloseDevice(alDevice);
    }
    alDevice = nullptr;
}

bool SoundManager::loadSound(const std::string& name,
                             const std::string& fileName, bool streamed) {
    Sound* sound = nullptr;
    auto sound_iter = sounds.find(name);

    if (sound_iter != sounds.end()) {
        sound = &sound_iter->second;
    } else {
        auto [it, emplaced] = sounds.emplace(std::piecewise_construct,
                                             std::forward_as_tuple(name),
                                             std::forward_as_tuple());
        sound = &it->second;

        sound->source = std::make_shared<SoundSource>();
        sound->buffer = streamed ? std::make_unique<SoundBufferStreamed>() : std::make_unique<SoundBuffer>();

        sound->source->loadFromFile(fileName, streamed);
        sound->isLoaded = sound->buffer->bufferData(*sound->source);
    }

    return sound->isLoaded;
}

void SoundManager::loadSound(size_t index) {
    Sound* sound = nullptr;

    auto [it, emplaced] =
        sfx.emplace(std::piecewise_construct, std::forward_as_tuple(index),
                    std::forward_as_tuple());
    sound = &it->second;

    sound->source = std::make_shared<SoundSource>();
    sound->source->loadSfx(sdt, index);
}

size_t SoundManager::createSfxInstance(size_t index) {
    Sound* sound = nullptr;
    auto soundRef = sfx.find(index);

    if (soundRef == sfx.end()) {
        // Sound source is not loaded yet
        loadSound(index);
        soundRef = sfx.find(index);
    }

    // Try to reuse first available buffer
    // (aka with stopped state)
    for (auto& [id, sound] : buffers) {
        if (sound.buffer && sound.isStopped()) {
            // Let's use this buffer
            sound.buffer = std::make_unique<SoundBuffer>();
            sound.source = soundRef->second.source;
            sound.isLoaded = sound.buffer->bufferData(*sound.source);
            return id;
        }
    }
    // There's no available free buffer, so
    // we should create a new one.
    auto [it, emplaced] = buffers.emplace(std::piecewise_construct,
                                          std::forward_as_tuple(bufferNr),
                                          std::forward_as_tuple());
    sound = &it->second;

    sound->id = bufferNr;
    sound->buffer = std::make_unique<SoundBuffer>();
    sound->source = soundRef->second.source;
    sound->isLoaded = sound->buffer->bufferData(*sound->source);
    bufferNr++;

    return sound->id;
}

bool SoundManager::isLoaded(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isLoaded;
    }
    return false;
}

bool SoundManager::isPlaying(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPlaying();
    }
    return fa

}

bool SoundManager::isStopped(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isStopped();
    }
    return false;
}

bool SoundManager::isPaused(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        return sound->second.isPaused();
    }
    return false;
}

void SoundManager::playSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        auto vol = getCalculatedVolumeOfMusic();
        sound->second.setGain(vol);
        sound->second.play();
    }
}

void SoundManager::eraseSound(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sounds.erase(sound);
    } else {
        RW_MESSAGE("Tried to erase no existing sound " << name);
    }
}

void SoundManager::playSfx(size_t name, const glm::vec3& position, bool looping,
                           int maxDist) {
    auto buffer = buffers.find(name);
    if (buffer != buffers.end()) {
        buffer->second.setPosition(position);
        if (looping) {
            buffer->second.setLooping(looping);
        }

        buffer->second.setPitch(1.f);
        buffer->second.setGain(getCalculatedVolumeOfEffects());
        if (maxDist != -1) {
            buffer->second.setMaxDistance(static_cast<float>(maxDist));
        }
        buffer->second.play();
    }
}

void SoundManager::pauseAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPlaying()) {
            sound.second.pause();
        }
    }
}

void SoundManager::resumeAllSounds() {
    for (auto& sound : sounds) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
    for (auto& sound : buffers) {
        if (sound.second.isPaused()) {
            sound.second.play();
        }
    }
}

bool SoundManager::playBackground(const std::string& fileName) {
    if (this->loadSound(fileName, fileName)) {
        backgroundNoise = fileName;
        auto& sound = getSoundRef(fileName);
        sound.play();
        return true;
    }

    return false;
}

bool SoundManager::loadMusic(const std::string& name,
                             const std::string& fileName) {
    return loadSound(name, fileName);
}

void SoundManager::playMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.play();
    }
}

void SoundManager::stopMusic(const std::string& name) {
    auto sound = sounds.find(name);
    if (sound != sounds.end()) {
        sound->second.stop();
    }
}

void SoundManager::pause(bool p) {
    if (backgroundNoise.length() > 0) {
        if (p) {
            pauseAllSounds();
        } else {
            resumeAllSounds();
        }
    }
}

void SoundManager::updateListenerTransform(const ViewCamera& cam) {
    // Orientation
    auto up = cam.rotation * glm::vec3(0.f, 0.f, 1.f);
    auto at = cam.rotation * glm::vec3(1.f, 0.f, 0.f);
    float orientation[6] = {at.x, at.y, at.z, up.x, up.y, up.z};
    alListenerfv(AL_ORIENTATION, orientation);

    // Position
    float position[3] = {cam.position.x, cam.position.y, cam.position.z};
    alListenerfv(AL_POSITION, position);

    // @todo ShFil119 it should be implemented
    // Velocity
}

void SoundManager::setSoundPosition(const std::string& name,
                                    const glm::vec3& position) {
    if (sounds.find(name) != sounds.end()) {
        alCheck(alSource3f(sounds[name].buffer->source, AL_POSITION, position.x,
                           position.y, position.z));
    }
}

void SoundManager::setVolume(float vol) {
    _volume = vol;
}

float SoundManager::getCalculatedVolumeOfEffects() const {
    return _volume * _effectsVolume;
}

float SoundManager::getCalculatedVolumeOfMusic() const {
    return _volume * _musicVolume;
}


void MCMachObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                uint64_t Address)

//                     The LLVM Compiler Infrastructure

#include "llvm/MC/MCObjectSymbolizer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRelocationInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace llvm;
using namespace object;

namespace {
class MCMachObjectSymbolizer : public MCObjectSymbolizer {
  const MachOObjectFile *MOOF;
  // __TEXT;__stubs support.
  uint64_t StubsStart;
  uint64_t StubsCount;
  uint64_t StubSize;
  uint64_t StubsIndSymIndex;

public:
  MCMachObjectSymbolizer(MCContext &Ctx,
                         std::unique_ptr<MCRelocationInfo> RelInfo,
                         const MachOObjectFile *MOOF);

  StringRef findExternalFunctionAt(uint64_t Addr) override;

  void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                       uint64_t Address) override;
};
} // End unnamed namespace

MCMachObjectSymbolizer::MCMachObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const MachOObjectFile *MOOF)
  : MCObjectSymbolizer(Ctx, std::move(RelInfo), MOOF), MOOF(MOOF),
    StubsStart(0), StubsCount(0), StubSize(0), StubsIndSymIndex(0) {

  for (const SectionRef &Section : MOOF->sections()) {
    StringRef Name;
    Section.getName(Name);
    if (Name == "__stubs") {
      SectionRef StubsSec = Section;
      if (MOOF->is64Bit()) {
        MachO::section_64 S = MOOF->getSection64(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      } else {
        MachO::section S = MOOF->getSection(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      }
      assert(StubSize && "Mach-O stub entry size can't be zero!");
      StubsSec.getAddress(StubsStart);
      StubsSec.getSize(StubsCount);
      StubsCount /= StubSize;
    }
  }
}

StringRef MCMachObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  // FIXME: also, this can all be done at the very beginning, by iterating over
  // all stubs and creating the calls to outside functions. Is it worth it
  if (!StubSize)
    return StringRef();
  uint64_t StubIdx = (Addr - StubsStart) / StubSize;
  if (StubIdx >= StubsCount)
    return StringRef();

  uint32_t SymtabIdx =
    MOOF->getIndirectSymbolTableEntry(MOOF->getDysymtabLoadCommand(), StubIdx);

  StringRef SymName;
  symbol_iterator SI = MOOF->symbol_begin();
  for (uint32_t i = 0; i != SymtabIdx; ++i)
    ++SI;
  SI->getName(SymName);
  assert(SI != MOOF->symbol_end() && "Stub wasn't found in the symbol table!");
  assert(SymName.front() == '_' && "Mach-O symbol doesn't start with '_'!");
  return SymName.substr(1);
}

{
  if (const RelocationRef *R = findRelocationAt(Address)) {
    const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R);
    if (!RelExpr || RelExpr->EvaluateAsAbsolute(Value) == false)
      return;
  }
  uint64_t Addr = Value;
  if (const SectionRef *S = findSectionContaining(Addr)) {
    StringRef Name; S->getName(Name);
    uint64_t SAddr; S->getAddress(SAddr);
    if (Name == "__cstring") {
      StringRef Contents;
      S->getContents(Contents);
      Contents = Contents.substr(Addr - SAddr);
      cStream << " ## literal pool for: "
              << Contents.substr(0, Contents.find_first_of(0));
    }
  }
}

MCObjectSymbolizer::MCObjectSymbolizer(
  MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
  const ObjectFile *Obj)
  : MCSymbolizer(Ctx, std::move(RelInfo)), Obj(Obj), SortedSections(),
    AddrToReloc() {}

bool MCObjectSymbolizer::
tryAddingSymbolicOperand(MCInst &MI, raw_ostream &cStream,
                         int64_t Value, uint64_t Address, bool IsBranch,
                         uint64_t Offset, uint64_t InstSize) {
  if (IsBranch) {
    StringRef ExtFnName = findExternalFunctionAt((uint64_t)Value);
    if (!ExtFnName.empty()) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(ExtFnName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }

  if (const RelocationRef *R = findRelocationAt(Address + Offset)) {
    if (const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R)) {
      MI.addOperand(MCOperand::CreateExpr(RelExpr));
      return true;
    }
    // Only try to create a symbol+offset expression if there is no relocation.
    return false;
  }

  // Interpret Value as a branch target.
  if (IsBranch == false)
    return false;
  uint64_t UValue = Value;
  // FIXME: map instead of looping each time?
  for (const SymbolRef &Symbol : Obj->symbols()) {
    uint64_t SymAddr;
    Symbol.getAddress(SymAddr);
    uint64_t SymSize;
    Symbol.getSize(SymSize);
    StringRef SymName;
    Symbol.getName(SymName);
    SymbolRef::Type SymType;
    Symbol.getType(SymType);
    if (SymAddr == UnknownAddressOrSize || SymSize == UnknownAddressOrSize ||
        SymName.empty() || SymType != SymbolRef::ST_Function)
      continue;

    if ( SymAddr == UValue ||
        (SymAddr <= UValue && SymAddr + SymSize > UValue)) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(SymName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      if (SymAddr != UValue) {
        const MCExpr *Off = MCConstantExpr::Create(UValue - SymAddr, Ctx);
        Expr = MCBinaryExpr::CreateAdd(Expr, Off, Ctx);
      }
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }
  return false;
}

void MCObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream,
                                int64_t Value, uint64_t Address) {
}

StringRef MCObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  return StringRef();
}

MCObjectSymbolizer *MCObjectSymbolizer::createObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const ObjectFile *Obj) {
  if (const MachOObjectFile *MOOF = dyn_cast<MachOObjectFile>(Obj))
    return new MCMachObjectSymbolizer(Ctx, std::move(RelInfo), MOOF);
  return new MCObjectSymbolizer(Ctx, std::move(RelInfo), Obj);
}

// SortedSections implementation.

static bool SectionStartsBefore(const SectionRef &S, uint64_t Addr) {
  uint64_t SAddr; S.getAddress(SAddr);
  return SAddr < Addr;
}

const SectionRef *MCObjectSymbolizer::findSectionContaining(uint64_t Addr) {
  if (SortedSections.empty())
    buildSectionList();

  SortedSectionList::iterator
    EndIt = SortedSections.end(),
    It = std::lower_bound(SortedSections.begin(), EndIt,
                          Addr, SectionStartsBefore);
  if (It == EndIt)
    return nullptr;
  uint64_t SAddr; It->getAddress(SAddr);
  uint64_t SSize; It->getSize(SSize);
  if (Addr >= SAddr + SSize)
    return nullptr;
  return &*It;
}

const RelocationRef *MCObjectSymbolizer::findRelocationAt(uint64_t Addr) {
  if (AddrToReloc.empty())
    buildRelocationByAddrMap();

  AddrToRelocMap::const_iterator RI = AddrToReloc.find(Addr);
  if (RI == AddrToReloc.end())
    return nullptr;
  return &RI->second;
}

void MCObjectSymbolizer::buildSectionList() {
  for (const SectionRef &Section : Obj->sections()) {
    bool RequiredForExec;
    Section.isRequiredForExecution(RequiredForExec);
    if (RequiredForExec == false)
      continue;
    uint64_t SAddr;
    Section.getAddress(SAddr);
    uint64_t SSize;
    Section.getSize(SSize);
    SortedSectionList::iterator It =
        std::lower_bound(SortedSections.begin(), SortedSections.end(), SAddr,
                         SectionStartsBefore);
    if (It != SortedSections.end()) {
      uint64_t FoundSAddr; It->getAddress(FoundSAddr);
      if (FoundSAddr < SAddr + SSize)
        llvm_unreachable("Inserting overlapping sections");
    }
    SortedSections.insert(It, Section);
  }
}

void MCObjectSymbolizer::buildRelocationByAddrMap() {
  for (const SectionRef &Section : Obj->sections()) {
    for (const RelocationRef &Reloc : Section.relocations()) {
      uint64_t Address;
      Reloc.getAddress(Address);
      // At a specific address, only keep the first relocation.
      if (AddrToReloc.find(Address) == AddrToReloc.end())
        AddrToReloc[Address] = Reloc;
    }
  }
}


//                     The LLVM Compiler Infrastructure

#include "llvm/MC/MCObjectSymbolizer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRelocationInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace llvm;
using namespace object;

namespace {
class MCMachObjectSymbolizer : public MCObjectSymbolizer {
  const MachOObjectFile *MOOF;
  // __TEXT;__stubs support.
  uint64_t StubsStart;
  uint64_t StubsCount;
  uint64_t StubSize;
  uint64_t StubsIndSymIndex;

public:
  MCMachObjectSymbolizer(MCContext &Ctx,
                         std::unique_ptr<MCRelocationInfo> RelInfo,
                         const MachOObjectFile *MOOF);

  StringRef findExternalFunctionAt(uint64_t Addr) override;

  void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                       uint64_t Address) override;
};
} // End unnamed namespace

MCMachObjectSymbolizer::MCMachObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const MachOObjectFile *MOOF)
  : MCObjectSymbolizer(Ctx, std::move(RelInfo), MOOF), MOOF(MOOF),
    StubsStart(0), StubsCount(0), StubSize(0), StubsIndSymIndex(0) {

  for (const SectionRef &Section : MOOF->sections()) {
    StringRef Name;
    Section.getName(Name);
    if (Name == "__stubs") {
      SectionRef StubsSec = Section;
      if (MOOF->is64Bit()) {
        MachO::section_64 S = MOOF->getSection64(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      } else {
        MachO::section S = MOOF->getSection(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      }
      assert(StubSize && "Mach-O stub entry size can't be zero!");
      StubsSec.getAddress(StubsStart);
      StubsSec.getSize(StubsCount);
      StubsCount /= StubSize;
    }
  }
}

StringRef MCMachObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  // FIXME: also, this can all be done at the very beginning, by iterating over
  // all stubs and creating the calls to outside functions. Is it worth it
  if (!StubSize)
    return StringRef();
  uint64_t StubIdx = (Addr - StubsStart) / StubSize;
  if (StubIdx >= StubsCount)
    return StringRef();

  uint32_t SymtabIdx =
    MOOF->getIndirectSymbolTableEntry(MOOF->getDysymtabLoadCommand(), StubIdx);

  StringRef SymName;
  symbol_iterator SI = MOOF->symbol_begin();
  for (uint32_t i = 0; i != SymtabIdx; ++i)
    ++SI;
  SI->getName(SymName);
  assert(SI != MOOF->symbol_end() && "Stub wasn't found in the symbol table!");
  assert(SymName.front() == '_' && "Mach-O symbol doesn't start with '_'!");
  return SymName.substr(1);
}

void MCMachObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                uint64_t Address) {
  if (const RelocationRef *R = findRelocationAt(Address)) {
    const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R);
    if (!RelExpr || RelExpr->EvaluateAsAbsolute(Value) == false)
      return;
  }
  uint64_t Addr = Value;
  if (const SectionRef *S = findSectionContaining(Addr)) {
    StringRef Name; S->getName(Name);
    uint64_t SAddr; S->getAddress(SAddr);
    if (Name == "__cstring") {
      StringRef Contents;
      S->getContents(Contents);
      Contents = Contents.substr(Addr - SAddr);
      cStream << " ## literal pool for: "
              << Contents.substr(0, Contents.find_first_of(0));
    }
  }
}

MCObjectSymbolizer::MCObjectSymbolizer(
  MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
  const ObjectFile *Obj)
  : MCSymbolizer(Ctx, std::move(RelInfo)), Obj(Obj), SortedSections(),
    AddrToReloc()

bool MCObjectSymbolizer::
tryAddingSymbolicOperand(MCInst &MI, raw_ostream &cStream,
                         int64_t Value, uint64_t Address, bool IsBranch,
                         uint64_t Offset, uint64_t InstSize) {
  if (IsBranch) {
    StringRef ExtFnName = findExternalFunctionAt((uint64_t)Value);
    if (!ExtFnName.empty()) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(ExtFnName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }

  if (const RelocationRef *R = findRelocationAt(Address + Offset)) {
    if (const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R)) {
      MI.addOperand(MCOperand::CreateExpr(RelExpr));
      return true;
    }
    // Only try to create a symbol+offset expression if there is no relocation.
    return false;
  }

  // Interpret Value as a branch target.
  if (IsBranch == false)
    return false;
  uint64_t UValue = Value;
  // FIXME: map instead of looping each time?
  for (const SymbolRef &Symbol : Obj->symbols()) {
    uint64_t SymAddr;
    Symbol.getAddress(SymAddr);
    uint64_t SymSize;
    Symbol.getSize(SymSize);
    StringRef SymName;
    Symbol.getName(SymName);
    SymbolRef::Type SymType;
    Symbol.getType(SymType);
    if (SymAddr == UnknownAddressOrSize || SymSize == UnknownAddressOrSize ||
        SymName.empty() || SymType != SymbolRef::ST_Function)
      continue;

    if ( SymAddr == UValue ||
        (SymAddr <= UValue && SymAddr + SymSize > UValue)) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(SymName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      if (SymAddr != UValue) {
        const MCExpr *Off = MCConstantExpr::Create(UValue - SymAddr, Ctx);
        Expr = MCBinaryExpr::CreateAdd(Expr, Off, Ctx);
      }
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }
  return false;
}

void MCObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream,
                                int64_t Value, uint64_t Address) {
}

StringRef MCObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  return StringRef();
}

MCObjectSymbolizer *MCObjectSymbolizer::createObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const ObjectFile *Obj) {
  if (const MachOObjectFile *MOOF = dyn_cast<MachOObjectFile>(Obj))
    return new MCMachObjectSymbolizer(Ctx, std::move(RelInfo), MOOF);
  return new MCObjectSymbolizer(Ctx, std::move(RelInfo), Obj);
}

// SortedSections implementation.

static bool SectionStartsBefore(const SectionRef &S, uint64_t Addr) {
  uint64_t SAddr; S.getAddress(SAddr);
  return SAddr < Addr;
}

const SectionRef *MCObjectSymbolizer::findSectionContaining(uint64_t Addr) {
  if (SortedSections.empty())
    buildSectionList();

  SortedSectionList::iterator
    EndIt = SortedSections.end(),
    It = std::lower_bound(SortedSections.begin(), EndIt,
                          Addr, SectionStartsBefore);
  if (It == EndIt)
    return nullptr;
  uint64_t SAddr; It->getAddress(SAddr);
  uint64_t SSize; It->getSize(SSize);
  if (Addr >= SAddr + SSize)
    return nullptr;
  return &*It;
}

const RelocationRef *MCObjectSymbolizer::findRelocationAt(uint64_t Addr) {
  if (AddrToReloc.empty())
    buildRelocationByAddrMap();

  AddrToRelocMap::const_iterator RI = AddrToReloc.find(Addr);
  if (RI == AddrToReloc.end())
    return nullptr;
  return &RI->second;
}

void MCObjectSymbolizer::buildSectionList() {
  for (const SectionRef &Section : Obj->sections()) {
    bool RequiredForExec;
    Section.isRequiredForExecution(RequiredForExec);
    if (RequiredForExec == false)
      continue;
    uint64_t SAddr;
    Section.getAddress(SAddr);
    uint64_t SSize;
    Section.getSize(SSize);
    SortedSectionList::iterator It =
        std::lower_bound(SortedSections.begin(), SortedSections.end(), SAddr,
                         SectionStartsBefore);
    if (It != SortedSections.end()) {
      uint64_t FoundSAddr; It->getAddress(FoundSAddr);
      if (FoundSAddr < SAddr + SSize)
        llvm_unreachable("Inserting overlapping sections");
    }
    SortedSections.insert(It, Section);
  }
}

void MCObjectSymbolizer::buildRelocationByAddrMap() {
  for (const SectionRef &Section : Obj->sections()) {
    for (const RelocationRef &Reloc : Section.relocations()) {
      uint64_t Address;
      Reloc.getAddress(Address);
      // At a specific address, only keep the first relocation.
      if (AddrToReloc.find(Address) == AddrToReloc.end())
        AddrToReloc[Address] = Reloc;
    }
  }
}


//                     The LLVM Compiler Infrastructure

#include "llvm/MC/MCObjectSymbolizer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRelocationInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace llvm;
using namespace object;

namespace {
class MCMachObjectSymbolizer : public MCObjectSymbolizer {
  const MachOObjectFile *MOOF;
  // __TEXT;__stubs support.
  uint64_t StubsStart;
  uint64_t StubsCount;
  uint64_t StubSize;
  uint64_t StubsIndSymIndex;

public:
  MCMachObjectSymbolizer(MCContext &Ctx,
                         std::unique_ptr<MCRelocationInfo> RelInfo,
                         const MachOObjectFile *MOOF);

  StringRef findExternalFunctionAt(uint64_t Addr) override;

  void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                       uint64_t Address) override;
};
} // End unnamed namespace

MCMachObjectSymbolizer::MCMachObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const MachOObjectFile *MOOF)
  : MCObjectSymbolizer(Ctx, std::move(RelInfo), MOOF), MOOF(MOOF),
    StubsStart(0), StubsCount(0), StubSize(0), StubsIndSymIndex(0) {

  for (const SectionRef &Section : MOOF->sections()) {
    StringRef Name;
    Section.getName(Name);
    if (Name == "__stubs") {
      SectionRef StubsSec = Section;
      if (MOOF->is64Bit()) {
        MachO::section_64 S = MOOF->getSection64(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      } else {
        MachO::section S = MOOF->getSection(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      }
      assert(StubSize && "Mach-O stub entry size can't be zero!");
      StubsSec.getAddress(StubsStart);
      StubsSec.getSize(StubsCount);
      StubsCount /= StubSize;
    }
  }
}

StringRef MCMachObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  // FIXME: also, this can all be done at the very beginning, by iterating over
  // all stubs and creating the calls to outside functions. Is it worth it
  if (!StubSize)
    return StringRef();
  uint64_t StubIdx = (Addr - StubsStart) / StubSize;
  if (StubIdx >= StubsCount)
    return StringRef();

  uint32_t SymtabIdx =
    MOOF->getIndirectSymbolTableEntry(MOOF->getDysymtabLoadCommand(), StubIdx);

  StringRef SymName;
  symbol_iterator SI = MOOF->symbol_begin();
  for (uint32_t i = 0; i != SymtabIdx; ++i)
    ++SI;
  SI->getName(SymName);
  assert(SI != MOOF->symbol_end() && "Stub wasn't found in the symbol table!");
  assert(SymName.front() == '_' && "Mach-O symbol doesn't start with '_'!");
  return SymName.substr(1);
}

void MCMachObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                uint64_t Address) {
  if (const RelocationRef *R = findRelocationAt(Address)) {
    const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R);
    if (!RelExpr || RelExpr->EvaluateAsAbsolute(Value) == false)
      return;
  }
  uint64_t Addr = Value;
  if (const SectionRef *S = findSectionContaining(Addr)) {
    StringRef Name; S->getName(Name);
    uint64_t SAddr; S->getAddress(SAddr);
    if (Name == "__cstring") {
      StringRef Contents;
      S->getContents(Contents);
      Contents = Contents.substr(Addr - SAddr);
      cStream << " ## literal pool for: "
              << Contents.substr(0, Contents.find_first_of(0));
    }
  }
}

MCObjectSymbolizer::MCObjectSymbolizer(
  MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
  const ObjectFile *Obj)
  : MCSymbolizer(Ctx, std::move(RelInfo)), Obj(Obj), SortedSections(),
    AddrToReloc() {}

bool MCObjectSymbolizer::
tryAddingSymbolicOperand(MCInst &MI, raw_ostream &cStream,
                         int64_t Value, uint64_t Address, bool IsBranch,
                         uint64_t Offset, uint64_t InstSize) {
  if (IsBranch) {
    StringRef ExtFnName = findExternalFunctionAt((uint64_t)Value);
    if (!ExtFnName.empty()) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(ExtFnName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }

  if (const RelocationRef *R = findRelocationAt(Address + Offset)) {
    if (const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R)) {
      MI.addOperand(MCOperand::CreateExpr(RelExpr));
      return true;
    }
    // Only try to create a symbol+offset expression if there is no relocation.
    return false;
  }

  // Interpret Value as a branch target.
  if (IsBranch == false)
    return false;
  uint64_t UValue = Value;
  // FIXME: map instead of looping each time?
  for (const SymbolRef &Symbol : Obj->symbols()) {
    uint64_t SymAddr;
    Symbol.getAddress(SymAddr);
    uint64_t SymSize;
    Symbol.getSize(SymSize);
    StringRef SymName;
    Symbol.getName(SymName);
    SymbolRef::Type SymType;
    Symbol.getType(SymType);
    if (SymAddr == UnknownAddressOrSize || SymSize == UnknownAddressOrSize ||
        SymName.empty() || SymType != SymbolRef::ST_Function)
      continue;

    if ( SymAddr == UValue ||
        (SymAddr <= UValue && SymAddr + SymSize > UValue)) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(SymName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      if (SymAddr != UValue) {
        const MCExpr *Off = MCConstantExpr::Create(UValue - SymAddr, Ctx);
        Expr = MCBinaryExpr::CreateAdd(Expr, Off, Ctx);
      }
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }
  return false;
}

void MCObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream,
                                int64_t Value, uint64_t Address) {
}

StringRef MCObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  return StringRef();
}

MCObjectSymbolizer *MCObjectSymbolizer::createObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const ObjectFile *Obj) {
  if (const MachOObjectFile *MOOF = dyn_cast<MachOObjectFile>(Obj))
    return new MCMachObjectSymbolizer(Ctx, std::move(RelInfo), MOOF);
  return new MCObjectSymbolizer(Ctx, std::move(RelInfo), Obj);
}

// SortedSections implementation.

static bool SectionStartsBefore(const SectionRef &S, uint64_t Addr) {
  uint64_t SAddr; S.getAddress(SAddr);
  return SAddr < Addr;
}

const SectionRef *MCObjectSymbolizer::findSectionContaining(uint64_t Addr) {
  if (SortedSections.empty())
    buildSectionList();

  SortedSectionList::iterator
    EndIt = SortedSections.end(),
    It = std::lower_bound(SortedSections.begin(), EndIt,
                          Addr, SectionStartsBefore);
  if (It == EndIt)
    return nullptr;
  uint64_t SAddr; It->getAddress(SAddr);
  uint64_t SSize; It->getSize

  if (Addr >= SAddr + SSize)
    return nullptr;
  return &*It;
}

const RelocationRef *MCObjectSymbolizer::findRelocationAt(uint64_t Addr) {
  if (AddrToReloc.empty())
    buildRelocationByAddrMap();

  AddrToRelocMap::const_iterator RI = AddrToReloc.find(Addr);
  if (RI == AddrToReloc.end())
    return nullptr;
  return &RI->second;
}

void MCObjectSymbolizer::buildSectionList() {
  for (const SectionRef &Section : Obj->sections()) {
    bool RequiredForExec;
    Section.isRequiredForExecution(RequiredForExec);
    if (RequiredForExec == false)
      continue;
    uint64_t SAddr;
    Section.getAddress(SAddr);
    uint64_t SSize;
    Section.getSize(SSize);
    SortedSectionList::iterator It =
        std::lower_bound(SortedSections.begin(), SortedSections.end(), SAddr,
                         SectionStartsBefore);
    if (It != SortedSections.end()) {
      uint64_t FoundSAddr; It->getAddress(FoundSAddr);
      if (FoundSAddr < SAddr + SSize)
        llvm_unreachable("Inserting overlapping sections");
    }
    SortedSections.insert(It, Section);
  }
}

void MCObjectSymbolizer::buildRelocationByAddrMap() {
  for (const SectionRef &Section : Obj->sections()) {
    for (const RelocationRef &Reloc : Section.relocations()) {
      uint64_t Address;
      Reloc.getAddress(Address);
      // At a specific address, only keep the first relocation.
      if (AddrToReloc.find(Address) == AddrToReloc.end())
        AddrToReloc[Address] = Reloc;
    }
  }
}


static bool SectionStartsBefore(const SectionRef &S, uint64_t Addr) {
  uint64_t SAddr; S.getAddress(SAddr);
  return SAddr < Addr;
}

//                     The LLVM Compiler Infrastructure

#include "llvm/MC/MCObjectSymbolizer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRelocationInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace llvm;
using namespace object;

namespace {
class MCMachObjectSymbolizer : public MCObjectSymbolizer {
  const MachOObjectFile *MOOF;
  // __TEXT;__stubs support.
  uint64_t StubsStart;
  uint64_t StubsCount;
  uint64_t StubSize;
  uint64_t StubsIndSymIndex;

public:
  MCMachObjectSymbolizer(MCContext &Ctx,
                         std::unique_ptr<MCRelocationInfo> RelInfo,
                         const MachOObjectFile *MOOF);

  StringRef findExternalFunctionAt(uint64_t Addr) override;

  void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                       uint64_t Address) override;
};
} // End unnamed namespace

MCMachObjectSymbolizer::MCMachObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const MachOObjectFile *MOOF)
  : MCObjectSymbolizer(Ctx, std::move(RelInfo), MOOF), MOOF(MOOF),
    StubsStart(0), StubsCount(0), StubSize(0), StubsIndSymIndex(0) {

  for (const SectionRef &Section : MOOF->sections()) {
    StringRef Name;
    Section.getName(Name);
    if (Name == "__stubs") {
      SectionRef StubsSec = Section;
      if (MOOF->is64Bit()) {
        MachO::section_64 S = MOOF->getSection64(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      } else {
        MachO::section S = MOOF->getSection(StubsSec.getRawDataRefImpl());
        StubsIndSymIndex = S.reserved1;
        StubSize = S.reserved2;
      }
      assert(StubSize && "Mach-O stub entry size can't be zero!");
      StubsSec.getAddress(StubsStart);
      StubsSec.getSize(StubsCount);
      StubsCount /= StubSize;
    }
  }
}

StringRef MCMachObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  // FIXME: also, this can all be done at the very beginning, by iterating over
  // all stubs and creating the calls to outside functions. Is it worth it
  if (!StubSize)
    return StringRef();
  uint64_t StubIdx = (Addr - StubsStart) / StubSize;
  if (StubIdx >= StubsCount)
    return StringRef();

  uint32_t SymtabIdx =
    MOOF->getIndirectSymbolTableEntry(MOOF->getDysymtabLoadCommand(), StubIdx);

  StringRef SymName;
  symbol_iterator SI = MOOF->symbol_begin();
  for (uint32_t i = 0; i != SymtabIdx; ++i)
    ++SI;
  SI->getName(SymName);
  assert(SI != MOOF->symbol_end() && "Stub wasn't found in the symbol table!");
  assert(SymName.front() == '_' && "Mach-O symbol doesn't start with '_'!");
  return SymName.substr(1);
}

void MCMachObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,
                                uint64_t Address) {
  if (const RelocationRef *R = findRelocationAt(Address)) {
    const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R);
    if (!RelExpr || RelExpr->EvaluateAsAbsolute(Value) == false)
      return;
  }
  uint64_t Addr = Value;
  if (const SectionRef *S = findSectionContaining(Addr)) {
    StringRef Name; S->getName(Name);
    uint64_t SAddr; S->getAddress(SAddr);
    if (Name == "__cstring") {
      StringRef Contents;
      S->getContents(Contents);
      Contents = Contents.substr(Addr - SAddr);
      cStream << " ## literal pool for: "
              << Contents.substr(0, Contents.find_first_of(0));
    }
  }
}

MCObjectSymbolizer::MCObjectSymbolizer(
  MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
  const ObjectFile *Obj)
  : MCSymbolizer(Ctx, std::move(RelInfo)), Obj(Obj), SortedSections(),
    AddrToReloc() {}

bool MCObjectSymbolizer::
tryAddingSymbolicOperand(MCInst &MI, raw_ostream &cStream,
                         int64_t Value, uint64_t Address, bool IsBranch,
                         uint64_t Offset, uint64_t InstSize) {
  if (IsBranch) {
    StringRef ExtFnName = findExternalFunctionAt((uint64_t)Value);
    if (!ExtFnName.empty()) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(ExtFnName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }

  if (const RelocationRef *R = findRelocationAt(Address + Offset)) {
    if (const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R)) {
      MI.addOperand(MCOperand::CreateExpr(RelExpr));
      return true;
    }
    // Only try to create a symbol+offset expression if there is no relocation.
    return false;
  }

  // Interpret Value as a branch target.
  if (IsBranch == false)
    return false;
  uint64_t UValue = Value;
  // FIXME: map instead of looping each time?
  for (const SymbolRef &Symbol : Obj->symbols()) {
    uint64_t SymAddr;
    Symbol.getAddress(SymAddr);
    uint64_t SymSize;
    Symbol.getSize(SymSize);
    StringRef SymName;
    Symbol.getName(SymName);
    SymbolRef::Type SymType;
    Symbol.getType(SymType);
    if (SymAddr == UnknownAddressOrSize || SymSize == UnknownAddressOrSize ||
        SymName.empty() || SymType != SymbolRef::ST_Function)
      continue;

    if ( SymAddr == UValue ||
        (SymAddr <= UValue && SymAddr + SymSize > UValue)) {
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(SymName);
      const MCExpr *Expr = MCSymbolRefExpr::Create(Sym, Ctx);
      if (SymAddr != UValue) {
        const MCExpr *Off = MCConstantExpr::Create(UValue - SymAddr, Ctx);
        Expr = MCBinaryExpr::CreateAdd(Expr, Off, Ctx);
      }
      MI.addOperand(MCOperand::CreateExpr(Expr));
      return true;
    }
  }
  return false;
}

void MCObjectSymbolizer::
tryAddingPcLoadReferenceComment(raw_ostream &cStream,
                                int64_t Value, uint64_t Address) {
}

StringRef MCObjectSymbolizer::findExternalFunctionAt(uint64_t Addr) {
  return StringRef();
}

MCObjectSymbolizer *MCObjectSymbolizer::createObjectSymbolizer(
    MCContext &Ctx, std::unique_ptr<MCRelocationInfo> RelInfo,
    const ObjectFile *Obj) {
  if (const MachOObjectFile *MOOF = dyn_cast<MachOObjectFile>(Obj))
    return new MCMachObjectSymbolizer(Ctx, std::move(RelInfo), MOOF);
  return new MCObjectSymbolizer(Ctx, std::move(RelInfo), Obj);
}

// SortedSections implementation.

const SectionRef *MCObjectSymbolizer::findSectionContaining(uint64_t Addr) {
  if (SortedSections.empty())
    buildSectionList();

  SortedSectionList::iterator
    EndIt = SortedSections.end(),
    It = std::lower_bound(SortedSections.begin(), EndIt,
                          Addr, SectionStartsBefore);
  if (It == EndIt)
    return nullptr;
  uint64_t SAddr; It->getAddress(SAddr);
  uint64_t SSize; It->getSize(SSize);
  if (Addr >= SAddr + SSize)
    return nullptr;
  return &*It;
}

const RelocationRef *MCObjectSymbolizer::findRelocationAt(uint64_t Addr) {
  if (AddrToReloc.empty())
    buildRelocationByAddrMap();

  AddrToRelocMap::const_iterator RI = AddrToReloc.find(Addr);
  if (RI == AddrToReloc.end())
    return nullptr;
  return &RI->second;
}

void MCObjectSymbolizer::buildSectionList() {
  for (const SectionRef &Section : Obj->sections()) {
    bool RequiredForExec;
    Section.isRequiredForExecution(RequiredForExec);
    if (RequiredForExec == false)
      continue;
    uint64_t SAddr;
    Section.getAddress(SAddr);
    uint64_t SSize;
    Section.getSize(SSize);
    SortedSectionList::iterator It =
        std::lower_bound(SortedSections.begin(), SortedSections.end(), SAddr,
                         SectionStartsBefore);
    if (It != SortedSections.end()) {
      uint64_t FoundSAddr; It->getAddress(FoundSAddr);
      if (FoundSAddr < SAddr + SSize)
        llvm_unreachable("Inserting overlapping sections");
    }
    SortedSections.insert(It, Section);
  }
}

void MCObjectSymbolizer::buildRelocationByAddrMap() {
  for (const SectionRef &Section : Obj->sections()) {
    for (const RelocationRef &Reloc : Section.relocations()) {
      uint64_t Address;
      Reloc.getAddress(Address);
      // At a specific address, only keep the first relocation.
      if (AddrToReloc.find(Address) == AddrToReloc.end())
        AddrToReloc[Address] = Reloc;
    }
  }
}


/*********************************************************\
 *  File: SceneContainer.cpp                             *
 *
 *
 *  This file is part of PixelLight.
 *
\*********************************************************/

#include <PLCore/Log/Log.h>
#include <PLCore/Base/Class.h>
#include <PLCore/Tools/Timing.h>
#include <PLCore/Tools/Loader.h>
#include <PLCore/Container/Stack.h>
#include "PLScene/Scene/SceneContext.h"
#include "PLScene/Scene/SceneHierarchy.h"
#include "PLScene/Scene/SceneHierarchyNode.h"
#include "PLScene/Scene/SceneQueryManager.h"
#include "PLScene/Scene/SceneLoader/SceneLoader.h"
#include "PLScene/Scene/SceneContainer.h"

using namespace PLCore;
using namespace PLMath;
namespace PLScene {

pl_implement_class(SceneContainer)

//[ Public RTTI get/set functions                         ]
String SceneContainer::GetHierarchy() const
{
	return m_sHierarchy;
}

void SceneContainer::SetHierarchy(const String &sValue)
{
	// Destroy the current hierarchy, but do NOT create the new one at once - this
	// is done if the new hierarchy is requested the first time  :)
	if (m_pHierarchy && m_pHierarchy->GetClass()->GetClassName() != sValue) {
		delete m_pHierarchy;
		m_pHierarchy = nullptr;
	}

	// Assign the new class hierarchy name
	m_sHierarchy = sValue;
}

void SceneContainer::SetFilename(const String &sValue)
{
	if (m_sFilename != sValue) {
		m_sFilename = sValue;

		// Load the container
		if (IsInitialized())
			LoadByFilename(m_sFilename);
	}
}

/**
*  @brief
*    Default constructor
*/
SceneContainer::SceneContainer() :
	Hierarchy(this),
	Flags(this),
	AABBMin(this),
	AABBMax(this),
	Filename(this),
	m_sHierarchy("PLScene::SHList"),
	m_pSceneContext(nullptr),
	m_pHierarchy(nullptr),
	m_pQueryManager(nullptr)
{
	// Overwritten SceneNode variables
	m_cAABoundingBox.vMin.SetXYZ(-10000.0f, -10000.0f, -10000.0f);
	m_cAABoundingBox.vMax.SetXYZ( 10000.0f,  10000.0f,  10000.0f);

	// Set the internal flag
	m_nInternalFlags |= ClassContainer;

	// Set draw function flags
	SetDrawFunctionFlags(UseDrawPre | UseDrawSolid | UseDrawTransparent | UseDrawDebug | UseDrawPost);
}

/**
*  @brief
*    Destructor
*/
SceneContainer::~SceneContainer()
{
	DeInit();
	if (m_pHierarchy)
		delete m_pHierarchy;
	if (m_pQueryManager)
		delete m_pQueryManager;
}

/**
*  @brief
*    Creates a new scene node at a certain index inside the scene node list
*/
SceneNode *SceneContainer::CreateAtIndex(const String &sClass, const String &sName, const String &sParameters, int nPosition)
{
	// Check parameter
	if (sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneNode");

		// Get scene node plugin
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				PL_LOG(Debug, "Create scene node '" + sName + "' of type '" + sClass + '\'')
				SceneNode *pNode = static_cast<SceneNode*>(pClass->Create());
				if (pNode) {

					// [TODO] Check/refactor the initialization/de-initialization process
					pNode->m_pManager = this;

					if (sParameters.GetLength())
						pNode->SetValues(sParameters);
					Add(*pNode, sName, true, nPosition);
				}

				// Return the created scene node
				return pNode;
			} else { // Plugin not found
				PL_LOG(Error, "Unknown scene node type: " + sClass)
			}
		}
	}

	// Error
	return nullptr;
}

/**
*  @brief
*    Calculates and sets the axis align bounding box in 'scene node space'
*/
void SceneContainer::CalculateAABoundingBox()
{
	AABoundingBox cAABoundingBox;

	// If there are any scene nodes...
	if (GetNumOfElements()) {
		// Set first bounding box
		cAABoundingBox = GetByIndex(0)->GetContainerAABoundingBox();

		// Combine all bounding boxes
		for (uint32 i=1; i<GetNumOfElements(); i++)
			cAABoundingBox.CombineAABoxes(GetByIndex(i)->GetContainerAABoundingBox());
	}

	// Set the final bounding box
	SetAABoundingBox(cAABoundingBox);
}

/**
*  @brief
*    Returns a transform matrix to bring a transformation from 'this scene container' into 'another scene container'
*/
bool SceneContainer::GetTransformMatrixTo(SceneContainer &cContainer, Matrix3x4 &mTransform)
{
	// Identity check
	if (this == &cContainer) {
		mTransform.SetIdentity();
	} else {
		// From this container space to the root space
		mTransform = GetTransform().GetMatrix();
		SceneContainer *pContainer = GetContainer();
		while (pContainer) {
			// Check if the job is already done and this container is just a child of the 'another scene container'
			if (pContainer == &cContainer)
				return true; // Done

			// Transform
			mTransform = pContainer->GetTransform().GetMatrix()*mTransform;
			pContainer = pContainer->GetContainer();
		}

		// From root space to target container space
		pContainer = &cContainer;
		Stack<SceneContainer*> lstStack;
		while (pContainer) {
			lstStack.Push(pContainer);
			pContainer = pContainer->GetContainer();
		}
		while (lstStack.GetNumOfElements()) {
			pContainer = lstStack.Top();
			lstStack.Pop();
			mTransform = pContainer->GetTransform().GetInverseMatrix()*mTransform;
		}
	}

	// Done
	return true;
}

/**
*  @brief
*    Creates the scene hierarchy of this scene container
*/
SceneHierarchy *SceneContainer::CreateHierarchy(const String &sClass)
{
	// Is this hierarchy type already used and is the given parameter valid?
	if ((!m_pHierarchy || m_sHierarchy != sClass) && sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneHierarchy");

		// Get class and check it
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				Object *pObject = pClass->Create();
				if (pObject) {
					// Destroy the hold hierarchy
					if (m_pHierarchy)
						delete m_pHierarchy;

					// Create new hierarchy
					m_pHierarchy = static_cast<SceneHierarchy*>(pObject);
					m_pHierarchy->m_pSceneContainer = this;

					// Initialize the hierarchy
					m_pHierarchy->Init(m_cAABoundingBox.vMin, m_cAABoundingBox.vMax);

					// Add scene nodes to the hierarchy
					for (uint32 i=0; i<GetNumOfElements(); i++)
						m_pHierarchy->AddSceneNode(*GetByIndex(i));

					// Touch recursive
					m_pHierarchy->GetRootNode().Touch(true);

					// Set the new hierarchy class name
					m_sHierarchy = sClass;
				}
			}
		}
	}

	// Return the hierarchy
	return m_pHierarchy;
}

/**
*  @brief
*    Returns the scene hierarchy of this scene container
*/
SceneHierarchy *SceneContainer::GetHierarchyInstance()
{
	// Hierarchy refresh
	if (m_pHierarchy) {
		while (!m_lstHierarchyRefresh.IsEmpty()) {
			SceneNode *pSceneNode = m_lstHierarchyRefresh.Get(0);
			m_lstHierarchyRefresh.RemoveAtIndex(0);
			pSceneNode->m_nInternalFlags &= ~SceneNode::RecalculateHierarchy;
			m_pHierarchy->RefreshSceneNode(*pSceneNode);
		}
	} else {
		// Create the hierarchy right now
		CreateHierarchy(m_sHierarchy);
	}

	// Return the hierarchy
	return m_pHierarchy;
}

/**
*  @brief
*    Creates a scene query
*/
SceneQuery *SceneContainer::CreateQuery(const String &sClass)
{
	// Check parameter
	if (sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneQuery");

		// Get class and check it
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				Object *pObject = pClass->Create();
				if (pObject) {
					SceneQuery *pSceneQuery = static_cast<SceneQuery*>(pObject);
					pSceneQuery->m_pSceneContainer = this;

					// Add to query manager
					if (!m_pQueryManager)
						m_pQueryManager = new SceneQueryManager();
					m_pQueryManager->Add(*pSceneQuery);

					// Return the created scene query
					return pSceneQuery;
				}
			}
		}
	}

	return nullptr;
}

/**
*  @brief
*    Destroys a scene query
*/
bool SceneContainer::DestroyQuery(SceneQuery &cQuery)
{
	// Is there a query manager?
	if (m_pQueryManager) {
		// Check parameter
		if (m_pQueryManager->Unload(cQuery)) {
			// Check query manager
			if (!m_pQueryManager->GetNumOfElements()) {
				delete m_pQueryManager;
				m_pQueryManager = nullptr;
			}

			// Done
			return true;
		}
	}

	return false;
}

/**
*  @brief
*    Adds a node
*/
bool SceneContainer::Add(SceneNode &cNode, const String &sName, bool bInitNode, int nPosition)
{
	// Add to list
	if (nPosition < 0 || nPosition >= static_cast<int>(m_lstElements.GetNumOfElements()))
		m_lstElements.Add(&cNode);
	else
		m_lstElements.AddAtIndex(&cNode, nPosition);
	cNode.m_pManager = this;

	// Set scene container
	if (cNode.IsContainer())
		static_cast<SceneContainer&>(cNode).m_pSceneContext = m_pSceneContext;

	// Emit signal
	cNode.SignalContainer();

	// Setup name
	String sNameT = sName;
	if (!sNameT.GetLength() && cNode.m_sName.GetLength())
		sNameT = cNode.m_sName; // Use it's default name

	// Is there already another scene node with the same name?
	bool bNameOccupied = false;
	if (sNameT.GetLength()) {
		SceneNode *pSceneNode = GetByName(sNameT);
		if (pSceneNode && pSceneNode != &cNode)
			bNameOccupied = true; // Sorry, this name is already in use
	}
	if (!bNameOccupied && sNameT.GetLength()) {
		cNode.m_sName = sNameT;
		m_mapElements.Add(sNameT, &cNode);
	} else { // Find an unused node name
		if (sNameT.GetLength()) {
			for (uint32 i=0; ; i++) {
				const String sNewName = sNameT + static_cast<int>(i);
				if (!GetByName(sNewName)) {
					cNode.m_sName = sNewName;
					m_mapElements.Add(sNewName, &cNode);
					break;
				}
			}
		} else {
			for (uint32 i=0; ; i++) {
				const String sNewName = cNode.GetClass()->GetClassName() + static_cast<int>(i);
				if (!GetByName(sNewName)) {
					cNode.m_sName = sNewName;
					m_mapElements.Add(sNewName, &cNode);
					break;
				}
			}
		}
	}

	// Initialize node
	if (bInitNode)
		cNode.InitSceneNode();

	// Add the scene node to the scene hierarchy
	if (m_pHierarchy)
		m_pHierarchy->AddSceneNode(cNode);

	// Done
	return true;
}

/**
*  @brief
*    Removes a node
*/
bool SceneContainer::Remove(SceneNode &cNode, bool bDeInitNode)
{
	// Check the given node
	if (!m_mapElements.Get(cNode.GetName()))
		return false; // Error!

	// Remove node
	if (bDeInitNode)
		cNode.DeInitSceneNode();
	m_mapElements.Remove(cNode.GetName());
	const bool bResult = m_lstElements.Remove(&cNode);
	cNode.m_pManager = nullptr;

	// Remove the scene node from the refresh list if required
	if (cNode.m_nInternalFlags & SceneNode::RecalculateHierarchy)
		m_lstHierarchyRefresh.Remove(&cNode);

	// Remove the scene node from the scene hierarchy
	if (m_pHierarchy)
		m_pHierarchy->RemoveSceneNode(cNode);

	// Done
	return bResult;
}

//[ Protected virtual SceneNode functions                 ]
void SceneContainer::InitFunction()
{
	// Call base implementation
	SceneNode::InitFunction();

	// Load the container?
	if (m_sFilename.GetLength())
		LoadByFilename(m_sFilename);
}

void SceneContainer::DeInitFunction()
{
	// De-initialize all scene nodes
	for (uint32 i=0; i<GetNumOfElements(); i++)
		GetByIndex(i)->DeInitSceneNode();

	// Cleanup
	Clear();

	// Call base implementation
	SceneNode::DeInitFunction();
}

void SceneContainer::OnActivate(bool bActivate)
{
	// Call the base implementation
	SceneNode::OnActivate(bActivate);

	// Loop through all scene nodes
	for (uint32 i=0; i<GetNumOfElements(); i++)
		GetByIndex(i)->OnActivate(bActivate);
}

//[ Public virtual PLCore::ElementManager functions       ]
bool SceneContainer::DeInit()
{
	// Cleanup
	Clear();

	// Done
	return true;
}

SceneNode *SceneContainer::GetByIndex(uint32 nIndex) const
{
	return m_lstElements[nIndex];
}

SceneNode *SceneContainer::GetByName(const String &sName) const
{
	// Name not empty and is '.' the first character?
	if (sName.GetLength() && sName[static_cast<uint32>(0)] != '.') {
		// Is the name 'This' at the beginning?
		static const String sThis = "This";
		if (sName.Compare(sThis, 0, 4)) {
			// Return this scene node?
			if (sName[static_cast<uint32>(4)] == '\0')
				return const_cast<SceneNode*>(reinterpret_cast<const SceneNode*>(this)); // [HACK] Make this not 'const'

			if (sName[static_cast<uint32>(4)] == '.')
				return GetByName(sName.GetSubstring(5));
		}

		// Is the name 'Root' at the beginning?
		static const String sRoot = "Root";
		if (sName.Compare(sRoot, 0, 4)) {
			// Return root scene node?
			if (sName[static_cast<uint32>(4)] == '\0')
				return m_pSceneContext->GetRoot();

			if (sName[static_cast<uint32>(4)] == '.')
				return m_pSceneContext->GetRoot() ? m_pSceneContext->GetRoot()->GetByName(sName.GetSubstring(5)) : nullptr;
		}

		// 'Parent' at the beginning?
		static const String sParent = "Parent";
		if (sName.Compare(sParent, 0, 6)) {
			// Return parent scene node?
			if (sName[static_cast<uint32>(6)] == '\0')
				return GetContainer();

			if (sName[static_cast<uint32>(6)] == '.') {
				// Is there a parent container?
				return GetContainer() ? GetContainer()->GetByName(sName.GetSubstring(7)) : nullptr;
			}
		}

		// Check for the character '.' within the name
		const int nIndex = sName.IndexOf('.');
		if (nIndex >= 0) {
			// Get the name of the scene container
			String sContainerName;
			sContainerName.Insert(sName, 0, static_cast<uint32>(nIndex));

			// Get the scene node and check whether it is a scene container
			SceneNode *pSceneNode = GetByName(sContainerName);
			if (pSceneNode && (pSceneNode->m_nInternalFlags & ClassContainer)) {
				return static_cast<SceneContainer*>(pSceneNode)->GetByName(sName.GetSubstring(nIndex+1));
			}
		} else {
			// Search for a scene node with this name
			SceneNode *pNode = m_mapElements.Get(sName);
			if (pNode)
				return pNode;
		}
	}

	return nullptr;
}

//[ Public virtual PLCore::Loadable functions             ]
bool SceneContainer::LoadByFilename(const String &sFilename, const String &sParams, const String &sMethod)
{
	// Pause all to avoid timing problems
	const bool bIsPaused = Timing::GetInstance()->IsPaused();
	Timing::GetInstance()->Pause(true);

	// Call base implementation
	const bool bResult = Loadable::LoadByFilename(sFilename, sParams, sMethod);

	// Restore previous pause state
	Timing::GetInstance()->Pause(bIsPaused);

	// Done
	return bResult;
}

bool SceneContainer::LoadByFile(File &cFile, const String &sParams, const String &sMethod)
{
	// Pause all to avoid timing problems
	const bool bIsPaused = Timing::GetInstance()->IsPaused();
	Timing::GetInstance()->Pause(true);

	// Call base implementation
	const bool bResult = Loadable::LoadByFile(cFile, sParams, sMethod);

	// Restore previous pause state
	Timing::GetInstance()->Pause(bIsPaused);

	// Done
	return bResult;
}

{
	// Cleanup the container
	if (Clear()) {
		// Call base implementation
		return Loadable::Unload();
	} else {
		return false;
	}
}

String SceneContainer::GetLoadableTypeName() const
{
	static const String sString = "Scene";
	return sString;
}

//[ Protected virtual PLCore::Loadable functions          ]
bool SceneContainer::CallLoadable(File &cFile, Loader &cLoader, const String &sMethod, const String &sParams)
{
	// Get the loader implementation
	LoaderImpl *pLoaderImpl = cLoader.GetImpl();
	if (pLoaderImpl) {
		// Load
		if (sParams.GetLength()) {
			pLoaderImpl->CallMethod(sMethod, "Param0=\"" + Type<SceneContainer&>::ConvertToString(*this) + "\" Param1=\"" + Type<File&>::ConvertToString(cFile) + "\" " + sParams);
			return true;
		} else {
			Params<bool, SceneContainer&, File&> cParams(*this, cFile);
			pLoaderImpl->CallMethod(sMethod, cParams);
			return cParams.Return;
		}
	}

	return false;
}

//[ Private virtual PLCore::ElementManager functions      ]
SceneNode *SceneContainer::CreateElement(const String &sName)
{
	return Create(sName, "");
}

} // PLScene


{
	// Pause all to avoid timing problems
	const bool bIsPaused = Timing::GetInstance()->IsPaused();
	Timing::GetInstance()->Pause(true);

	// Call base implementation
	const bool bResult = Loadable::LoadByFile(cFile, sParams, sMethod);

	// Restore previous pause state
	Timing::GetInstance()->Pause(bIsPaused);

	// Done
	return bResult;
}

/*********************************************************\
 *  File: SceneContainer.cpp                             *
 *
 *
 *  This file is part of PixelLight.
 *
\*********************************************************/

#include <PLCore/Log/Log.h>
#include <PLCore/Base/Class.h>
#include <PLCore/Tools/Timing.h>
#include <PLCore/Tools/Loader.h>
#include <PLCore/Container/Stack.h>
#include "PLScene/Scene/SceneContext.h"
#include "PLScene/Scene/SceneHierarchy.h"
#include "PLScene/Scene/SceneHierarchyNode.h"
#include "PLScene/Scene/SceneQueryManager.h"
#include "PLScene/Scene/SceneLoader/SceneLoader.h"
#include "PLScene/Scene/SceneContainer.h"

using namespace PLCore;
using namespace PLMath;
namespace PLScene {

pl_implement_class(SceneContainer)

//[ Public RTTI get/set functions                         ]
String SceneContainer::GetHierarchy() const
{
	return m_sHierarchy;
}

void SceneContainer::SetHierarchy(const String &sValue)
{
	// Destroy the current hierarchy, but do NOT create the new one at once - this
	// is done if the new hierarchy is requested the first time  :)
	if (m_pHierarchy && m_pHierarchy->GetClass()->GetClassName() != sValue) {
		delete m_pHierarchy;
		m_pHierarchy = nullptr;
	}

	// Assign the new class hierarchy name
	m_sHierarchy = sValue;
}

void SceneContainer::SetFilename(const String &sValue)
{
	if (m_sFilename != sValue) {
		m_sFilename = sValue;

		// Load the container
		if (IsInitialized())
			LoadByFilename(m_sFilename);
	}
}

/**
*  @brief
*    Default constructor
*/
SceneContainer::SceneContainer() :
	Hierarchy(this),
	Flags(this),
	AABBMin(this),
	AABBMax(this),
	Filename(this),
	m_sHierarchy("PLScene::SHList"),
	m_pSceneContext(nullptr),
	m_pHierarchy(nullptr),
	m_pQueryManager(nullptr)
{
	// Overwritten SceneNode variables
	m_cAABoundingBox.vMin.SetXYZ(-10000.0f, -10000.0f, -10000.0f);
	m_cAABoundingBox.vMax.SetXYZ( 10000.0f,  10000.0f,  10000.0f);

	// Set the internal flag
	m_nInternalFlags |= ClassContainer;

	// Set draw function flags
	SetDrawFunctionFlags(UseDrawPre | UseDrawSolid | UseDrawTransparent | UseDrawDebug | UseDrawPost);
}

/**
*  @brief
*    Destructor
*/
SceneContainer::~SceneContainer()
{
	DeInit();
	if (m_pHierarchy)
		delete m_pHierarchy;
	if (m_pQueryManager)
		delete m_pQueryManager;
}

/**
*  @brief
*    Creates a new scene node at a certain index inside the scene node list
*/
SceneNode *SceneContainer::CreateAtIndex(const String &sClass, const String &sName, const String &sParameters, int nPosition)
{
	// Check parameter
	if (sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneNode");

		// Get scene node plugin
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				PL_LOG(Debug, "Create scene node '" + sName + "' of type '" + sClass + '\'')
				SceneNode *pNode = static_cast<SceneNode*>(pClass->Create());
				if (pNode) {

					// [TODO] Check/refactor the initialization/de-initialization process
					pNode->m_pManager = this;

					if (sParameters.GetLength())
						pNode->SetValues(sParameters);
					Add(*pNode, sName, true, nPosition);
				}

				// Return the created scene node
				return pNode;
			} else { // Plugin not found
				PL_LOG(Error, "Unknown scene node type: " + sClass)
			}
		}
	}

	// Error
	return nullptr;
}

/**
*  @brief
*    Calculates and sets the axis align bounding box in 'scene node space'
*/
void SceneContainer::CalculateAABoundingBox()
{
	AABoundingBox cAABoundingBox;

	// If there are any scene nodes...
	if (GetNumOfElements()) {
		// Set first bounding box
		cAABoundingBox = GetByIndex(0)->GetContainerAABoundingBox();

		// Combine all bounding boxes
		for (uint32 i=1; i<GetNumOfElements(); i++)
			cAABoundingBox.CombineAABoxes(GetByIndex(i)->GetContainerAABoundingBox());
	}

	// Set the final bounding box
	SetAABoundingBox(cAABoundingBox);
}

/**
*  @brief
*    Returns a transform matrix to bring a transformation from 'this scene container' into 'another scene container'
*/
bool SceneContainer::GetTransformMatrixTo(SceneContainer &cContainer, Matrix3x4 &mTransform)
{
	// Identity check
	if (this == &cContainer) {
		mTransform.SetIdentity();
	} else {
		// From this container space to the root space
		mTransform = GetTransform().GetMatrix();
		SceneContainer *pContainer = GetContainer();
		while (pContainer) {
			// Check if the job is already done and this container is just a child of the 'another scene container'
			if (pContainer == &cContainer)
				return true; // Done

			// Transform
			mTransform = pContainer->GetTransform().GetMatrix()*mTransform;
			pContainer = pContainer->GetContainer();
		}

		// From root space to target container space
		pContainer = &cContainer;
		Stack<SceneContainer*> lstStack;
		while (pContainer) {
			lstStack.Push(pContainer);
			pContainer = pContainer->GetContainer();
		}
		while (lstStack.GetNumOfElements()) {
			pContainer = lstStack.Top();
			lstStack.Pop();
			mTransform = pContainer->GetTransform().GetInverseMatrix()*mTransform;
		}
	}

	// Done
	return true;
}

/**
*  @brief
*    Creates the scene hierarchy of this scene container
*/
SceneHierarchy *SceneContainer::CreateHierarchy(const String &sClass)
{
	// Is this hierarchy type already used and is the given parameter valid?
	if ((!m_pHierarchy || m_sHierarchy != sClass) && sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneHierarchy");

		// Get class and check it
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				Object *pObject = pClass->Create();
				if (pObject) {
					// Destroy the hold hierarchy
					if (m_pHierarchy)
						delete m_pHierarchy;

					// Create new hierarchy
					m_pHierarchy = static_cast<SceneHierarchy*>(pObject);
					m_pHierarchy->m_pSceneContainer = this;

					// Initialize the hierarchy
					m_pHierarchy->Init(m_cAABoundingBox.vMin, m_cAABoundingBox.vMax);

					// Add scene nodes to the hierarchy
					for (uint32 i=0; i<GetNumOfElements(); i++)
						m_pHierarchy->AddSceneNode(*GetByIndex(i));

					// Touch recursive
					m_pHierarchy->GetRootNode().Touch(true);

					// Set the new hierarchy class name
					m_sHierarchy = sClass;
				}
			}
		}
	}

	// Return the hierarchy
	return m_pHierarchy;
}

/**
*  @brief
*    Returns the scene hierarchy of this scene container
*/
SceneHierarchy *SceneContainer::GetHierarchyInstance()
{
	// Hierarchy refresh
	if (m_pHierarchy) {
		while (!m_lstHierarchyRefresh.IsEmpty()) {
			SceneNode *pSceneNode = m_lstHierarchyRefresh.Get(0);
			m_lstHierarchyRefresh.RemoveAtIndex(0);
			pSceneNode->m_nInternalFlags &= ~SceneNode::RecalculateHierarchy;
			m_pHierarchy->RefreshSceneNode(*pSceneNode);
		}
	} else {
		// Create the hierarchy right now
		CreateHierarchy(m_sHierarchy);
	}

	// Return the hierarchy
	return m_pHierarchy;
}

/**
*  @brief
*    Creates a scene query
*/
SceneQuery *SceneContainer::CreateQuery(const String &sClass)
{
	// Check parameter
	if (sClass.GetLength()) {
		// To keep things as fast as possible we store a pointer to the base class
		// (no hash map operation + quite efficient internal string comparison)
		static const Class *pBaseClass = ClassManager::GetInstance()->GetClass("PLScene::SceneQuery");

		// Get class and check it
		if (pBaseClass) {
			const Class *pClass = ClassManager::GetInstance()->GetClass(sClass);
			if (pClass && pClass->IsDerivedFrom(*pBaseClass)) {
				Object *pObject = pClass->Create();
				if (pObject) {
					SceneQuery *pSceneQuery = static_cast<SceneQuery*>(pObject);
					pSceneQuery->m_pSceneContainer = this;

					// Add to query manager
					if (!m_pQueryManager)
						m_pQueryManager = new SceneQueryManager();
					m_pQueryManager->Add(*pSceneQuery);

					// Return the created scene query
					return pSceneQuery;
				}
			}
		}
	}

	return nullptr;
}

/**
*  @brief
*    Destroys a scene query
*/
bool SceneContainer::DestroyQuery(SceneQuery &cQuery)
{
	// Is there a query manager?
	if (m_pQueryManager) {
		// Check parameter
		if (m_pQueryManager->Unload(cQuery)) {
			// Check query manager
			if (!m_pQueryManager->GetNumOfElements()) {
				delete m_pQueryManager;
				m_pQueryManager = nullptr;
			}

			// Done
			return true;
		}
	}

	return false;
}

/**
*  @brief
*    Adds a node
*/
bool SceneContainer::Add(SceneNode &cNode, const String &sName, bool bInitNode, int nPosition)
{
	// Add to list
	if (nPosition < 0 || nPosition >= static_cast<int>(m_lstElements.GetNumOfElements()))
		m_lstElements.Add(&cNode);
	else
		m_lstElements.AddAtIndex(&cNode, nPosition);
	cNode.m_pManager = this;

	// Set scene container
	if (cNode.IsContainer())
		static_cast<SceneContainer&>(cNode).m_pSceneContext = m_pSceneContext;

	// Emit signal
	cNode.SignalContainer();

	// Setup name
	String sNameT = sName;
	if (!sNameT.GetLength() && cNode.m_sName.GetLength())
		sNameT = cNode.m_sName; // Use it's default name

	// Is there already another scene node with the same name?
	bool bNameOccupied = false;
	if (sNameT.GetLength()) {
		SceneNode *pSceneNode = GetByName(sNameT);
		if (pSceneNode && pSceneNode != &cNode)
			bNameOccupied = true; // Sorry, this name is already in use
	}
	if (!bNameOccupied && sNameT.GetLength()) {
		cNode.m_sName = sNameT;
		m_mapElements.Add(sNameT, &cNode);
	} else { // Find an unused node name
		if (sNameT.GetLength()) {
			for (uint32 i=0; ; i++) {
				const String sNewName = sNameT + static_cast<int>(i);
				if (!GetByName(sNewName)) {
					cNode.m_sName = sNewName;
					m_mapElements.Add(sNewName, &cNode);
					break;
				}
			}
		} else {
			for (uint32 i=0; ; i++) {
				const String sNewName = cNode.GetClass()->GetClassName() + static_cast<int>(i);
				if (!GetByName(sNewName)) {
					cNode.m_sName = sNewName;
					m_mapElements.Add(sNewName, &cNode);
					break;
				}
			}
		}
	}

	// Initialize node
	if (bInitNode)
		cNode.InitSceneNode();

	// Add the scene node to the scene hierarchy
	if (m_pHierarchy)
		m_pHierarchy->AddSceneNode(cNode);

	// Done
	return true;
}

/**
*  @brief
*    Removes a node
*/
bool SceneContainer::Remove(SceneNode &cNode, bool bDeInitNode)
{
	// Check the given node
	if (!m_mapElements.Get(cNode.GetName()))
		return false; // Error!

	// Remove node
	if (bDeInitNode)
		cNode.DeInitSceneNode();
	m_mapElements.Remove(cNode.GetName());
	const bool bResult = m_lstElements.Remove(&cNode);
	cNode.m_pManager = nullptr;

	// Remove the scene node from the refresh list if required
	if (cNode.m_nInternalFlags & SceneNode::RecalculateHierarchy)
		m_lstHierarchyRefresh.Remove(&cNode);

	// Remove the scene node from the scene hierarchy
	if (m_pHierarchy)
		m_pHierarchy->RemoveSceneNode(cNode);

	// Done
	return bResult;
}

//[ Protected virtual SceneNode functions                 ]
void SceneContainer::InitFunction()
{
	// Call base implementation
	SceneNode::InitFunction();

	// Load the container?
	if (m_sFilename.GetLength())
		LoadByFilename(m_sFilename);
}

void SceneContainer::DeInitFunction()
{
	// De-initialize all scene nodes
	for (uint32 i=0; i<GetNumOfElements(); i++)
		GetByIndex(i)->DeInitSceneNode();

	// Cleanup
	Clear();

	// Call base implementation
	SceneNode::DeInitFunction();
}

void SceneContainer::OnActivate(bool bActivate)
{
	// Call the base implementation
	SceneNode::OnActivate(bActivate);

	// Loop through all scene nodes
	for (uint32 i=0; i<GetNumOfElements(); i++)
		GetByIndex(i)->OnActivate(bActivate);
}

//[ Public virtual PLCore::ElementManager functions       ]
bool SceneContainer::DeInit()
{
	// Cleanup
	Clear();

	// Done
	return true;
}

SceneNode *SceneContainer::GetByIndex(uint32 nIndex) const
{
	return m_lstElements[nIndex];
}

SceneNode *SceneContainer::GetByName(const String &sName) const
{
	// Name not empty and is '.' the first character?
	if (sName.GetLength() && sName[static_cast<uint32>(0)] != '.') {
		// Is the name 'This' at the beginning?
		static const String sThis = "This";
		if (sName.Compare(sThis, 0, 4)) {
			// Return this scene node?
			if (sName[static_cast<uint32>(4)] == '\0')
				return const_cast<SceneNode*>(reinterpret_cast<const SceneNode*>(this)); // [HACK] Make this not 'const'

			if (sName[static_cast<uint32>(4)] == '.')
				return GetByName(sName.GetSubstring(5));
		}

		// Is the name 'Root' at the beginning?
		static const String sRoot = "Root";
		if (sName.Compare(sRoot, 0, 4)) {
			// Return root scene node?
			if (sName[static_cast<uint32>(4)] == '\0')
				return m_pSceneContext->GetRoot();

			if (sName[static_cast<uint32>(4)] == '.')
				return m_pSceneContext->GetRoot() ? m_pSceneContext->GetRoot()->GetByName(sName.GetSubstring(5)) : nullptr;
		}

		// 'Parent' at the beginning?
		static const String sParent = "Parent";
		if (sName.Compare(sParent, 0, 6)) {
			// Return parent scene node?
			if (sName[static_cast<uint32>(6)] == '\0')
				return GetContainer();

			if (sName[static_cast<uint32>(6)] == '.') {
				// Is there a parent container?
				return GetContainer() ? GetContainer()->GetByName(sName.GetSubstring(7)) : nullptr;
			}
		}

		// Check for the character '.' within the name
		const int nIndex = sName.IndexOf('.');
		if (nIndex >= 0) {
			// Get the name of the scene container
			String sContainerName;
			sContainerName.Insert(sName, 0, static_cast<uint32>(nIndex));

			// Get the scene node and check whether it is a scene container
			SceneNode *pSceneNode = GetByName(sContainerName);
			if (pSceneNode && (pSceneNode->m_nInternalFlags & ClassContainer)) {
				return static_cast<SceneContainer*>(pSceneNode)->GetByName(sName.GetSubstring(nIndex+1));
			}
		} else {
			// Search for a scene node with this name
			SceneNode *pNode = m_mapElements.Get(sName);
			if (pNode)
				return pNode;
		}
	}

	return nullptr;
}

//[ Public virtual PLCore::Loadable functions             ]
bool SceneContainer::LoadByFilename(const String &sFilename, const String &sParams, const String &sMethod)
{
	// Pause all to avoid timing problems
	const bool bIsPaused = Timing::GetInstance()->IsPaused();
	Timing::GetInstance()->Pause(true);

	// Call base implementation
	const bool bResult = Loadable::LoadByFilename(sFilename, sParams, sMethod);

	// Restore previous pause state
	Timing::GetInstance()->Pause(bIsPaused);

	// Done
	return bResult;
}

bool SceneContainer::LoadByFile(File &cFile, const String &sParams, const String &sMethod)

bool SceneContainer::Unload()
{
	// Cleanup the container
	if (Clear()) {
		// Call base implementation
		return Loadable::Unload();
	} else {
		return false;
	}
}

String SceneContainer::GetLoadableTypeName() const
{
	static const String sString = "Scene";
	return sString;
}

//[ Protected virtual PLCore::Loadable functions          ]
bool SceneContainer::CallLoadable(File &cFile, Loader &cLoader, const String &sMethod, const String &sParams)
{
	// Get the loader implementation
	LoaderImpl *pLoaderImpl = cLoader.GetImpl();
	if (pLoaderImpl) {
		// Load
		if (sParams.GetLength()) {
			pLoaderImpl->CallMethod(sMethod, "Param0=\"" + Type<SceneContainer&>::ConvertToString(*this) + "\" Param1=\"" + Type<File&>::ConvertToString(cFile) + "\" " + sParams);
			return true;
		} else {
			Params<bool, SceneContainer&, File&> cParams(*this, cFile);
			pLoaderImpl->CallMethod(sMethod, cParams);
			return cParams.Return;
		}
	}

	return false;
}

//[ Private virtual PLCore::ElementManager functions      ]
SceneNode *SceneContainer::CreateElement(const String &sName)
{
	return Create(sName, "");
}

} // PLScene