/*********************************************************\
		 __      __           __  __
		/ /     / / _____    / / / / ______   __     __
	   / /     / / /___  \  / / / / / ___  | / /    / /
	  / /_____/ / _____| / / / / / / /__/ / / /    / /
	 / ______  / / ___  / / / / / / _____/ / /    / /
	/ /     / / / /__/ / / / / / / |____  / |____/ /
   /_/     /_/ |______/ /_/ /_/  \_____/  \_____  /
										 ______/ /
			High Level Game Framework   /_______/

  ------------------------------------------------------

  Copyright (c) 2008 - Rodrigo Braz Monteiro.
  This file is subject to the terms of license.txt.

\*********************************************************/

#include "audio.h"
#include "exception.h"
#include "sample_manager.h"
#include "music_manager.h"
#include "SDL_mixer.h"

using namespace Halley;

Audio::Audio()
{
	char buffer[1024];
	std::cout << "Initializing audio: " << SDL_AudioDriverName(buffer, 1024) << std::endl;

	int flags = MIX_INIT_OGG | MIX_INIT_MOD;
	int rFlags = Mix_Init(flags);
	if (rFlags != flags) {
		if ((rFlags & MIX_INIT_OGG) == 0) throw Exception("Ogg Vorbis decoding not available.");
		if ((rFlags & MIX_INIT_MOD) == 0) throw Exception("MOD decoding not available.");
	}

	if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1) {
		throw Exception(String("Error opening audio system: ") + Mix_GetError());
	}

	setNumChannels(16);

	int mDecs = Mix_GetNumMusicDecoders();
	std::cout << "Music decoders:" << std::endl;
	for (int i=0; i<mDecs; i++) {
		std::cout << "    " << Mix_GetMusicDecoder(i) << std::endl;
	}
}

Audio::~Audio()
{
	MusicManager::clear();
	SampleManager::clear();
	Mix_CloseAudio();
	Mix_Quit();
}

static void channelFinished(int channelN)
{
	Audio::get()->onChannelFinished(channelN);
}

void Audio::onChannelFinished(int n)
{
	Mix_UnregisterAllEffects(n);
	channels[n].playing = false;
}

void Audio::setNumChannels(int n)
{
	Mix_AllocateChannels(n);
	channels.resize(n);
	Mix_ChannelFinished(channelFinished);
}

shared_ptr<Audio> Audio::get()
{
	if (!instance) instance = shared_ptr<Audio>(new Audio());
	return instance;
}

int Audio::allocateChannel()
{
	for (size_t i=0; i<channels.size(); i++) {
		if (!channels[i].playing) {
			channels[i].playing = true;
			return i;
		}
	}
	return -1;
}

void Audio::play(String name, float pan, float volume)
{
	// Get sample
	auto sample = SampleManager::get(name);
	if (!sample) throw Exception("Unable to find sample: "+name);

	// Get channel
	int channel = allocateChannel();

	// Play
	if (channel >= 0) {
		bool played = sample->play(channel, pan, volume);
		if (!played) channels[channel].playing = false;
	}
}

void Audio::playMusic(String name, int loops)
{
	auto mus = MusicManager::get(name);
	if (!mus) throw Exception("Unable to find music: "+name);
	mus->play(loops);
}

void Audio::stopMusic()
{
	Mix_HaltMusic();
}

shared_ptr<Audio> Audio::instance;
