/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "plugutil.h"
#include <esd.h>

/* name prefix to give to EsounD */
#define SNDPREFIX	"golem_sound"

/*
 * sound configurations are stored as filenames when we aren't
 * cacheing the sounds to the server, or as an esd sample id
 * if we are.
 */
typedef union {
	char	*fn;
	int	sid;
} snddata_t;

/* filenames to play for various windowmanager events */
static snddata_t	sound_birth;
static snddata_t	sound_death;
static snddata_t	sound_iconify;
static snddata_t	sound_restore;
static snddata_t	sound_zoom;
static snddata_t	sound_unzoom;
static snddata_t	sound_deskchange;
static snddata_t	sound_wspacechange;

/* should sounds be cached on the server */
static int	sound_cache;

/* the esd server */
static int	esdhost		= -1;

/* callback for windowmanager events */
static int sound_callback(int pcall, client_t *client, ...) {
	snddata_t *snd;

	/* look up the filename to play */
	switch (pcall) {
	case PCALL_WINDOW_BIRTH:
		/*
		 * don't play sounds during startup when we
		 * are reparenting windows that already exist.
		 */
		if (client->flags.unmap_from_reparent)
			return PLUGIN_OK;
		snd = &sound_birth;
		break;
	case PCALL_WINDOW_DEATH:
		snd = &sound_death;
		break;
	case PCALL_ICONIFY_NOTIFY:
		snd = &sound_iconify;
		break;
	case PCALL_RESTORE_NOTIFY:
		snd = &sound_restore;
		break;
	case PCALL_ZOOM_NOTIFY:
		snd = &sound_zoom;
		break;
	case PCALL_UNZOOM_NOTIFY:
		snd = &sound_unzoom;
		break;
	case PCALL_DESKTOP_CHANGE:
		snd = &sound_deskchange;
		break;
	case PCALL_WORKSPACE_CHANGE:
		snd = &sound_wspacechange;
		break;
	default:
		PTRACE("unhandled windowmanager event");
		return PLUGIN_OK;
	}

	/* play the sound */
	if (sound_cache && snd->sid)
		esd_sample_play(esdhost, snd->sid);
	else if (snd->fn)
		esd_play_file(SNDPREFIX, snd->fn, 1);

	return PLUGIN_OK;
}

/*
 * plugin initialization; look through params for files to play
 * for various windowmanager events.
 */
int sound_init() {
	/* register all events into one callback */
	plugin_callback_add(plugin_this, PCALL_WINDOW_BIRTH, sound_callback);
	plugin_callback_add(plugin_this, PCALL_WINDOW_DEATH, sound_callback);
	plugin_callback_add(plugin_this, PCALL_ICONIFY_NOTIFY, sound_callback);
	plugin_callback_add(plugin_this, PCALL_RESTORE_NOTIFY, sound_callback);
	plugin_callback_add(plugin_this, PCALL_ZOOM_NOTIFY, sound_callback);
	plugin_callback_add(plugin_this, PCALL_UNZOOM_NOTIFY, sound_callback);
	plugin_callback_add(plugin_this, PCALL_DESKTOP_CHANGE, sound_callback);
	plugin_callback_add(plugin_this, PCALL_WORKSPACE_CHANGE, sound_callback);

	/* get sound file names from the plugin parameters */
	OPTIONAL_PARAM(&plugin_this->params, "sound_birth", string, sound_birth.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_death", string, sound_death.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_iconify", string, sound_iconify.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_restore", string, sound_restore.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_zoom", string, sound_zoom.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_unzoom", string, sound_unzoom.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_deskchange", string, sound_deskchange.fn, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "sound_wspacechange", string,
		sound_wspacechange.fn, NULL);

	/* configuration parameters */
	OPTIONAL_PARAM(&plugin_this->params, "sound_cache", bool, sound_cache, 1);

	return PLUGIN_OK;
}

/*
 * plugin startup; if configured to do so, cache all the sounds files into
 * the EsounD server.
 */
int sound_start() {
	int id;

	/* connect to the server if we're going to cache sounds */
	if (!sound_cache)
		return PLUGIN_OK;
	if ((esdhost = esd_open_sound(NULL)) == -1) {
		sound_cache = 0;
		return PLUGIN_OK;
	}

#define CACHESND(snd) do {					\
	if (snd.fn) {						\
		id = esd_file_cache(esdhost, SNDPREFIX, snd.fn);\
		free(snd.fn);					\
		snd.sid = id <= 0 ? 0 : id;			\
	} else							\
		snd.sid = 0;					\
} while (0)

	CACHESND(sound_birth);
	CACHESND(sound_death);
	CACHESND(sound_iconify);
	CACHESND(sound_restore);
	CACHESND(sound_zoom);
	CACHESND(sound_unzoom);
	CACHESND(sound_wspacechange);
	CACHESND(sound_deskchange);

#undef CACHESND

	return PLUGIN_OK;
}

/* plugin termination */
void sound_shutdown() {
	if (!sound_cache) {
		if (sound_birth.fn)		free(sound_birth.fn);
		if (sound_death.fn)		free(sound_death.fn);
		if (sound_iconify.fn)		free(sound_iconify.fn);
		if (sound_restore.fn)		free(sound_restore.fn);
		if (sound_zoom.fn)		free(sound_zoom.fn);
		if (sound_unzoom.fn)		free(sound_unzoom.fn);
		if (sound_wspacechange.fn)	free(sound_wspacechange.fn);
		if (sound_deskchange.fn)	free(sound_deskchange.fn);
	} else {
		if (sound_birth.sid)
			esd_sample_free(esdhost, sound_birth.sid);
		if (sound_death.sid)
			esd_sample_free(esdhost, sound_death.sid);
		if (sound_iconify.sid)
			esd_sample_free(esdhost, sound_iconify.sid);
		if (sound_restore.sid)
			esd_sample_free(esdhost, sound_restore.sid);
		if (sound_zoom.sid)
			esd_sample_free(esdhost, sound_zoom.sid);
		if (sound_unzoom.sid)
			esd_sample_free(esdhost, sound_unzoom.sid);
		if (sound_wspacechange.sid)
			esd_sample_free(esdhost, sound_wspacechange.sid);
		if (sound_deskchange.sid)
			esd_sample_free(esdhost, sound_deskchange.sid);

		esd_close(esdhost);
	}
}

/* stuff we don't care about */
void sound_xevent_handler(XEvent *e) {}
