/*
 * ALSA <-> Audioflinger CTL plugin
 *
 * Copyright (c) 2010 by Motorola Corporation
 * Copyright (C) 2011-2012 Canonical, Ltd.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include <alsa/asoundlib.h>
#include <alsa/control_external.h>
#include "waudio.h"

#define ANDROID_AUDIO_ID "0"
#define ANDROID_AUDIO_DRIVER "android-audio-router"
#define ANDROID_AUDIO_NAME "android-audio-router"
#define ANDROID_AUDIO_LONGNAME "android-audio-router to audioflinger"
#define ANDROID_AUDIO_MIXERNAME "android-audio-router to audioflinger"

enum {
	VOLUME_MASTER = 0,
        VOLUME_MASTER_MUTE,
	VOLUME_PULSE_STREAM,
	VOLUME_PULSE_STREAM_MUTE,
	VOLUME_MICROPHONE_MUTE,
	VOLUME_COUNT
};

typedef struct snd_ctl_android {
	snd_ctl_ext_t ext;
} snd_ctl_android_t;

static const char *const vol_name[VOLUME_COUNT] = {
	[VOLUME_MASTER] = "Master Playback Volume",
	[VOLUME_MASTER_MUTE] =	"Master Playback Switch",
	[VOLUME_PULSE_STREAM] = "PulseAudio Stream Volume",
	[VOLUME_PULSE_STREAM_MUTE] = "PulseAudio Stream Switch",
	[VOLUME_MICROPHONE_MUTE] = "Microphone Switch"
};

static void android_close(snd_ctl_ext_t *ext)
{
	snd_ctl_android_t *android = ext->private_data;

	free(android);
}

static int android_elem_count(snd_ctl_ext_t *ext)
{
	snd_ctl_android_t *android = ext->private_data;

	assert(android);

	return VOLUME_COUNT;
}

static int android_elem_list(snd_ctl_ext_t *ext,
		unsigned int offset, snd_ctl_elem_id_t *id)
{
	snd_ctl_android_t *android = ext->private_data;

	assert(android);

	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
	if (offset < VOLUME_COUNT)
		snd_ctl_elem_id_set_name(id, vol_name[offset]);

	return 0;
}

static snd_ctl_ext_key_t android_find_elem(snd_ctl_ext_t *ext,
				       const snd_ctl_elem_id_t *id)
{
	snd_ctl_android_t *android = ext->private_data;
	const char *name;
	unsigned int numid, i;

	assert(android);

	numid = snd_ctl_elem_id_get_numid(id);
	if (numid > 0 && numid <= VOLUME_COUNT)
		return numid - 1;

	name = snd_ctl_elem_id_get_name(id);

	for (i = 0; i < VOLUME_COUNT; i++) {
		if (strcmp(name, vol_name[i]) == 0)
			return i;
	}

	return SND_CTL_EXT_KEY_NOT_FOUND;
}

static int android_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
			     int *type, unsigned int *acc, unsigned int *count)
{
	snd_ctl_android_t *android = ext->private_data;

	if (key > VOLUME_COUNT -1)
		return -EINVAL;

	assert(android);

	switch (key) {
	case VOLUME_MASTER:
		*type = SND_CTL_ELEM_TYPE_INTEGER;
		break;
	case VOLUME_MASTER_MUTE:
		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
		break;
	case VOLUME_PULSE_STREAM:
		*type = SND_CTL_ELEM_TYPE_INTEGER;
		break;
	case VOLUME_PULSE_STREAM_MUTE:
		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
		break;
	case VOLUME_MICROPHONE_MUTE:
		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
		break;
	default:
		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
		break;
	}

	*acc = SND_CTL_EXT_ACCESS_READWRITE;
	*count = 1;

	return 0;
}

static int android_get_integer_info(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
				snd_ctl_ext_key_t key, long *imin, long *imax,
				long *istep)
{
	switch (key) {
	case VOLUME_MASTER:
		*istep = 1;
		*imin = 0;
		*imax = 10;
		break;
	case VOLUME_PULSE_STREAM:
		*istep = 1;
		*imin = 0;
		*imax = 10;
		break;
	default:
		break;
	}

	return 0;
}

static int android_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
				long *value)
{
	snd_ctl_android_t *android = ext->private_data;
	int err = 0, mute = 0;
	float fvol = 0;

	assert(android);

	switch (key) {
	case VOLUME_MASTER:
		if (AudioSystem_getMasterVolume(&fvol) == OK) {
			*value = (int) (fvol * 10);
		}
		break;
	case VOLUME_MASTER_MUTE:
		if (AudioSystem_getMasterMute(&mute) == OK)
			*value = mute ^ 1;
		break;
	case VOLUME_PULSE_STREAM:
		if (AudioSystem_getStreamVolume(AUDIO_STREAM_MUSIC, &fvol, 0) == OK) {
			*value = (int) (fvol * 10);
		}
		break;
	case VOLUME_PULSE_STREAM_MUTE:
		if (AudioSystem_getStreamMute(AUDIO_STREAM_MUSIC, &mute) == OK)
			*value = mute ^ 1;
		break;
	case VOLUME_MICROPHONE_MUTE:
		if (AudioSystem_isMicrophoneMuted(&mute) == OK)
			*value = mute ^ 1;
		break;
	default:
		err = -EINVAL;
		break;
	}

	return err;
}

static int android_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
				long *value)
{
	snd_ctl_android_t *android = ext->private_data;
	int err = 0;
	float fvalue;

	assert(android);

	switch (key) {
	case VOLUME_MASTER:
		fvalue = (float) *value / 10;
		if (AudioSystem_setMasterVolume(fvalue) != OK)
			printf("error in master set volume\n");
		break;
	case VOLUME_MASTER_MUTE:
		if (AudioSystem_setMasterMute(*value ^ 1) != OK)
			printf("error in master set mute\n");
		break;
	case VOLUME_PULSE_STREAM:
		fvalue = (float) *value / 10;
		if (AudioSystem_setStreamVolume(AUDIO_STREAM_MUSIC, fvalue, 0) != OK)
			printf("error in pulse stream set volume\n");
		break;
	case VOLUME_PULSE_STREAM_MUTE:
		if (AudioSystem_setStreamMute(AUDIO_STREAM_MUSIC, *value ^ 1) != OK)
			printf("error in pulse stream set mute\n");
		break;
	case VOLUME_MICROPHONE_MUTE:
		if (AudioSystem_muteMicrophone(*value ^ 1) != OK)
			printf("error in microphone set mute\n");
		break;
	default:
		err = -EINVAL;
		break;
	}

	return err;
}

static int android_read_event(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
			  snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED,
			  unsigned int *event_mask ATTRIBUTE_UNUSED)
{
	return -EAGAIN;
}

static snd_ctl_ext_callback_t android_ext_callback = {
	.close = android_close,
	.elem_count = android_elem_count,
	.elem_list = android_elem_list,
	.find_elem = android_find_elem,
	.get_attribute = android_get_attribute,
	.get_integer_info = android_get_integer_info,
	.read_integer = android_read_integer,
	.write_integer = android_write_integer,
	.read_event = android_read_event,
};


SND_CTL_PLUGIN_DEFINE_FUNC(android)
{
	snd_config_iterator_t it, next;
	int err;
	snd_ctl_android_t *android;

	snd_config_for_each(it, next, conf) {
		snd_config_t *n = snd_config_iterator_entry(it);
		const char *id;
		if (snd_config_get_id(n, &id) < 0)
			continue;
		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 ||
			strcmp(id, "hint") == 0	|| strcmp(id, "device") == 0)
			continue;

		SNDERR("Unknown field %s", id);
		return -EINVAL;
	}

	android = calloc(1, sizeof(*android));

	android->ext.version = SND_CTL_EXT_VERSION;
	android->ext.card_idx = 0;
	strncpy(android->ext.id, ANDROID_AUDIO_ID, sizeof(android->ext.id) - 1);
	strncpy(android->ext.driver, ANDROID_AUDIO_DRIVER,
				sizeof(android->ext.driver) - 1);
	strncpy(android->ext.name, ANDROID_AUDIO_NAME,
				sizeof(android->ext.name) - 1);
	strncpy(android->ext.longname, ANDROID_AUDIO_LONGNAME,
				sizeof(android->ext.longname) - 1);
	strncpy(android->ext.mixername, ANDROID_AUDIO_MIXERNAME,
				sizeof(android->ext.mixername) - 1);
	android->ext.poll_fd = -1;
	android->ext.callback = &android_ext_callback;
	android->ext.private_data = android;

	err = snd_ctl_ext_create(&android->ext, name, mode);
	if (err < 0)
		goto error;

	*handlep = android->ext.handle;
	return 0;

error:
	free(android);
	return err;
}

SND_CTL_PLUGIN_SYMBOL(android);
