/* ALSA Card Profile */
/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
/* SPDX-License-Identifier: MIT */

#ifndef PA_PROPLIST_H
#define PA_PROPLIST_H

#include <stdio.h>

#include "array.h"
#include "acp.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PA_PROP_DEVICE_DESCRIPTION             "device.description"

#define PA_PROP_DEVICE_CLASS                   "device.class"

#define PA_PROP_DEVICE_FORM_FACTOR             "device.form_factor"

#define PA_PROP_DEVICE_INTENDED_ROLES          "device.intended_roles"

#define PA_PROP_DEVICE_PROFILE_NAME            "device.profile.name"

#define PA_PROP_DEVICE_STRING                  "device.string"

#define PA_PROP_DEVICE_API                     "device.api"

#define PA_PROP_DEVICE_PRODUCT_NAME            "device.product.name"

#define PA_PROP_DEVICE_PROFILE_DESCRIPTION     "device.profile.description"

typedef struct pa_proplist_item {
	char *key;
	char *value;
} pa_proplist_item;

typedef struct pa_proplist {
	struct pa_array array;
} pa_proplist;

static inline pa_proplist* pa_proplist_new(void)
{
	pa_proplist *p = calloc(1, sizeof(*p));
	pa_array_init(&p->array, 16);
	return p;
}
static inline pa_proplist_item* pa_proplist_item_find(const pa_proplist *p, const void *key)
{
	pa_proplist_item *item;
	pa_array_for_each(item, &p->array) {
		if (strcmp(key, item->key) == 0)
			return item;
	}
	return NULL;
}

static inline void pa_proplist_item_free(pa_proplist_item* it)
{
	free(it->key);
	free(it->value);
}

static inline void pa_proplist_clear(pa_proplist* p)
{
	pa_proplist_item *item;
	pa_array_for_each(item, &p->array)
		pa_proplist_item_free(item);
	pa_array_reset(&p->array);
}

static inline void pa_proplist_free(pa_proplist* p)
{
	pa_proplist_clear(p);
	pa_array_clear(&p->array);
	free(p);
}

static inline unsigned pa_proplist_size(const pa_proplist *p)
{
	return pa_array_get_len(&p->array, pa_proplist_item);
}

static inline int pa_proplist_contains(const pa_proplist *p, const char *key)
{
	return pa_proplist_item_find(p, key) ? 1 : 0;
}

static inline int pa_proplist_sets(pa_proplist *p, const char *key, const char *value)
{
	pa_proplist_item *item = pa_proplist_item_find(p, key);
	if (item != NULL)
		pa_proplist_item_free(item);
        else
                item = pa_array_add(&p->array, sizeof(*item));
	item->key = strdup(key);
	item->value = strdup(value);
	return 0;
}

static inline int pa_proplist_unset(pa_proplist *p, const char *key)
{
	pa_proplist_item *item = pa_proplist_item_find(p, key);
	if (item == NULL)
		return -ENOENT;
	pa_proplist_item_free(item);
	pa_array_remove(&p->array, item);
	return 0;
}

static PA_PRINTF_FUNC(3,4) inline int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...)
{
	pa_proplist_item *item = pa_proplist_item_find(p, key);
	va_list args;
	int res;

	va_start(args, format);
	if (item != NULL)
		pa_proplist_item_free(item);
        else
                item = pa_array_add(&p->array, sizeof(*item));
	item->key = strdup(key);
	if ((res = vasprintf(&item->value, format, args)) < 0)
		res = -errno;
	va_end(args);
	return res;
}

static inline const char *pa_proplist_gets(const pa_proplist *p, const char *key)
{
	pa_proplist_item *item = pa_proplist_item_find(p, key);
	return item ? item->value : NULL;
}

typedef enum pa_update_mode {
    PA_UPDATE_SET
    /**< Replace the entire property list with the new one. Don't keep
     *  any of the old data around. */,
    PA_UPDATE_MERGE
    /**< Merge new property list into the existing one, not replacing
     *  any old entries if they share a common key with the new
     *  property list. */,
    PA_UPDATE_REPLACE
    /**< Merge new property list into the existing one, replacing all
     *  old entries that share a common key with the new property
     *  list. */
} pa_update_mode_t;


static inline void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other)
{
	pa_proplist_item *item;

	if (mode == PA_UPDATE_SET)
		pa_proplist_clear(p);

	pa_array_for_each(item, &other->array) {
		if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, item->key))
			continue;
		pa_proplist_sets(p, item->key, item->value);
	}
}

static inline pa_proplist* pa_proplist_new_dict(const struct acp_dict *dict)
{
	pa_proplist *p = pa_proplist_new();
	if (dict) {
		const struct acp_dict_item *item;
		struct acp_dict_item *it;
		acp_dict_for_each(item, dict) {
			it = pa_array_add(&p->array, sizeof(*it));
			it->key = strdup(item->key);
			it->value = strdup(item->value);
		}
	}
	return p;
}

static inline void pa_proplist_as_dict(const pa_proplist *p, struct acp_dict *dict)
{
	*dict = ACP_DICT_INIT(p->array.data, pa_proplist_size(p));
}

#ifdef __cplusplus
}  /* extern "C" */
#endif

#endif /* PA_PROPLIST_H */
