/*!
  \file
  \brief 背景曲の再生

  \author Satofumi KAMIMURA

  $Id: BackMusic.cpp 1965 2011-08-13 12:02:33Z satofumi $
*/

#include "BackMusic.h"
#include "Audio.h"
#include <SDL_mixer.h>
#include <map>
#include <string>

using namespace qrk;
using namespace std;


struct BackMusic::pImpl
{
    typedef map<string, Mix_Music*> Musics;

    static Mix_Music* next_music_;
    static int next_fade_in_msec_;
    static int next_play_times_;

    Audio audio_;
    Musics musics_;
    bool is_mute_;
    int mix_volume_;


    pImpl(void) : is_mute_(false), mix_volume_(0)
    {
        Mix_HookMusicFinished(music_finished_callback);
    }


    ~pImpl(void)
    {
        for (Musics::iterator it = musics_.begin(); it != musics_.end(); ++it) {
            Mix_FreeMusic(it->second);
        }
    }


    static pImpl* singleton_object(void)
    {
        static pImpl object_;
        return &object_;
    }


    static void set_next_music(Mix_Music* music,
                               int fade_in_msec, int play_times)
    {
        next_music_ = music;
        next_fade_in_msec_ = fade_in_msec;
        next_play_times_ = play_times;
    }


    static void music_finished_callback(void)
    {
        // 次の音楽が登録されているならば、再生を開始する
        if (next_music_) {
            play_music(next_music_, next_fade_in_msec_, next_play_times_);
            next_music_ = NULL;
        }
    }


    static bool play_music(Mix_Music* music, int fade_in_msec, int play_times)
    {
        int ret = Mix_FadeInMusic(music, play_times, fade_in_msec);
        if (ret == -1) {
            return false;
        } else {
            return true;
        }
    }


    Mix_Music* find_music(const char* file_path)
    {
        // Load に失敗した場合には musics_ に NULL を格納し、
        // 成功した場合にはリソースのポインタを格納する
        Musics::iterator it = musics_.find(file_path);
        if (it != musics_.end()) {
            return it->second;
        }

        Mix_Music* music = Mix_LoadMUS(file_path);
        musics_[file_path] = music;
        return music;
    }


    void free_music(const char* file_path)
    {
        musics_.erase(file_path);
    }
};

Mix_Music* BackMusic::pImpl::next_music_ = NULL;
int BackMusic::pImpl::next_fade_in_msec_ = 0;
int BackMusic::pImpl::next_play_times_ = 0;


BackMusic::BackMusic(void) : pimpl(pImpl::singleton_object())
{
}


BackMusic::~BackMusic(void)
{
}


void BackMusic::setMute(bool is_mute)
{
    if (is_mute) {
        int saved_mix_volume = pimpl->mix_volume_;
        setVolume(0);
        pimpl->mix_volume_ = saved_mix_volume;
    } else {
        // ミュートする前の音量で再生を再開する
        setVolume(pimpl->mix_volume_);
    }
    pimpl->is_mute_ = is_mute;
}


bool BackMusic::isMute(void) const
{
    return pimpl->is_mute_;
}


void BackMusic::free_music_resource(const char* file_path)
{
    if (!pimpl->audio_.isInitialized()) {
        return;
    }

    Mix_Music* music = pimpl->find_music(file_path);
    if (music) {
        Mix_FreeMusic(music);
        pimpl->free_music(file_path);
    }
}


void BackMusic::setVolume(size_t percent)
{
    if (!pimpl->audio_.isInitialized()) {
        return;
    }

    pimpl->mix_volume_ = static_cast<int>(MIX_MAX_VOLUME * percent / 100.0);
    if (!pimpl->is_mute_) {
        Mix_VolumeMusic(pimpl->mix_volume_);
    }
}


bool BackMusic::play(const char* file_path, int fade_in_msec,
                     int play_times, int fade_out_msec)
{
    if (!pimpl->audio_.isInitialized()) {
        return false;
    }

    Mix_Music* music = pimpl->find_music(file_path);

    if (isPlaying()) {
        // 再生中の曲の fade out を待ってから次の曲の再生を開始させる
        pImpl::set_next_music(music, fade_in_msec, play_times);
        stop(fade_out_msec);
        return (music) ? true : false;

    } else {
        return pImpl::play_music(music, fade_in_msec, play_times);
    }
}


void BackMusic::stop(int fade_out_msec)
{
    if (!pimpl->audio_.isInitialized()) {
        return;
    }
    Mix_FadeOutMusic(fade_out_msec);
}


bool BackMusic::isPlaying(void) const
{
    if (!pimpl->audio_.isInitialized()) {
        return false;
    }

    return (Mix_PlayingMusic() == 0) ? false : true;
}
