AudioCache* AudioEngineImpl::preload(const std::string& filePath, std::function<void(bool)> callback)
{
    AudioCache* audioCache = nullptr;
    do 
    {
        auto it = _audioCaches.find(filePath);
        if (it == _audioCaches.end()) {
            FileFormat fileFormat = FileFormat::UNKNOWN;

            std::string fileExtension = FileUtils::getInstance()->getFileExtension(filePath);

            if (fileExtension == ".wav")
            {
                fileFormat = FileFormat::WAV;
            }
            else if (fileExtension == ".ogg")
            {
                fileFormat = FileFormat::OGG;
            }
            else if (fileExtension == ".mp3")
            {
                fileFormat = FileFormat::MP3;
            }
            else
            {
                log("Unsupported media type file: %s\n", filePath.c_str());
                break;
            }

            audioCache = &_audioCaches[filePath];
            audioCache->_fileFormat = fileFormat;

            std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
            audioCache->_fileFullPath = fullPath;
            AudioEngine::addTask(std::bind(&AudioCache::readDataTask, audioCache));
        }
        else 
        {
            audioCache = &it->second;
        }
    } while (false);

    if (callback)
    {
        if (audioCache)
        {
            audioCache->addLoadCallback(callback);
        } 
        else
        {
            callback(false);
        }
    }

    return audioCache;
}
int AudioEngineImpl::play2d(const std::string &filePath, bool loop, float volume)
{
    AudioCache* audioCache = nullptr;
    auto it = _audioCaches.find(filePath);
    if (it == _audioCaches.end()) {
        audioCache = &_audioCaches[filePath];

        auto ext = filePath.substr(filePath.rfind('.'));
        transform(ext.begin(), ext.end(), ext.begin(), tolower);

        bool eraseCache = true;

        if (ext.compare(".wav") == 0){
            audioCache->_fileFormat = FileFormat::WAV;
            eraseCache = false;
        }
        else if (ext.compare(".ogg") == 0){
            audioCache->_fileFormat = FileFormat::OGG;
            eraseCache = false;
        }
        else if (ext.compare(".mp3") == 0){
            audioCache->_fileFormat = FileFormat::MP3;
            eraseCache = false;
        }
        else{
            log("unsupported media type:%s\n", ext.c_str());
        }

        if (eraseCache){
            _audioCaches.erase(filePath);
            return AudioEngine::INVALID_AUDIO_ID;
        }

        std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
        audioCache->_fileFullPath = fullPath;
        _threadPool->addTask(std::bind(&AudioCache::readDataTask, audioCache));
    }
    else {
        audioCache = &it->second;
    }

    auto player = &_audioPlayers[_currentAudioID];
    player->_loop = loop;
    player->_volume = volume;
    audioCache->addCallback(std::bind(&AudioEngineImpl::_play2d, this, audioCache, _currentAudioID));

    if (_lazyInitLoop) {
        _lazyInitLoop = false;

        auto scheduler = cocos2d::Director::getInstance()->getScheduler();
        scheduler->schedule(schedule_selector(AudioEngineImpl::update), this, 0.05f, false);
    }

    return _currentAudioID++;
}
int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume)
{
    bool availableSourceExist = false;
    ALuint alSource;
    for (int i = 0; i < MAX_AUDIOINSTANCES; ++i) {
        alSource = _alSources[i];
        if ( !_alSourceUsed[alSource]) {
            availableSourceExist = true;
            break;
        }
    }
    if(!availableSourceExist){
        return AudioEngine::INVALID_AUDIO_ID;
    }
    
    AudioCache* audioCache = preload(filePath, nullptr);
    if (audioCache == nullptr)
    {
        return AudioEngine::INVALID_AUDIO_ID;
    }
    
    auto player = &_audioPlayers[_currentAudioID];
    player->_alSource = alSource;
    player->_loop = loop;
    player->_volume = volume;
    audioCache->addPlayCallback(std::bind(&AudioEngineImpl::_play2d, this, audioCache, _currentAudioID));
    
    _alSourceUsed[alSource] = true;
    
    if (_lazyInitLoop) {
        _lazyInitLoop = false;
        
        auto scheduler = cocos2d::Director::getInstance()->getScheduler();
        scheduler->schedule(schedule_selector(AudioEngineImpl::update), this, 0.05f, false);
    }
    
    return _currentAudioID++;
}
int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume)
{
    bool availableSourceExist = false;
    ALuint alSource;
    for (int i = 0; i < MAX_AUDIOINSTANCES; ++i) {
        alSource = _alSources[i];
        if ( !_alSourceUsed[alSource]) {
            availableSourceExist = true;
            break;
        }
    }
    if(!availableSourceExist){
        return AudioEngine::INVALID_AUDIO_ID;
    }
    
    AudioCache* audioCache = nullptr;
    auto it = _audioCaches.find(filePath);
    if (it == _audioCaches.end()) {
        audioCache = &_audioCaches[filePath];
        
        auto ext = filePath.substr(filePath.rfind('.'));
        transform(ext.begin(), ext.end(), ext.begin(), tolower);

        bool eraseCache = true;
        if (ext.compare(".ogg") == 0){
            audioCache->_fileFormat = AudioCache::FileFormat::OGG;
            eraseCache = false;
        }
        else if (ext.compare(".mp3") == 0){
            audioCache->_fileFormat = AudioCache::FileFormat::MP3;

            if (MPG123_LAZYINIT){
                auto error = mpg123_init();
                if(error == MPG123_OK){
                    MPG123_LAZYINIT = false;
                    eraseCache = false;
                }
                else{
                    log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
                }
            }
            else{
                eraseCache = false;
            }
        }
        else{
            log("unsupported media type:%s\n",ext.c_str());
        }
        
        if (eraseCache){
            _audioCaches.erase(filePath);
            return AudioEngine::INVALID_AUDIO_ID;
        }

        audioCache->_fileFullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
        _threadPool->addTask(std::bind(&AudioCache::readDataTask, audioCache));
    }
    else {
        audioCache = &it->second;
    }
    
    auto player = &_audioPlayers[_currentAudioID];
    player->_alSource = alSource;
    player->_loop = loop;
    player->_volume = volume;
    audioCache->addCallbacks(std::bind(&AudioEngineImpl::_play2d,this,audioCache,_currentAudioID));
    
    _alSourceUsed[alSource] = true;
    
    if (_lazyInitLoop) {
        _lazyInitLoop = false;
        
        auto scheduler = cocos2d::Director::getInstance()->getScheduler();
        scheduler->schedule(schedule_selector(AudioEngineImpl::update), this, 0.05f, false);
    }
    
    return _currentAudioID++;
}
AudioCache* AudioEngineImpl::preload(const std::string& filePath, std::function<void(bool)> callback)
{
    AudioCache* audioCache = nullptr;

    do 
    {
        auto it = _audioCaches.find(filePath);
        if (it != _audioCaches.end())
        {
            audioCache = &it->second;
            if (callback && audioCache->_alBufferReady)
            {
                callback(true);
            }
            break;
        }

        AudioCache::FileFormat fileFormat = AudioCache::FileFormat::UNKNOWN;

        std::string fileExtension = FileUtils::getInstance()->getFileExtension(filePath);
        if (fileExtension == ".ogg")
        {
            fileFormat = AudioCache::FileFormat::OGG;
        }
        else if (fileExtension == ".mp3")
        {
            fileFormat = AudioCache::FileFormat::MP3;

            if (MPG123_LAZYINIT)
            {
                auto error = mpg123_init();
                if (error == MPG123_OK)
                {
                    MPG123_LAZYINIT = false;
                }
                else
                {
                    log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
                    break;
                }
            }
        }
        else
        {
            log("Unsupported media type file: %s\n", filePath.c_str());
            break;
        }

        audioCache = &_audioCaches[filePath];
        audioCache->_fileFormat = fileFormat;

        audioCache->_fileFullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
        AudioEngine::addTask(std::bind(&AudioCache::readDataTask, audioCache));
    } while (false);

    if (callback)
    {
        if (audioCache)
        {
            audioCache->addLoadCallback(callback);
        } 
        else
        {
            callback(false);
        }
    }

    return audioCache;
}