/* Copyright 2016 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "cras_alsa_mixer_name.h"
#include "utlist.h"

struct mixer_name *mixer_name_add(struct mixer_name *names,
				  const char *name,
				  enum CRAS_STREAM_DIRECTION dir,
				  mixer_name_type type)
{
	struct mixer_name *m_name;

	if (!name)
		return names;

	m_name = (struct mixer_name *)calloc(1, sizeof(struct mixer_name));
	if (!m_name)
		return names;

	m_name->name = strdup(name);
	if (!m_name->name) {
		free(m_name);
		return names;
	}
	m_name->dir = dir;
	m_name->type = type;

	DL_APPEND(names, m_name);
	return names;
}

struct mixer_name *mixer_name_add_array(struct mixer_name *names,
					const char * const *name_array,
					size_t name_array_size,
					enum CRAS_STREAM_DIRECTION dir,
					mixer_name_type type)
{
	size_t i;
	for (i = 0; i < name_array_size; i++)
		names = mixer_name_add(names, name_array[i], dir, type);
	return names;
}

void mixer_name_free(struct mixer_name *names)
{
	struct mixer_name *m_name;
	DL_FOREACH(names, m_name) {
		DL_DELETE(names, m_name);
		free((void*)m_name->name);
		free(m_name);
	}
}

struct mixer_name *mixer_name_find(struct mixer_name *names,
				   const char *name,
				   enum CRAS_STREAM_DIRECTION dir,
				   mixer_name_type type)
{
	if (!name && type == MIXER_NAME_UNDEFINED)
		return NULL;

	struct mixer_name *m_name;
	DL_FOREACH(names, m_name) {
		/* Match the direction. */
		if (dir != m_name->dir)
			continue;
		/* Match the type unless the type is UNDEFINED. */
		if (type != MIXER_NAME_UNDEFINED &&
		    type != m_name->type)
			continue;
		/* Match the name if it is non-NULL, or return the first
		 * item with the correct type when the name is not defined. */
		if ((type != MIXER_NAME_UNDEFINED && !name) ||
		    (name && !strcmp(m_name->name, name)))
			return m_name;
	}
	return NULL;
}

static const char *mixer_name_type_str(enum CRAS_STREAM_DIRECTION dir,
				       mixer_name_type type)
{
	switch (dir) {
	case CRAS_STREAM_OUTPUT:
		switch (type) {
		case MIXER_NAME_VOLUME:
			return "output volume";
		case MIXER_NAME_MAIN_VOLUME:
			return "main volume";
		case MIXER_NAME_UNDEFINED:
			break;
		}
		break;
	case CRAS_STREAM_INPUT:
		switch (type) {
		case MIXER_NAME_VOLUME:
			return "input volume";
		case MIXER_NAME_MAIN_VOLUME:
			return "main capture";
		case MIXER_NAME_UNDEFINED:
			break;
		}
		break;
	case CRAS_STREAM_UNDEFINED:
	case CRAS_STREAM_POST_MIX_PRE_DSP:
	case CRAS_NUM_DIRECTIONS:
		break;
	}
	return "undefined";
}

void mixer_name_dump(struct mixer_name *names, const char *message)
{
	struct mixer_name *m_name;

	if (!names) {
		syslog(LOG_DEBUG, "%s: empty", message);
		return;
	}

	syslog(LOG_DEBUG, "%s:", message);
	DL_FOREACH(names, m_name) {
		const char *type_str =
			mixer_name_type_str(m_name->dir, m_name->type);
		syslog(LOG_DEBUG, "    %s %s", m_name->name, type_str);
	}
}