module coneneko.sound;
import
	sdl,
	sdl_mixer,
	std.windows.charset,
	std.string;

string getSdlError() ///
{
	return std.string.toString(SDL_GetError());
}

///
class Sound
{
	protected static Mix_Chunk*[string] chunkMap;
	protected static bool[Se] channelMap;
	protected const VOID = false; // 特に意味のない値
	
	static void initialize() ///
	{
		if (SDL_WasInit(SDL_INIT_AUDIO)) return;
		Mix_OpenAudio(44100, AUDIO_S16, 2, 4096);
		Mix_ChannelFinished(&channelFinished);
	}
	
	extern (C) static void channelFinished(int channel)
	{
		foreach (v; channelMap.keys)
		{
			if (channel == v.channel) v.channel = -1;
		}
	}
	
	static ~this() ///
	{
		Mix_ChannelFinished(null);
		releaseResource();
		Mix_CloseAudio();
	}
	
	static void releaseResource() ///
	{
		foreach (Mix_Chunk* v; chunkMap.values) Mix_FreeChunk(v);
		chunkMap = null;
	}
}

///
class Se : Sound
{
	private int channel = -1;
	const string fileName; ///
	
	///
	this(string fileName)
	{
		initialize();
		this.fileName = fileName;
		if (fileName in chunkMap) return;
		channelMap[this] = VOID;
		auto chunk = Mix_LoadWAV_RW(SDL_RWFromFile(cast(char*)fileName.toMBSz(), cast(char*)"rb"), 1);
		if (!chunk) throw new Error("Se.this " ~ getSdlError());
		chunkMap[fileName] = chunk;
	}
	
	///
	~this() { stop(); channelMap.remove(this); }
	
	void stop() { if (playing) Mix_HaltChannel(channel); } ///
	bool playing() { return channel != -1; } ///
	
	void play(bool loop = false) ///
	{
		stop();
		assert(!playing);
		channel = Mix_PlayChannelTimed(-1, chunkMap[fileName], loop ? -1 : 0, -1);
	}
	
	static Se play(string fileName, bool loop = false) ///
	{
		Se result = new Se(fileName);
		result.play(loop);
		return result;
	}
}

///
class Bgm : Sound
{
	private static Mix_Music* music;
	private static string _fileName;
	static string fileName() { return _fileName; } /// なければnull
	static bool playing() { return _fileName ? true : false; } ///
	
	static void play(string fileName) ///
	{
		initialize();
		if (music) stop();
		music = Mix_LoadMUS(cast(char*)fileName.toMBSz());
		if (!music) throw new Error("Bgm.play " ~ getSdlError());
		Mix_PlayMusic(music, -1);
		_fileName = fileName;
	}
	
	static void stop() ///
	{
		if (!music) return;
		_fileName = null;
		Mix_HaltMusic();
		Mix_FreeMusic(music);
		music = null;
	}
}
