/*
 *  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 of the License, or (at your option) any later version.
 *
 *  This library 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  Support for the verb/device/modifier core logic and API,
 *  command line tool and file parser was kindly sponsored by
 *  Texas Instruments Inc.
 *  Support for multiple active modifiers and devices,
 *  transition sequences, multiple client access and user defined use
 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
 *
 *  Copyright (C) 2008-2010 SlimLogic Ltd
 *  Copyright (C) 2010 Wolfson Microelectronics PLC
 *  Copyright (C) 2010 Texas Instruments Inc.
 *  Copyright (C) 2010 Red Hat Inc.
 *  Authors: Liam Girdwood <lrg@slimlogic.co.uk>
 *	         Stefan Schmidt <stefan@slimlogic.co.uk>
 *	         Justin Xu <justinx@slimlogic.co.uk>
 *               Jaroslav Kysela <perex@perex.cz>
 */

#include "ucm_local.h"

void uc_mgr_error(const char *fmt,...)
{
	va_list va;
	va_start(va, fmt);
	fprintf(stderr, "ucm: ");
	vfprintf(stderr, fmt, va);
	va_end(va);
}

void uc_mgr_stdout(const char *fmt,...)
{
	va_list va;
	va_start(va, fmt);
	vfprintf(stdout, fmt, va);
	va_end(va);
}

int uc_mgr_config_load(const char *file, snd_config_t **cfg)
{
	FILE *fp;
	snd_input_t *in;
	snd_config_t *top;
	char *default_path;
	int err;

	fp = fopen(file, "r");
	err = fp == NULL ? -errno : snd_input_stdio_attach(&in, fp, 1);
	if (err < 0) {
		uc_error("could not open configuration file %s", file);
		return err;
	}
	err = snd_config_top(&top);
	if (err < 0)
		return err;

	default_path = getenv(ALSA_CONFIG_UCM_VAR);
	if (!default_path || !*default_path)
		default_path = ALSA_CONFIG_DIR "/ucm";
	default_path = strdup(default_path);
	if (!default_path) {
		err = -ENOMEM;
		goto __err2;
	}
	err = _snd_config_load_with_include(top, in, 0, default_path);
	if (err < 0) {
		uc_error("could not load configuration file %s", file);
		goto __err2;
	}
	err = snd_input_close(in);
	if (err < 0)
		goto __err2;
	*cfg = top;
	return 0;

 __err2:
        snd_config_delete(top);
	return err;
}

void uc_mgr_free_value(struct list_head *base)
{
	struct list_head *pos, *npos;
	struct ucm_value *val;
	
	list_for_each_safe(pos, npos, base) {
		val = list_entry(pos, struct ucm_value, list);
		free(val->name);
		free(val->data);
		list_del(&val->list);
		free(val);
	}
}

void uc_mgr_free_dev_list(struct dev_list *dev_list)
{
	struct list_head *pos, *npos;
	struct dev_list_node *dlist;
	
	list_for_each_safe(pos, npos, &dev_list->list) {
		dlist = list_entry(pos, struct dev_list_node, list);
		free(dlist->name);
		list_del(&dlist->list);
		free(dlist);
	}
}

void uc_mgr_free_sequence_element(struct sequence_element *seq)
{
	if (seq == NULL)
		return;
	switch (seq->type) {
	case SEQUENCE_ELEMENT_TYPE_CSET:
	case SEQUENCE_ELEMENT_TYPE_EXEC:
		free(seq->data.exec);
		break;
	default:
		break;
	}
	free(seq);
}

void uc_mgr_free_sequence(struct list_head *base)
{
	struct list_head *pos, *npos;
	struct sequence_element *seq;
	
	list_for_each_safe(pos, npos, base) {
		seq = list_entry(pos, struct sequence_element, list);
		list_del(&seq->list);
		uc_mgr_free_sequence_element(seq);
	}
}

void uc_mgr_free_transition_element(struct transition_sequence *tseq)
{
	free(tseq->name);
	uc_mgr_free_sequence(&tseq->transition_list);
	free(tseq);
}

void uc_mgr_free_transition(struct list_head *base)
{
	struct list_head *pos, *npos;
	struct transition_sequence *tseq;
	
	list_for_each_safe(pos, npos, base) {
		tseq = list_entry(pos, struct transition_sequence, list);
		list_del(&tseq->list);
		uc_mgr_free_transition_element(tseq);
	}
}

void uc_mgr_free_modifier(struct list_head *base)
{
	struct list_head *pos, *npos;
	struct use_case_modifier *mod;
	
	list_for_each_safe(pos, npos, base) {
		mod = list_entry(pos, struct use_case_modifier, list);
		free(mod->name);
		free(mod->comment);
		uc_mgr_free_sequence(&mod->enable_list);
		uc_mgr_free_sequence(&mod->disable_list);
		uc_mgr_free_transition(&mod->transition_list);
		uc_mgr_free_dev_list(&mod->dev_list);
		uc_mgr_free_value(&mod->value_list);
		list_del(&mod->list);
		free(mod);
	}
}

void uc_mgr_free_device(struct list_head *base)
{
	struct list_head *pos, *npos;
	struct use_case_device *dev;
	
	list_for_each_safe(pos, npos, base) {
		dev = list_entry(pos, struct use_case_device, list);
		free(dev->name);
		free(dev->comment);
		uc_mgr_free_sequence(&dev->enable_list);
		uc_mgr_free_sequence(&dev->disable_list);
		uc_mgr_free_transition(&dev->transition_list);
		uc_mgr_free_dev_list(&dev->dev_list);
		uc_mgr_free_value(&dev->value_list);
		list_del(&dev->list);
		free(dev);
	}
}

void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
{
	struct list_head *pos, *npos;
	struct use_case_verb *verb;

	list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
		verb = list_entry(pos, struct use_case_verb, list);
		free(verb->name);
		free(verb->comment);
		uc_mgr_free_sequence(&verb->enable_list);
		uc_mgr_free_sequence(&verb->disable_list);
		uc_mgr_free_transition(&verb->transition_list);
		uc_mgr_free_value(&verb->value_list);
		uc_mgr_free_device(&verb->device_list);
		uc_mgr_free_device(&verb->cmpt_device_list);
		uc_mgr_free_modifier(&verb->modifier_list);
		list_del(&verb->list);
		free(verb);
	}
	uc_mgr_free_sequence(&uc_mgr->default_list);
	uc_mgr_free_value(&uc_mgr->value_list);
	free(uc_mgr->comment);
	uc_mgr->comment = NULL;
	uc_mgr->active_verb = NULL;
	INIT_LIST_HEAD(&uc_mgr->active_devices);
	INIT_LIST_HEAD(&uc_mgr->active_modifiers);
	if (uc_mgr->ctl != NULL) {
		snd_ctl_close(uc_mgr->ctl);
		uc_mgr->ctl = NULL;
	}
	free(uc_mgr->ctl_dev);
	uc_mgr->ctl_dev = NULL;
}

void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
{
	uc_mgr_free_verb(uc_mgr);
	free(uc_mgr->card_name);
	free(uc_mgr);
}
