// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#include <vector>
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;

#include <boost/bind.hpp>

#include "world.h"
#include "soundresourcemanager.h"
#include "../thread/thread.h"
#include "../console/console.h"
using namespace Console;

void soundUpdateFunc(SoundResourceManager *soundResourceManager) {

    while(!soundResourceManager->soundUpdateFuncQuit) {

        soundResourceManager->update();
        usleep(10000);
    }

    out("system") << "Sound thread shutting down." << endl;

}

SoundResourceManager::SoundResourceManager(World *world) {
	
	this->world = world;

    soundUpdateFuncQuit = false;
     
    soundManagerMutex = new Threads::Mutex();
    soundQueueMutex = new Threads::Mutex();
    soundUpdateThread = new Threads::Thread(boost::bind(soundUpdateFunc, this));
}

SoundResourceManager::~SoundResourceManager(void) {

    soundUpdateFuncQuit = true;
    soundUpdateThread->join();

	vector<string> namesToPurge;
	
	for(map<string, unsigned int>::iterator i = soundsByName.begin(); i != soundsByName.end(); i++) {
		namesToPurge.push_back(i->first);
	}
	
	for(unsigned int i = 0; i < namesToPurge.size(); i++) {
		
		unsigned int num = soundsByName[namesToPurge[i]];
		string name = namesToPurge[i];
		
		soundAges.erase(num);
		namesByNumber.erase(num);
		soundsByName.erase(name);
		
		soundManager.freeSound(num);
	}

    delete soundUpdateThread;
    delete soundManagerMutex;
    delete soundQueueMutex;
}

unsigned int SoundResourceManager::getSound(const std::string &fileName) {
	
	unsigned int soundNum = soundsByName[fileName];
	
	if(soundNum) {
		
		soundAges[soundNum] = 0;
		return soundNum;
	}
	
	soundNum = soundManager.loadOgg(world->getRealFile(fileName));
	
	soundsByName[fileName] = soundNum;
	namesByNumber[soundNum] = fileName;
	soundAges[soundNum] = 0;
	
	return soundNum;
}

void SoundResourceManager::cleanOldSounds(unsigned int maxAge) {
	
	vector<string> namesToPurge;
	
	for(map<string, unsigned int>::iterator i = soundsByName.begin(); i != soundsByName.end(); i++) {
		
		unsigned int num = i->second;
		string name = i->first;
		
		soundAges[num]++;
		if(soundAges[num] > maxAge) {
			namesToPurge.push_back(name);
		}
	}

    if(namesToPurge.size()) {

        soundManagerMutex->lock();

        for(unsigned int i = 0; i < namesToPurge.size(); i++) {
		
            unsigned int num = soundsByName[namesToPurge[i]];
            string name = namesToPurge[i];
		
            soundAges.erase(num);
            namesByNumber.erase(num);
            soundsByName.erase(name);

            out("system") << "Destroying sound: " << name << endl;

            soundManager.freeSound(num);
        }

        soundManagerMutex->unlock();
    }
}

void SoundResourceManager::queueSound(const std::string &fileName, float volume) {

    soundQueueMutex->lock();
    QueuedSound snd;
    snd.fileName = fileName;
    snd.volume = volume;
    queuedSounds.push_back(snd);
    soundQueueMutex->unlock();

}

void SoundResourceManager::queueMusic(const std::string &musicName) {

    soundQueueMutex->lock();
    queuedMusic = musicName;
    soundQueueMutex->unlock();

}

void SoundResourceManager::update(void) {

    soundManagerMutex->lock();
    soundManager.updateMusic();
    soundManagerMutex->unlock();

    // Move over the list of queued sounds.
    soundQueueMutex->lock();
    vector<QueuedSound> localQueuedSounds = queuedSounds;
    queuedSounds.clear();
    std::string newMusic = queuedMusic;
    soundQueueMutex->unlock();

    // Handle individual sounds.
    for(unsigned int i = 0; i < localQueuedSounds.size(); i++) {

        string fileName = localQueuedSounds[i].fileName;

        soundManagerMutex->lock();
        unsigned int soundNum = getSound(fileName);
        if(soundNum) {
            soundManager.playSound(soundNum, localQueuedSounds[i].volume);
        }
        soundManagerMutex->unlock();
    }

    // Handle music.
    if(newMusic != currentMusic) {
        soundManagerMutex->lock();
        if(newMusic.size()) {
            soundManager.startMusic(newMusic);
        } else {
            soundManager.stopMusic();
        }
        soundManagerMutex->unlock();
        currentMusic = newMusic;
    }

    cleanOldSounds();
}

float SoundResourceManager::getSoundVolume() {
    soundManagerMutex->lock();
    float r = soundManager.getSoundVolume();
    soundManagerMutex->unlock();
    return r;
}

float SoundResourceManager::getMusicVolume() {
    soundManagerMutex->lock();
    float r = soundManager.getMusicVolume();
    soundManagerMutex->unlock();
    return r;
}

void SoundResourceManager::setSoundVolume(float v) {
    soundManagerMutex->lock();
    soundManager.setSoundVolume(v);
    soundManagerMutex->unlock();
}

void SoundResourceManager::setMusicVolume(float v) {
    soundManagerMutex->lock();
    soundManager.setMusicVolume(v);
    soundManagerMutex->unlock();
}

