/*Copyright (C) 2010-2020 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "MusicPlayer.hpp"

namespace MusicPlayer
{
    namespace
    {
        std::map<unsigned int, MusicData> musics;
        std::vector<pthread_t> threads;

        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
        unsigned int lastID = 0;

        void *launchThread(void *data)
        {
            unsigned int threadID = *(static_cast<int*>(data));
            delete static_cast<int*>(data);
            
            pthread_mutex_lock(&mutex);
            
            std::cout << "[Music] Thread " << threadID;
            std::cout << " launched for " << musics[threadID].path << " playback" << std::endl;
            
            Music music(musics[threadID].path);
            music.setLooping(musics[threadID].loop);
            music.setVolume(0);
            
            pthread_mutex_unlock(&mutex);
            
            music.play();
            
            while (music.isPlaying())
            {
                music.update();
                
                pthread_mutex_lock(&mutex);
                
                //Fade init
                if (!musics[threadID].close_fade && music.getVolume() < musics[threadID].volume)
                {
                    float volume = music.getVolume() + musics[threadID].volume / 100;
                    if (volume > musics[threadID].volume) volume = musics[threadID].volume;
                    
                    music.setVolume(volume);
                }
                
                //Force close
                if (musics[threadID].close_force)
                {
                    std::cout << "Exiting Thread " << threadID << std::endl;
                    pthread_mutex_unlock(&mutex);
                    
                    break;
                }
                
                //Fade close
                if (musics[threadID].close_fade)
                {
                    float volume = music.getVolume() - musics[threadID].volume / 100;
                    if (volume > 0)
                        music.setVolume(volume);
                    else
                    {
                        std::cout << "Exiting Thread " << threadID << " (fade out)" << std::endl;
                        pthread_mutex_unlock(&mutex);
                        
                        break;
                    }
                }
                
                pthread_mutex_unlock(&mutex);
                
                Timer::sleep(10);
            }
            
            pthread_mutex_lock(&mutex);
            
            musics.erase(threadID);
            
            pthread_mutex_unlock(&mutex);
            
            return NULL;
        }
    }
    
    void playMusic(const std::string &path, float volume, bool loop)
    {
        //Register data
        MusicData data(path, volume, loop);
        pthread_t thread;
        
        pthread_mutex_lock(&mutex);
        
        musics[++lastID] = data;
        int *pt_id = new int(lastID);
        
        pthread_mutex_unlock(&mutex);
        
        //Create & register thread
        pthread_create(&thread, NULL, launchThread, pt_id);
        threads.push_back(thread);
    }

    void stopMusic(const std::string &path)
    {
        pthread_mutex_lock(&mutex);
        
        std::map<unsigned int, MusicData>::iterator it;
        for (it = musics.begin(); it != musics.end(); it++)
        {
            if (it->second.path == path)
                it->second.close_fade = true; //Flag to stop the music (fade out)
        }
        
        pthread_mutex_unlock(&mutex);
    }

    bool isPlaying(const std::string &path)
    {
        pthread_mutex_lock(&mutex);
        
        std::map<unsigned int, MusicData>::iterator it;
        for (it = musics.begin(); it != musics.end(); it++)
        {
            if (it->second.path == path && !it->second.close_fade)
            {
                pthread_mutex_unlock(&mutex);
                return true;
            }
        }
        
        pthread_mutex_unlock(&mutex);
        return false;
    }
    
    void destroy()
    {
        //Stop remaining threads
        pthread_mutex_lock(&mutex);
        
        std::map<unsigned int, MusicData>::iterator it;
        for (it = musics.begin(); it != musics.end(); it++)
            it->second.close_force = true; //Flag to stop the music (stop immediately)
        
        pthread_mutex_unlock(&mutex);
        
        //Wait for remaining threads to exit
        for (unsigned int i = 0; i < threads.size(); i++)
            pthread_join(threads[i], NULL);
        
        //Clear musics & threads
        threads.clear();
        musics.clear();
        
        //Destroy the allocated mutex
        pthread_mutex_destroy(&mutex);
    }
}
