/* $Id: win32_m.cpp 9050 2007-03-07 11:47:46Z rubidium $ */

#include "../stdafx.h"
#include "win32_m.h"
#include <windows.h>
#include <mmsystem.h>

static struct {
	bool stop_song;
	bool terminate;
	bool playing;
	int new_vol;
	HANDLE wait_obj;
	UINT_PTR devid;
	char start_song[260];
} _midi;

static void Win32MidiPlaySong(const char *filename)
{
	strcpy(_midi.start_song, filename);
	_midi.playing = true;
	_midi.stop_song = false;
	SetEvent(_midi.wait_obj);
}

static void Win32MidiStopSong()
{
	if (_midi.playing) {
		_midi.stop_song = true;
		_midi.start_song[0] = '\0';
		SetEvent(_midi.wait_obj);
	}
}

static bool Win32MidiIsSongPlaying()
{
	return _midi.playing;
}

static void Win32MidiSetVolume(byte vol)
{
	_midi.new_vol = vol;
	SetEvent(_midi.wait_obj);
}

static MCIERROR CDECL MidiSendCommand(const char* cmd, ...)
{
	va_list va;
	char buf[512];

	va_start(va, cmd);
	vsnprintf(buf, lengthof(buf), cmd, va);
	va_end(va);
	return mciSendStringA(buf, NULL, 0, 0);
}

static bool MidiIntPlaySong(const char *filename)
{
	MidiSendCommand("close all");
	if (MidiSendCommand("open \"%s\" type sequencer alias song", filename) != 0) return false;

	return MidiSendCommand("play song from 0") == 0;
}

static void MidiIntStopSong()
{
	MidiSendCommand("close all");
}

static void MidiIntSetVolume(int vol)
{
	DWORD v = (vol * 65535 / 127);
	midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16));
}

static bool MidiIntIsSongPlaying()
{
	char buf[16];
	mciSendStringA("status song mode", buf, sizeof(buf), 0);
	return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
}

static DWORD WINAPI MidiThread(LPVOID arg)
{
	_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL);

	do {
		char *s;
		int vol;

		vol = _midi.new_vol;
		if (vol != -1) {
			_midi.new_vol = -1;
			MidiIntSetVolume(vol);
		}

		s = _midi.start_song;
		if (s[0] != '\0') {
			_midi.playing = MidiIntPlaySong(s);
			s[0] = '\0';

			// Delay somewhat in case we don't manage to play.
			if (!_midi.playing) Sleep(5000);
		}

		if (_midi.stop_song && _midi.playing) {
			_midi.stop_song = false;
			_midi.playing = false;
			MidiIntStopSong();
		}

		if (_midi.playing && !MidiIntIsSongPlaying()) _midi.playing = false;

		WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000);
	} while (!_midi.terminate);

	DeleteObject(_midi.wait_obj);
	return 0;
}

static const char *Win32MidiStart(const char * const *parm)
{
	MIDIOUTCAPS midicaps;
	DWORD threadId;
	UINT nbdev;
	UINT_PTR dev;
	char buf[16];

	mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0);
	if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio";

	memset(&_midi, 0, sizeof(_midi));
	_midi.new_vol = -1;

	/* Get midi device */
	_midi.devid = MIDI_MAPPER;
	for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) {
		if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) {
			_midi.devid = dev;
			break;
		}
	}

	if (CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId) == NULL) return "Failed to create thread";

	return NULL;
}

static void Win32MidiStop()
{
	_midi.terminate = true;
	SetEvent(_midi.wait_obj);
}

const HalMusicDriver _win32_music_driver = {
	Win32MidiStart,
	Win32MidiStop,
	Win32MidiPlaySong,
	Win32MidiStopSong,
	Win32MidiIsSongPlaying,
	Win32MidiSetVolume,
};
