

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "ice_streamer.h"
#include "frost_list.h"

/** @file */

/* Default source preferences for accessing "propdicts" */
const char *default_source_pref[] = {
    "server",
    "client/*",
    "plugin/playlist",
    "plugin/id3v2",
    "plugin/segment",
    "plugin/*",
    "*",
    NULL
};



/*************************************XLIST*******************************/
typedef int (*XCompareFunc) (const void *a, const void *b);
typedef int (*XCompareDataFunc) (const void *a, const void *b, void *user_data);
typedef int (*XEqualFunc) (const void *a, const void *b);
typedef int (*XFunc) (void *data, void *user_data);
typedef unsigned int (*XHashFunc) (const void *key);
typedef void (*XHFunc) (const void *key, const void *value, void *user_data);
typedef int  (*XHRFunc) (void *key, void *value, void *user_data);
typedef void (*XDestroyNotify) (void *data);

static inline void
x_print_err (const char *func, const char *msg)
{
    fprintf (stderr, " ******\n");
    fprintf (stderr, " * %s was called %s\n", func, msg);
    fprintf (stderr, " * This is probably an error in the application using libclient\n");
    fprintf (stderr, " ******\n");
}

static inline void
x_print_internal_err (const char *func, const char *msg)
{
    fprintf (stderr, " ******\n");
    fprintf (stderr, " * %s raised a fatal error: %s\n", func, msg);
    fprintf (stderr, " * This is probably a bug in ICE STREAMER\n");
    fprintf (stderr, " ******\n");
}

#define x_check_conn(c, retval) do { x_api_error_if (!c, "with a NULL connection", retval); x_api_error_if (!c->ipc, "with a connection that isn't connected", retval);} while (0)

#define x_api_warning(msg) do { x_print_err (__FUNCTION__, msg); } while(0)
#define x_api_warning_if(cond, msg) do { if (cond) { x_print_err (__FUNCTION__, msg); } } while(0)
#define x_api_error(msg, retval) do { x_print_err (__FUNCTION__, msg); return retval; } while(0)
#define x_api_error_if(cond, msg, retval) do { if (cond) { x_print_err (__FUNCTION__, msg); return retval;} } while(0)
#define x_internal_error(msg) do { x_print_internal_err (__FUNCTION__, msg); } while(0)

#define _x_list_alloc x_list_alloc
x_list_t *
x_list_alloc (void)
{
    x_list_t *list;

    list = (x_list_t *)calloc (1, sizeof (x_list_t));

    return list;
}

void
x_list_free (x_list_t *list)
{
    x_list_t *last;

    while (list) {
        last = list;
        list = list->next;
        free (last);
    }
}

#define _x_list_free_1 x_list_free_1
void
x_list_free_1 (x_list_t *list)
{
     free (list);
}

x_list_t*
x_list_last (x_list_t *list)
{
    if (list) {
        while (list->next)
            list = list->next;
    }

    return list;
}

x_list_t*
x_list_append (x_list_t *list, void *data)
{
    x_list_t *new_list;
    x_list_t *last;

    new_list = _x_list_alloc ();
    new_list->data = data;

    if (list) {
        last = x_list_last (list);
        /* g_assert (last != NULL); */
        last->next = new_list;
        new_list->prev = last;

        return list;
    } else {
        return new_list;
    }
}

x_list_t*
x_list_prepend (x_list_t *list, void *data)
{
    x_list_t *new_list;

    new_list = _x_list_alloc ();
    new_list->data = data;

    if (list) {
        if (list->prev) {
            list->prev->next = new_list;
            new_list->prev = list->prev;
        }
        list->prev = new_list;
        new_list->next = list;
    }

    return new_list;
}

x_list_t*
x_list_nth (x_list_t *list, unsigned int n)
{
    while ((n-- > 0) && list)
        list = list->next;

    return list;
}

x_list_t*
x_list_insert (x_list_t *list, void *data, int position)
{
    x_list_t *new_list;
    x_list_t *tmp_list;

    if (position < 0) {
        return x_list_append (list, data);
    } else if (position == 0) {
        return x_list_prepend (list, data);
    }

    tmp_list = x_list_nth (list, position);
    if (!tmp_list)
        return x_list_append (list, data);

    new_list = _x_list_alloc ();
    new_list->data = data;

    if (tmp_list->prev) {
        tmp_list->prev->next = new_list;
        new_list->prev = tmp_list->prev;
    }
    new_list->next = tmp_list;
    tmp_list->prev = new_list;

    if (tmp_list == list) {
        return new_list;
    } else {
        return list;
    }
}

x_list_t*
x_list_insert_before (x_list_t *list, x_list_t *sibling, void *data)
{
    if (!list) {
        list = x_list_alloc ();
        list->data = data;
        assert (sibling);
        return list;
    } else if (sibling) {
        x_list_t *node;

        node = x_list_alloc ();
        node->data = data;
        if (sibling->prev) {
            node->prev = sibling->prev;
            node->prev->next = node;
            node->next = sibling;
            sibling->prev = node;
            return list;
        } else {
            node->next = sibling;
            sibling->prev = node;
            assert (sibling);
            return node;
        }
    } else {
        x_list_t *last;

        last = list;
        while (last->next) {
            last = last->next;
        }

        last->next = x_list_alloc ();
        last->next->data = data;
        last->next->prev = last;

        return list;
    }
}

x_list_t *
x_list_concat (x_list_t *list1, x_list_t *list2)
{
    x_list_t *tmp_list;

    if (list2) {
        tmp_list = x_list_last (list1);
        if (tmp_list) {
            tmp_list->next = list2;
        } else {
            list1 = list2;
        }
        list2->prev = tmp_list;
    }

    return list1;
}

x_list_t*
x_list_remove (x_list_t *list, const void *data)
{
    x_list_t *tmp;

    tmp = list;
    while (tmp) {
        if (tmp->data != data) {
            tmp = tmp->next;
        } else {
            if (tmp->prev)
                tmp->prev->next = tmp->next;
            if (tmp->next)
                tmp->next->prev = tmp->prev;

            if (list == tmp)
                list = list->next;

            _x_list_free_1 (tmp);

            break;
        }
    }
    return list;
}

x_list_t*
x_list_remove_all (x_list_t *list, const void * data)
{
    x_list_t *tmp = list;

    while (tmp) {
        if (tmp->data != data) {
            tmp = tmp->next;
        } else {
            x_list_t *next = tmp->next;

            if (tmp->prev) {
                tmp->prev->next = next;
            } else {
                list = next;
            }
            if (next) {
                next->prev = tmp->prev;
            }

            _x_list_free_1 (tmp);
            tmp = next;
        }
    }
    return list;
}

static inline x_list_t*
_x_list_remove_link (x_list_t *list, x_list_t *link)
{
    if (link) {
        if (link->prev)
            link->prev->next = link->next;
        if (link->next)
            link->next->prev = link->prev;

        if (link == list)
            list = list->next;

        link->next = NULL;
        link->prev = NULL;
    }

    return list;
}

x_list_t*
x_list_remove_link (x_list_t *list, x_list_t *link)
{
    return _x_list_remove_link (list, link);
}

x_list_t*
x_list_delete_link (x_list_t *list, x_list_t *link)
{
    list = _x_list_remove_link (list, link);
    _x_list_free_1 (link);

    return list;
}

x_list_t*
x_list_copy (x_list_t *list)
{
    x_list_t *new_list = NULL;

    if (list) {
        x_list_t *last;

        new_list = _x_list_alloc ();
        new_list->data = list->data;
        last = new_list;
        list = list->next;
        while (list) {
            last->next = _x_list_alloc ();
            last->next->prev = last;
            last = last->next;
            last->data = list->data;
            list = list->next;
        }
    }

    return new_list;
}

x_list_t*
x_list_reverse (x_list_t *list)
{
    x_list_t *last;

    last = NULL;
    while (list) {
        last = list;
        list = last->next;
        last->next = last->prev;
        last->prev = list;
    }

    return last;
}


x_list_t*
x_list_nth_prev (x_list_t *list, unsigned int n)
{
    while ((n-- > 0) && list)
        list = list->prev;

    return list;
}

void *
x_list_nth_data (x_list_t *list, unsigned int n)
{
    while ((n-- > 0) && list)
        list = list->next;

    return list ? list->data : NULL;
}

x_list_t*
x_list_find (x_list_t *list, const void *data)
{
    while (list) {
        if (list->data == data)
            break;
        list = list->next;
    }

    return list;
}

x_list_t*
x_list_find_custom (x_list_t *list, const void *data, XCompareFunc func)
{
    assert (func != NULL);

    while (list) {
        if (! func (list->data, data))
            return list;
        list = list->next;
    }

    return NULL;
}


int
x_list_position (x_list_t *list, x_list_t *link)
{
    int i;

    i = 0;
    while (list) {
        if (list == link)
            return i;
        i++;
        list = list->next;
    }

    return -1;
}

int
x_list_index (x_list_t *list, const void *data)
{
    int i;

    i = 0;
    while (list) {
        if (list->data == data)
            return i;
        i++;
        list = list->next;
    }

    return -1;
}


x_list_t*
x_list_first (x_list_t *list)
{
    if (list) {
        while (list->prev)
            list = list->prev;
    }

    return list;
}

unsigned int
x_list_length (x_list_t *list)
{
    unsigned int length;

    length = 0;
    while (list) {
        length++;
        list = list->next;
    }

    return length;
}

void
x_list_foreach (x_list_t *list, XFunc func, void *user_data)
{
    while (list) {
        x_list_t *next = list->next;
        (*func) (list->data, user_data);
        list = next;
    }
}


x_list_t*
x_list_insert_sorted (x_list_t *list, void *data, XCompareFunc func)
{
    x_list_t *tmp_list = list;
    x_list_t *new_list;
    int cmp;

    assert (func != NULL);

    if (!list) {
        new_list = _x_list_alloc ();
        new_list->data = data;
        return new_list;
    }

    cmp = (*func) (data, tmp_list->data);

    while ((tmp_list->next) && (cmp > 0)) {
        tmp_list = tmp_list->next;
        cmp = (*func) (data, tmp_list->data);
    }

    new_list = _x_list_alloc ();
    new_list->data = data;

    if ((!tmp_list->next) && (cmp > 0)) {
        tmp_list->next = new_list;
        new_list->prev = tmp_list;
        return list;
    }

    if (tmp_list->prev) {
        tmp_list->prev->next = new_list;
        new_list->prev = tmp_list->prev;
    }
    new_list->next = tmp_list;
    tmp_list->prev = new_list;

    if (tmp_list == list)
        return new_list;
    else
        return list;
}

/*************************************************************************/

struct ice_val_St {
    union {
        char *error;
        int32_t int32;
        char *string;
        ice_val_coll_t *coll;
        ice_val_bin_t bin;
        ice_val_list_t *list;
        ice_val_dict_t *dict;

        struct {
            bool ro;
            unsigned char *buf;
            int alloclen; /* in bits */
            int len; /* in bits */
            int pos; /* in bits */
        } bit;
    } value;
    ice_val_type_t type;

    int ref;  /* refcounting */
};

/**
 * Allocates a new empty #ice_val_t.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_none (void)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_NONE);
    return val;
}

/**
 * Allocates a new error #ice_val_t.
 * @param s The error message to store in the #ice_val_t. The
 * string is copied in the value.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_error (const char *errstr)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_ERROR);

    if (val) {
        val->value.error = strdup (errstr);
    }

    return val;
}

/**
 * Allocates a new integer #ice_val_t.
 * @param i The value to store in the #ice_val_t.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_int (int32_t i)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_INT32);

    if (val) {
        val->value.int32 = i;
    }

    return val;
}

/**
 * Allocates a new string #ice_val_t.
 * @param s The value to store in the #ice_val_t. The string is
 * copied in the value.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_string (const char *s)
{
    ice_val_t *val;

    frost_return_val_if_fail (s, NULL);
    frost_return_val_if_fail (ice_val_utf8_validate (s), NULL);

    val = ice_val_new (ICE_VAL_TYPE_STRING);
    if (val) {
        val->value.string = strdup (s);
    }

    return val;
}

/**
 * Allocates a new collection #ice_val_t.
 * @param s The value to store in the #ice_val_t.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_coll (ice_val_coll_t *c)
{
    ice_val_t *val;

    frost_return_val_if_fail (c, NULL);

    val = ice_val_new (ICE_VAL_TYPE_COLL);
    if (val) {
        val->value.coll = c;
        ice_val_coll_ref (c);
    }

    return val;
}

/**
 * Allocates a new binary data #ice_val_t.
 * @param data The data to store in the #ice_val_t.
 * @param len The size of the data.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_bin (const unsigned char *data, unsigned int len)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_BIN);

    if (val) {
        /* copy the data! */
        val->value.bin.data = (unsigned char *)x_malloc (len);
        if (!val->value.bin.data) {
            free (val);
            x_oom ();
            return NULL;
        }
        memcpy (val->value.bin.data, data, len);
        val->value.bin.len = len;
    }

    return val;
}

/**
 * Allocates a new list #ice_val_t.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_list (void)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_LIST);

    if (val) {
        val->value.list = ice_val_list_new ();
        val->value.list->parent_value = val;
    }

    return val;
}

/**
 * Allocates a new dict #ice_val_t.
 * @return The new #ice_val_t. Must be unreferenced with
 * #ice_val_unref.
 */
ice_val_t *
ice_val_new_dict (void)
{
    ice_val_t *val = ice_val_new (ICE_VAL_TYPE_DICT);

    if (val) {
        val->value.dict = ice_val_dict_new ();
    }

    return val;
}



/**
 * References the #ice_val_t
 *
 * @param val the value to reference.
 * @return val
 */
ice_val_t *
ice_val_ref (ice_val_t *val)
{
    frost_return_val_if_fail (val, NULL);
    val->ref++;

    return val;
}

/**
 * Decreases the references for the #ice_val_t
 * When the number of references reaches 0 it will
 * be freed. And thus all data you extracted from it
 * will be deallocated.
 */
void
ice_val_unref (ice_val_t *val)
{
    frost_return_if_fail (val);
    x_api_error_if (val->ref < 1, "with a freed value",);

    val->ref--;
    if (val->ref == 0) {
        ice_val_free (val);
    }
}


/**
 * Allocates new #ice_val_t and references it.
 * @internal
 */
ice_val_t *
ice_val_new (ice_val_type_t type)
{
    ice_val_t *val;

    val = (ice_val_t *)x_new0 (ice_val_t, 1);
    if (!val) {
        x_oom ();
        return NULL;
    }

    val->type = type;

    return ice_val_ref (val);
}

/**
 * Free a #ice_val_t along with its internal data.
 * @internal
 */
void
ice_val_free (ice_val_t *val)
{
    frost_return_if_fail (val);

    switch (val->type) {
        case ICE_VAL_TYPE_NONE :
        case ICE_VAL_TYPE_END :
        case ICE_VAL_TYPE_INT32 :
            break;
        case ICE_VAL_TYPE_ERROR :
            free (val->value.error);
            val->value.error = NULL;
            break;
        case ICE_VAL_TYPE_STRING :
            free (val->value.string);
            val->value.string = NULL;
            break;
        case ICE_VAL_TYPE_COLL:
            ice_val_coll_unref (val->value.coll);
            val->value.coll = NULL;
            break;
        case ICE_VAL_TYPE_BIN :
            free (val->value.bin.data);
            val->value.bin.len = 0;
            break;
        case ICE_VAL_TYPE_LIST:
            ice_val_list_free (val->value.list);
            val->value.list = NULL;
            break;
        case ICE_VAL_TYPE_DICT:
            ice_val_dict_free (val->value.dict);
            val->value.dict = NULL;
            break;
        case ICE_VAL_TYPE_BITBUFFER:
            if (!val->value.bit.ro && val->value.bit.buf) {
                free (val->value.bit.buf);
            }
            val->value.bit.buf = NULL;
            break;
    }

    free (val);
}


/**
 * Get the type of the value.
 *
 * @param val a #ice_val_t to get the type from.
 * @returns The data type in the value.
 */
ice_val_type_t
ice_val_get_type (const ice_val_t *val)
{
    x_api_error_if (!val, "NULL value",
                    ICE_VAL_TYPE_NONE);

    return val->type;
}

/**
 * Check if value is of specified type.
 *
 * @param val #ice_val_t to check.
 * @param t #ice_val_type_t to check for.
 * @return 1 if value is of specified type, 0 otherwise.
 */
int
ice_val_is_type (const ice_val_t *val, ice_val_type_t t)
{
    x_api_error_if (!val, "NULL value", 0);

    return (ice_val_get_type (val) == t);
}


/* Merely legacy aliases */

/**
 * Check if the value stores an error.
 *
 * @param val a #ice_val_t
 * @return 1 if error was encountered, 0 otherwise.
 */
int
ice_val_is_error (const ice_val_t *val)
{
    return ice_val_is_type (val, ICE_VAL_TYPE_ERROR);
}

/**
 * Check if the value stores a list.
 *
 * @param val a #ice_val_t
 * @return 1 if value stores a list, 0 otherwise.
 */
int
ice_val_is_list (const ice_val_t *val)
{
    return ice_val_is_type (val, ICE_VAL_TYPE_LIST);
}

/**
 * Check if the value stores a dict.
 *
 * @param val a #ice_val_t
 * @return 1 if value stores a dict, 0 otherwise.
 */
int
ice_val_is_dict (const ice_val_t *val)
{
    return ice_val_is_type (val, ICE_VAL_TYPE_DICT);
}

/**
 * Legacy alias to retrieve the error string from an
 * #ice_val_t. Obsolete now, use #ice_val_get_error instead!
 *
 * @param val an error #ice_val_t
 * @return the error string if valid, NULL otherwise.
 */
const char *
ice_val_get_error_old (const ice_val_t *val)
{
    if (!val || val->type != ICE_VAL_TYPE_ERROR) {
        return NULL;
    }

    return val->value.error;
}

/**
 * Helper function to build a list #ice_val_t containing the
 * strings from the input array.
 *
 * @param array An array of C strings. Must be NULL-terminated if num
 *              is -1.
 * @param num The optional number of elements to read from the array. Set to
 *            -1 if the array is NULL-terminated.
 * @return An #ice_val_t containing the list of strings. Must be
 *         unreffed manually when done.
 */
ice_val_t *
ice_val_make_stringlist (char *array[], int num)
{
    ice_val_t *list, *elem;
    int i;

    list = ice_val_new_list ();
    if (array) {
        for (i = 0; (num >= 0 && i < num) || array[i]; i++) {
            elem = ice_val_new_string (array[i]);
            ice_val_list_append (list, elem);
            ice_val_unref (elem);
        }
    }

    return list;
}

/**
 * Gets the type of a dict entry.
 *
 * @param val A ice_val_t containing a dict.
 * @param key The key in the dict.
 * @return The type of the entry or #ICE_VAL_TYPE_NONE if something goes wrong.
 */
ice_val_type_t
ice_val_dict_entry_get_type (ice_val_t *val, const char *key)
{
    ice_val_t *v;

    if (!ice_val_dict_get (val, key, &v)) {
        return ICE_VAL_TYPE_NONE;
    }

    return ice_val_get_type (v);
}


/* macro-magically define dict extractors */
#define GEN_DICT_EXTRACTOR_FUNC(typename, type)         \
    int                             \
    ice_val_dict_entry_get_##typename (ice_val_t *val, const char *key, \
                                     type *r)           \
    {                               \
        ice_val_t *v;                     \
        if (!ice_val_dict_get (val, key, &v)) {           \
            return 0;                   \
        }                           \
        return ice_val_get_##typename (v, r);         \
    }

GEN_DICT_EXTRACTOR_FUNC (string, const char *)
GEN_DICT_EXTRACTOR_FUNC (int, int32_t)
GEN_DICT_EXTRACTOR_FUNC (coll, ice_val_coll_t *)

/* macro-magically define dict set functions */
#define GEN_DICT_SET_FUNC(typename, type) \
    int \
    ice_val_dict_set_##typename (ice_val_t *dict, const char *key, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_dict_set (dict, key, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_DICT_SET_FUNC (string, const char *)
GEN_DICT_SET_FUNC (int, int32_t)
GEN_DICT_SET_FUNC (coll, ice_val_coll_t *)

/* macro-magically define dict_iter extractors */
#define GEN_DICT_ITER_EXTRACTOR_FUNC(typename, type) \
    int \
    ice_val_dict_iter_pair_##typename (ice_val_dict_iter_t *it, \
                                     const char **key, \
                                     type *r) \
    { \
        ice_val_t *v; \
        if (!ice_val_dict_iter_pair (it, key, &v)) { \
            return 0; \
        } \
        if (r) { \
            return ice_val_get_##typename (v, r); \
        } else { \
            return 1; \
        } \
    }

GEN_DICT_ITER_EXTRACTOR_FUNC (string, const char *)
GEN_DICT_ITER_EXTRACTOR_FUNC (int, int32_t)
GEN_DICT_ITER_EXTRACTOR_FUNC (coll, ice_val_coll_t *)

/* macro-magically define dict_iter set functions */
#define GEN_DICT_ITER_SET_FUNC(typename, type) \
    int \
    ice_val_dict_iter_set_##typename (ice_val_dict_iter_t *it, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_dict_iter_set (it, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_DICT_ITER_SET_FUNC (string, const char *)
GEN_DICT_ITER_SET_FUNC (int, int32_t)
GEN_DICT_ITER_SET_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list extractors */
#define GEN_LIST_EXTRACTOR_FUNC(typename, type) \
    int \
    ice_val_list_get_##typename (ice_val_t *val, int pos, type *r) \
    { \
        ice_val_t *v; \
        if (!ice_val_list_get (val, pos, &v)) { \
            return 0; \
        } \
        return ice_val_get_##typename (v, r); \
    }

GEN_LIST_EXTRACTOR_FUNC (string, const char *)
GEN_LIST_EXTRACTOR_FUNC (int, int32_t)
GEN_LIST_EXTRACTOR_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list set functions */
#define GEN_LIST_SET_FUNC(typename, type) \
    int \
    ice_val_list_set_##typename (ice_val_t *list, int pos, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_list_set (list, pos, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_LIST_SET_FUNC (string, const char *)
GEN_LIST_SET_FUNC (int, int32_t)
GEN_LIST_SET_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list insert functions */
#define GEN_LIST_INSERT_FUNC(typename, type) \
    int \
    ice_val_list_insert_##typename (ice_val_t *list, int pos, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_list_insert (list, pos, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_LIST_INSERT_FUNC (string, const char *)
GEN_LIST_INSERT_FUNC (int, int32_t)
GEN_LIST_INSERT_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list append functions */
#define GEN_LIST_APPEND_FUNC(typename, type) \
    int \
    ice_val_list_append_##typename (ice_val_t *list, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_list_append (list, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_LIST_APPEND_FUNC (string, const char *)
GEN_LIST_APPEND_FUNC (int, int32_t)
GEN_LIST_APPEND_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list_iter extractors */
#define GEN_LIST_ITER_EXTRACTOR_FUNC(typename, type) \
    int \
    ice_val_list_iter_entry_##typename (ice_val_list_iter_t *it, type *r) \
    { \
        ice_val_t *v; \
        if (!ice_val_list_iter_entry (it, &v)) { \
            return 0; \
        } \
        return ice_val_get_##typename (v, r); \
    }

GEN_LIST_ITER_EXTRACTOR_FUNC (string, const char *)
GEN_LIST_ITER_EXTRACTOR_FUNC (int, int32_t)
GEN_LIST_ITER_EXTRACTOR_FUNC (coll, ice_val_coll_t *)

/* macro-magically define list_iter insert functions */
#define GEN_LIST_ITER_INSERT_FUNC(typename, type) \
    int \
    ice_val_list_iter_insert_##typename (ice_val_list_iter_t *it, type elem) \
    { \
        int ret; \
        ice_val_t *v; \
 \
        v = ice_val_new_##typename (elem); \
        ret = ice_val_list_iter_insert (it, v); \
        ice_val_unref (v); \
 \
        return ret; \
    }

GEN_LIST_ITER_INSERT_FUNC (string, const char *)
GEN_LIST_ITER_INSERT_FUNC (int, int32_t)
GEN_LIST_ITER_INSERT_FUNC (coll, ice_val_coll_t *)

static int
source_match_pattern (const char *source, const char *pattern)
{
    int match = 0;
    int lpos = strlen (pattern) - 1;

    if (strcasecmp (pattern, source) == 0) {
        match = 1;
    } else if (lpos >= 0 && pattern[lpos] == '*' &&
               (lpos == 0 || strncasecmp (source, pattern, lpos) == 0)) {
        match = 1;
    }

    return match;
}

/* Return the index of the source in the source prefs list, or -1 if
 * no match.
 */
static int
find_match_index (const char *source, const char **src_prefs)
{
    int i, match = -1;

    for (i = 0; src_prefs[i]; i++) {
        if (source_match_pattern (source, src_prefs[i])) {
            match = i;
            break;
        }
    }

    return match;
}

/**
 * Helper function to transform a key-source-value dict-of-dict
 * #ice_val_t (formerly a propdict) to a regular key-value dict, given a
 * list of source preference.
 *
 * @param propdict A key-source-value dict-of-dict #ice_val_t.
 * @param src_prefs A list of source names or patterns. Must be
 *                  NULL-terminated. If this argument is NULL, the
 *                  default source preferences is used.
 * @return An #ice_val_t containing a simple key-value dict. Must be
 *         unreffed manually when done.
 */
ice_val_t *
ice_val_propdict_to_dict (ice_val_t *propdict, const char **src_prefs)
{
    ice_val_t *dict, *source_dict, *value, *best_value;
    ice_val_dict_iter_t *key_it, *source_it;
    const char *key, *source;
    const char **local_prefs;
    int match_index, best_index;

    dict = ice_val_new_dict ();

    local_prefs = src_prefs ? src_prefs : default_source_pref;

    ice_val_get_dict_iter (propdict, &key_it);
    while (ice_val_dict_iter_valid (key_it)) {
        ice_val_dict_iter_pair (key_it, &key, &source_dict);

        best_value = NULL;
        best_index = -1;
        ice_val_get_dict_iter (source_dict, &source_it);
        while (ice_val_dict_iter_valid (source_it)) {
            ice_val_dict_iter_pair (source_it, &source, &value);
            match_index = find_match_index (source, local_prefs);
            /* keep first match or better match */
            if (match_index >= 0 && (best_index < 0 ||
                                     match_index < best_index)) {
                best_value = value;
                best_index = match_index;
            }
            ice_val_dict_iter_next (source_it);
        }

        /* Note: we do not insert a key-value pair if no source matches */
        if (best_value) {
            ice_val_dict_set (dict, key, best_value);
        }

        ice_val_dict_iter_next (key_it);
    }

    return dict;
}


/**
 * Retrieves an error string describing the server error from the
 * value.
 *
 * @param val a #ice_val_t containing a integer.
 * @param r the return error.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_error (const ice_val_t *val, const char **r)
{
    if (!val || val->type != ICE_VAL_TYPE_ERROR) {
        return 0;
    }

    *r = val->value.error;

    return 1;
}

/**
 * Retrieves a signed integer from the value.
 *
 * @param val a #ice_val_t containing an integer.
 * @param r the return integer.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_int (const ice_val_t *val, int32_t *r)
{
    if (!val || val->type != ICE_VAL_TYPE_INT32) {
        return 0;
    }

    *r = val->value.int32;

    return 1;
}

/**
 * Retrieves a unsigned integer from the value.
 *
 * @param val a #ice_val_t containing an unsigned integer.
 * @param r the return unsigned integer.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_uint (const ice_val_t *val, uint32_t *r)
{
    if (!val)
        return 0;
    if (val->type != ICE_VAL_TYPE_INT32)
        return 0;

    *r = val->value.int32;

    return 1;
}

/**
 * Retrieves a string from the value.
 *
 * @param val a #ice_val_t containing a string.
 * @param r the return string. This string is owned by the value and
 * will be freed when the value is freed.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_string (const ice_val_t *val, const char **r)
{
    if (!val || val->type != ICE_VAL_TYPE_STRING) {
        return 0;
    }

    *r = val->value.string;

    return 1;
}

/**
 * Retrieves a collection from the value.
 *
 * @param val a #ice_val_t containing a collection.
 * @param c the return collection. This collection is owned by the
 * value and will be unref'd when the value is freed.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_coll (const ice_val_t *val, ice_val_coll_t **c)
{
    if (!val || val->type != ICE_VAL_TYPE_COLL) {
        return 0;
    }

    *c = val->value.coll;

    return 1;
}

/**
 * Retrieves binary data from the value.
 *
 * @param val a #ice_val_t containing a string.
 * @param r the return data. This data is owned by the value and will
 * be freed when the value is freed.
 * @param rlen the return length of data.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_bin (const ice_val_t *val, const unsigned char **r, unsigned int *rlen)
{
    if (!val || val->type != ICE_VAL_TYPE_BIN) {
        return 0;
    }

    *r = val->value.bin.data;
    *rlen = val->value.bin.len;

    return 1;
}


/**
 * Retrieves a list iterator from a list #ice_val_t.
 *
 * @param val a #ice_val_t containing a list.
 * @param it An #ice_val_list_iter_t that can be used to access the list
 *           data. The iterator will be freed when the value is freed.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_list_iter (const ice_val_t *val, ice_val_list_iter_t **it)
{
    ice_val_list_iter_t *new_it;

    if (!val || val->type != ICE_VAL_TYPE_LIST) {
        *it = NULL;
        return 0;
    }

    new_it = ice_val_list_iter_new (val->value.list);
    if (!new_it) {
        *it = NULL;
        return 0;
    }

    *it = new_it;

    return 1;
}

/**
 * Retrieves a dict iterator from a dict #ice_val_t.
 *
 * @param val a #ice_val_t containing a dict.
 * @param it An #ice_val_dict_iter_t that can be used to access the dict
 *           data. The iterator will be freed when the value is freed.
 * @return 1 upon success otherwise 0
 */
int
ice_val_get_dict_iter (const ice_val_t *val, ice_val_dict_iter_t **it)
{
    ice_val_dict_iter_t *new_it;

    if (!val || val->type != ICE_VAL_TYPE_DICT) {
        *it = NULL;
        return 0;
    }

    new_it = ice_val_dict_iter_new (val->value.dict);
    if (!new_it) {
        *it = NULL;
        return 0;
    }

    *it = new_it;

    return 1;
}


/* List stuff */

ice_val_list_t *
ice_val_list_new (void)
{
    ice_val_list_t *list;

    list = (ice_val_list_t *)x_new0 (ice_val_list_t, 1);
    if (!list) {
        x_oom ();
        return NULL;
    }

    /* list is all empty for now! */

    return list;
}

void
ice_val_list_free (ice_val_list_t *l)
{
    ice_val_list_iter_t *it;
    int i;

    /* free iterators */
    while (l->iterators) {
        it = (ice_val_list_iter_t *) l->iterators->data;
        ice_val_list_iter_free (it);
    }

    /* unref contents */
    for (i = 0; i < l->size; i++) {
        ice_val_unref (l->list[i]);
    }

    free (l->list);
    free (l);
}

int
ice_val_list_resize (ice_val_list_t *l, int newsize)
{
    ice_val_t **newmem;

    newmem = (ice_val_t **)realloc (l->list, newsize * sizeof (ice_val_t *));

    if (newsize != 0 && newmem == NULL) {
        x_oom ();
        return 0;
    }

    l->list = newmem;
    l->allocated = newsize;

    return 1;
}

int
_ice_val_list_insert (ice_val_list_t *l, int pos, ice_val_t *val)
{
    ice_val_list_iter_t *it;
    x_list_t *n;

    if (!absolutify_and_validate_pos (&pos, l->size, 1)) {
        return 0;
    }

    if (l->restricted) {
        frost_return_val_if_fail (ice_val_is_type (val, l->restricttype), 0);
    }

    /* We need more memory, reallocate */
    if (l->size == l->allocated) {
        int success;
        size_t double_size;
        if (l->allocated > 0) {
            double_size = l->allocated << 1;
        } else {
            double_size = 1;
        }
        success = ice_val_list_resize (l, double_size);
        frost_return_val_if_fail (success, 0);
    }

    /* move existing items out of the way */
    if (l->size > pos) {
        memmove (l->list + pos + 1, l->list + pos,
                 (l->size - pos) * sizeof (ice_val_t *));
    }

    l->list[pos] = ice_val_ref (val);
    l->size++;

    /* update iterators pos */
    for (n = l->iterators; n; n = n->next) {
        it = (ice_val_list_iter_t *) n->data;
        if (it->position > pos) {
            it->position++;
        }
    }

    return 1;
}

int
_ice_val_list_append (ice_val_list_t *l, ice_val_t *val)
{
    return _ice_val_list_insert (l, l->size, val);
}

int
_ice_val_list_remove (ice_val_list_t *l, int pos)
{
    ice_val_list_iter_t *it;
    int half_size;
    x_list_t *n;

    /* prevent removing after the last element */
    if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
        return 0;
    }

    ice_val_unref (l->list[pos]);

    l->size--;

    /* fill the gap */
    if (pos < l->size) {
        memmove (l->list + pos, l->list + pos + 1,
                 (l->size - pos) * sizeof (ice_val_t *));
    }

    /* Reduce memory usage by two if possible */
    half_size = l->allocated >> 1;
    if (l->size <= half_size) {
        int success;
        success = ice_val_list_resize (l, half_size);
        frost_return_val_if_fail (success, 0);
    }

    /* update iterator pos */
    for (n = l->iterators; n; n = n->next) {
        it = (ice_val_list_iter_t *) n->data;
        if (it->position > pos) {
            it->position--;
        }
    }

    return 1;
}

int
_ice_val_list_move (ice_val_list_t *l, int old_pos, int new_pos)
{
    ice_val_t *v;
    ice_val_list_iter_t *it;
    x_list_t *n;

    if (!absolutify_and_validate_pos (&old_pos, l->size, 0)) {
        return 0;
    }
    if (!absolutify_and_validate_pos (&new_pos, l->size, 0)) {
        return 0;
    }

    v = l->list[old_pos];
    if (old_pos < new_pos) {
        memmove (l->list + old_pos, l->list + old_pos + 1,
                 (new_pos - old_pos) * sizeof (ice_val_t *));
        l->list[new_pos] = v;

        /* update iterator pos */
        for (n = l->iterators; n; n = n->next) {
            it = (ice_val_list_iter_t *) n->data;
            if (it->position >= old_pos && it->position <= new_pos) {
                if (it->position == old_pos) {
                    it->position = new_pos;
                } else {
                    it->position--;
                }
            }
        }
    } else {
        memmove (l->list + new_pos + 1, l->list + new_pos,
                 (old_pos - new_pos) * sizeof (ice_val_t *));
        l->list[new_pos] = v;

        /* update iterator pos */
        for (n = l->iterators; n; n = n->next) {
            it = (ice_val_list_iter_t *) n->data;
            if (it->position >= new_pos && it->position <= old_pos) {
                if (it->position == old_pos) {
                    it->position = new_pos;
                } else {
                    it->position++;
                }
            }
        }
    }

    return 1;
}

void
_ice_val_list_clear (ice_val_list_t *l)
{
    ice_val_list_iter_t *it;
    x_list_t *n;
    int i;

    /* unref all stored values */
    for (i = 0; i < l->size; i++) {
        ice_val_unref (l->list[i]);
    }

    /* free list, declare empty */
    free (l->list);
    l->list = NULL;

    l->size = 0;
    l->allocated = 0;

    /* reset iterator pos */
    for (n = l->iterators; n; n = n->next) {
        it = (ice_val_list_iter_t *) n->data;
        it->position = 0;
    }
}

/**
 * Get the element at the given position in the list #ice_val_t. This
 * function does not increase the refcount of the element, the
 * reference is still owned by the list.
 *
 * @param listv A #ice_val_t containing a list.
 * @param pos The position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc).
 * @param val Pointer set to a borrowed reference to the element at
 *            the given position in the list.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_get (ice_val_t *listv, int pos, ice_val_t **val)
{
    ice_val_list_t *l;

    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    l = listv->value.list;

    /* prevent accessing after the last element */
    if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
        return 0;
    }

    if (val) {
        *val = l->list[pos];
    }

    return 1;
}

/**
 * Set the element at the given position in the list #ice_val_t.
 *
 * @param listv A #ice_val_t containing a list.
 * @param pos The position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc).
 * @param val The element to put at the given position in the list.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_set (ice_val_t *listv, int pos, ice_val_t *val)
{
    ice_val_t *old_val;
    ice_val_list_t *l;

    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (val, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    l = listv->value.list;

    if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
        return 0;
    }

    old_val = l->list[pos];
    l->list[pos] = ice_val_ref (val);
    ice_val_unref (old_val);

    return 1;
}

/**
 * Insert an element at the given position in the list #ice_val_t.
 * The list will hold a reference to the element until it's removed.
 *
 * @param listv A #ice_val_t containing a list.
 * @param pos The position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc).
 * @param val The element to insert.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_insert (ice_val_t *listv, int pos, ice_val_t *val)
{
    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);
    frost_return_val_if_fail (val, 0);

    return _ice_val_list_insert (listv->value.list, pos, val);
}

/**
 * Remove the element at the given position from the list #ice_val_t.
 *
 * @param listv A #ice_val_t containing a list.
 * @param pos The position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc).
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_remove (ice_val_t *listv, int pos)
{
    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    return _ice_val_list_remove (listv->value.list, pos);
}

/**
 * Move the element from position #old to position #new.
 *
 * #ice_val_list_iter_t's remain pointing at their element (which might or might
 * not be at a different position).
 *
 * @param listv A #ice_val_t containing a list
 * @param old The original position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc.)
 * @param new The new position in the list. If negative start counting from the
 *            end (-1 is the last element, etc.) For the sake of counting the
 *            element to be moved is still at its old position.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_move (ice_val_t *listv, int old_pos, int new_pos)
{
    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    return _ice_val_list_move (listv->value.list, old_pos, new_pos);
}

/**
 * Append an element to the end of the list #ice_val_t.
 * The list will hold a reference to the element until it's removed.
 *
 * @param listv A #ice_val_t containing a list.
 * @param val The element to append.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_append (ice_val_t *listv, ice_val_t *val)
{
    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);
    frost_return_val_if_fail (val, 0);

    return _ice_val_list_append (listv->value.list, val);
}

/**
 * Empty the list from all its elements.
 *
 * @param listv A #ice_val_t containing a list.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_clear (ice_val_t *listv)
{
    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    _ice_val_list_clear (listv->value.list);

    return 1;
}

/**
 * Apply a function to each element in the list, in sequential order.
 *
 * @param listv A #ice_val_t containing a list.
 * @param function The function to apply to each element.
 * @param user_data User data passed to the foreach function.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_foreach (ice_val_t *listv, ice_val_list_foreach_func func,
                    void* user_data)
{
    ice_val_list_iter_t *it;
    ice_val_t *v;

    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);
    frost_return_val_if_fail (ice_val_get_list_iter (listv, &it), 0);

    while (ice_val_list_iter_valid (it)) {
        ice_val_list_iter_entry (it, &v);
        func (v, user_data);
        ice_val_list_iter_next (it);
    }

    ice_val_list_iter_free (it);

    return 1;
}

/**
 * Return the size of the list.
 *
 * @param listv The #ice_val_t containing the list.
 * @return The size of the list, or -1 if listv is invalid.
 */
int
ice_val_list_get_size (ice_val_t *listv)
{
    frost_return_val_if_fail (listv, -1);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), -1);

    return listv->value.list->size;
}


int
ice_val_list_restrict_type (ice_val_t *listv, ice_val_type_t type)
{
    ice_val_list_iter_t *it;
    ice_val_t *v;

    frost_return_val_if_fail (listv, 0);
    frost_return_val_if_fail (ice_val_is_type (listv, ICE_VAL_TYPE_LIST), 0);

    frost_return_val_if_fail (!listv->value.list->restricted, 0);

    frost_return_val_if_fail (ice_val_get_list_iter (listv, &it), 0);
    while (ice_val_list_iter_valid (it)) {
        ice_val_list_iter_entry (it, &v);
        frost_return_val_if_fail (ice_val_is_type (v, type), 0);
        ice_val_list_iter_next (it);
    }

    ice_val_list_iter_free (it);

    listv->value.list->restricted = frost_true;
    listv->value.list->restricttype = type;

    return 1;
}


ice_val_list_iter_t *
ice_val_list_iter_new (ice_val_list_t *l)
{
    ice_val_list_iter_t *it;

    it = (ice_val_list_iter_t *)x_new0 (ice_val_list_iter_t, 1);
    if (!it) {
        x_oom ();
        return NULL;
    }

    it->parent = l;
    it->position = 0;

    /* register iterator into parent */
    l->iterators = x_list_prepend (l->iterators, it);

    return it;
}

void
ice_val_list_iter_free (ice_val_list_iter_t *it)
{
    /* unref iterator from list and free it */
    it->parent->iterators = x_list_remove (it->parent->iterators, it);
    free (it);
}

/**
 * Explicitly free list iterator.
 *
 * Immediately frees any resources used by this iterator. The iterator
 * is freed automatically when the list is freed, but this function is
 * useful when the list can be long lived.
 *
 * @param it iterator to free
 *
 */
void
ice_val_list_iter_explicit_destroy (ice_val_list_iter_t *it)
{
    ice_val_list_iter_free (it);
}

/**
 * Get the element currently pointed at by the iterator. This function
 * does not increase the refcount of the element, the reference is
 * still owned by the list. If iterator does not point on a valid
 * element ice_val_list_iter_entry returns 0 and leaves val untouched.
 *
 * @param it A #ice_val_list_iter_t.
 * @param val Pointer set to a borrowed reference to the element
 *            pointed at by the iterator.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_iter_entry (ice_val_list_iter_t *it, ice_val_t **val)
{
    if (!ice_val_list_iter_valid (it))
        return 0;

    *val = it->parent->list[it->position];

    return 1;
}

/**
 * Check whether the iterator is valid and points to a valid element.
 *
 * @param it A #ice_val_list_iter_t.
 * @return 1 if the iterator is valid, 0 otherwise
 */
int
ice_val_list_iter_valid (ice_val_list_iter_t *it)
{
    return it && (it->position < it->parent->size) && (it->position >= 0);
}

/**
 * Rewind the iterator to the start of the list.
 *
 * @param it A #ice_val_list_iter_t.
 */
void
ice_val_list_iter_first (ice_val_list_iter_t *it)
{
    frost_return_if_fail (it);

    it->position = 0;
}

/**
 * Move the iterator to end of the list.
 *
 * @param listv A #ice_val_list_iter_t.
 */
void
ice_val_list_iter_last (ice_val_list_iter_t *it)
{
    frost_return_if_fail (it);

    if (it->parent->size > 0) {
        it->position = it->parent->size - 1;
    } else {
        it->position = it->parent->size;
    }
}

/**
 * Advance the iterator to the next element in the list.
 *
 * @param it A #ice_val_list_iter_t.
 */
void
ice_val_list_iter_next (ice_val_list_iter_t *it)
{
    frost_return_if_fail (it);

    if (it->position < it->parent->size) {
        it->position++;
    }
}

/**
 * Move the iterator to the previous element in the list.
 *
 * @param listv A #ice_val_list_iter_t.
 */
void
ice_val_list_iter_prev (ice_val_list_iter_t *it)
{
    frost_return_if_fail (it);

    if (it->position >= 0) {
        it->position--;
    }
}


/**
 * Move the iterator to the n-th element in the list.
 *
 * @param it A #ice_val_list_iter_t.
 * @param pos The position in the list. If negative, start counting
 *            from the end (-1 is the last element, etc).
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_iter_seek (ice_val_list_iter_t *it, int pos)
{
    frost_return_val_if_fail (it, 0);

    if (!absolutify_and_validate_pos (&pos, it->parent->size, 1)) {
        return 0;
    }
    it->position = pos;

    return 1;
}

/**
 * Tell the position of the iterator.
 *
 * @param it A #ice_val_list_iter_t.
 * @return The position of the iterator, or -1 if invalid.
 */
int
ice_val_list_iter_tell (const ice_val_list_iter_t *it)
{
    frost_return_val_if_fail (it, -1);

    return it->position;
}

/**
 * Return the parent #ice_val_t of an iterator.
 *
 * @param it A #ice_val_list_iter_t.
 * @return The parent #ice_val_t of the iterator, or NULL if invalid.
 */
ice_val_t*
ice_val_list_iter_get_parent (const ice_val_list_iter_t *it)
{
    frost_return_val_if_fail (it, NULL);

    return it->parent->parent_value;
}

/**
 * Insert an element in the list at the position pointed at by the
 * iterator.
 *
 * @param it A #ice_val_list_iter_t.
 * @param val The element to insert.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_iter_insert (ice_val_list_iter_t *it, ice_val_t *val)
{
    frost_return_val_if_fail (it, 0);
    frost_return_val_if_fail (val, 0);

    return _ice_val_list_insert (it->parent, it->position, val);
}

/**
 * Remove the element in the list at the position pointed at by the
 * iterator.
 *
 * @param it A #ice_val_list_iter_t.
 * @return 1 upon success otherwise 0
 */
int
ice_val_list_iter_remove (ice_val_list_iter_t *it)
{
    frost_return_val_if_fail (it, 0);

    return _ice_val_list_remove (it->parent, it->position);
}

/* Dict stuff */

struct ice_val_dict_St {
    /* dict implemented as a flat [key1, val1, key2, val2, ...] list */
    ice_val_list_t *flatlist;
    x_list_t *iterators;
};

struct ice_val_dict_iter_St {
    /* iterator of the contained flatlist */
    ice_val_list_iter_t *lit;
    ice_val_dict_t *parent;
};

 ice_val_dict_t *
ice_val_dict_new (void)
{
    ice_val_dict_t *dict;

    dict = (ice_val_dict_t *)x_new0 (ice_val_dict_t, 1);
    if (!dict) {
        x_oom ();
        return NULL;
    }

    dict->flatlist = ice_val_list_new ();

    return dict;
}

void
ice_val_dict_free (ice_val_dict_t *dict)
{
    ice_val_dict_iter_t *it;

    /* free iterators */
    while (dict->iterators) {
        it = (ice_val_dict_iter_t *) dict->iterators->data;
        ice_val_dict_iter_free (it);
    }

    ice_val_list_free (dict->flatlist);

    free (dict);
}

/**
 * Get the element corresponding to the given key in the dict #ice_val_t
 * (if it exists).  This function does not increase the refcount of
 * the element, the reference is still owned by the dict.
 *
 * @param dictv A #ice_val_t containing a dict.
 * @param key The key in the dict.
 * @param val Pointer set to a borrowed reference to the element
 *            corresponding to the given key in the dict.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_get (ice_val_t *dictv, const char *key, ice_val_t **val)
{
    ice_val_dict_iter_t *it;
    int ret = 1;

    frost_return_val_if_fail (key, 0);
    frost_return_val_if_fail (dictv, 0);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), 0);
    frost_return_val_if_fail (ice_val_get_dict_iter (dictv, &it), 0);

    if (!ice_val_dict_iter_find (it, key)) {
        ret = 0;
    }

    /* If found, return value and success */
    if (ret && val) {
        ice_val_dict_iter_pair (it, NULL, val);
    }

    ice_val_dict_iter_free (it);

    return ret;
}

/**
 * Insert an element under the given key in the dict #ice_val_t. If the
 * key already referenced an element, that element is unref'd and
 * replaced by the new one.
 *
 * @param dictv A #ice_val_t containing a dict.
 * @param key The key in the dict.
 * @param val The new element to insert in the dict.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_set (ice_val_t *dictv, const char *key, ice_val_t *val)
{
    ice_val_dict_iter_t *it;
    int ret;

    frost_return_val_if_fail (key, 0);
    frost_return_val_if_fail (val, 0);
    frost_return_val_if_fail (dictv, 0);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), 0);
    frost_return_val_if_fail (ice_val_get_dict_iter (dictv, &it), 0);

    /* if key already present, replace value */
    if (ice_val_dict_iter_find (it, key)) {
        ret = ice_val_dict_iter_set (it, val);

    /* else, insert a new key-value pair */
    } else {
        ice_val_t *keyval;

        keyval = ice_val_new_string (key);

        ret = ice_val_list_iter_insert (it->lit, keyval);
        if (ret) {
            ice_val_list_iter_next (it->lit);
            ret = ice_val_list_iter_insert (it->lit, val);
            if (!ret) {
                /* we added the key, but we couldn't add the value.
                 * we remove the key again to put the dictionary back
                 * in a consistent state.
                 */
                it->lit->position--;
                ice_val_list_iter_remove (it->lit);
            }
        }
        ice_val_unref (keyval);
    }

    ice_val_dict_iter_free (it);

    return ret;
}

/**
 * Remove the element corresponding to a given key in the dict
 * #ice_val_t (if it exists).
 *
 * @param dictv A #ice_val_t containing a dict.
 * @param key The key in the dict.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_remove (ice_val_t *dictv, const char *key)
{
    ice_val_dict_iter_t *it;
    int ret = 1;

    frost_return_val_if_fail (key, 0);
    frost_return_val_if_fail (dictv, 0);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), 0);
    frost_return_val_if_fail (ice_val_get_dict_iter (dictv, &it), 0);

    if (!ice_val_dict_iter_find (it, key)) {
        ret = 0;
    } else {
        ret = ice_val_list_iter_remove (it->lit) &&
              ice_val_list_iter_remove (it->lit);
        /* FIXME: cleanup if only the first fails */
    }

    ice_val_dict_iter_free (it);

    return ret;
}

/**
 * Empty the dict of all its elements.
 *
 * @param dictv A #ice_val_t containing a dict.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_clear (ice_val_t *dictv)
{
    frost_return_val_if_fail (dictv, 0);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), 0);

    _ice_val_list_clear (dictv->value.dict->flatlist);

    return 1;
}

/**
 * Apply a function to each key-element pair in the list. No
 * particular order is assumed.
 *
 * @param dictv A #ice_val_t containing a dict.
 * @param function The function to apply to each key-element pair.
 * @param user_data User data passed to the foreach function.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_foreach (ice_val_t *dictv, ice_val_dict_foreach_func func,
                    void *user_data)
{
    ice_val_dict_iter_t *it;
    const char *key;
    ice_val_t *v;

    frost_return_val_if_fail (dictv, 0);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), 0);
    frost_return_val_if_fail (ice_val_get_dict_iter (dictv, &it), 0);

    while (ice_val_dict_iter_valid (it)) {
        ice_val_dict_iter_pair (it, &key, &v);
        func (key, v, user_data);
        ice_val_dict_iter_next (it);
    }

    ice_val_dict_iter_free (it);

    return 1;
}

/**
 * Return the size of the dict.
 *
 * @param dictv The #ice_val_t containing the dict.
 * @return The size of the dict, or -1 if dict is invalid.
 */
int
ice_val_dict_get_size (ice_val_t *dictv)
{
    frost_return_val_if_fail (dictv, -1);
    frost_return_val_if_fail (ice_val_is_type (dictv, ICE_VAL_TYPE_DICT), -1);

    return dictv->value.dict->flatlist->size / 2;
}

ice_val_dict_iter_t *
ice_val_dict_iter_new (ice_val_dict_t *d)
{
    ice_val_dict_iter_t *it;

    it = (ice_val_dict_iter_t *)x_new0 (ice_val_dict_iter_t, 1);
    if (!it) {
        x_oom ();
        return NULL;
    }

    it->lit = ice_val_list_iter_new (d->flatlist);
    it->parent = d;

    /* register iterator into parent */
    d->iterators = x_list_prepend (d->iterators, it);

    return it;
}

void
ice_val_dict_iter_free (ice_val_dict_iter_t *it)
{
    /* we don't free the parent list iter, already managed by the flatlist */

    /* unref iterator from dict and free it */
    it->parent->iterators = x_list_remove (it->parent->iterators, it);
    free (it);
}

/**
 * Explicitly free dict iterator.
 *
 * Immediately frees any resources used by this iterator. The iterator
 * is freed automatically when the dict is freed, but this function is
 * useful when the dict can be long lived.
 *
 * @param it iterator to free
 *
 */
void
ice_val_dict_iter_explicit_destroy (ice_val_dict_iter_t *it)
{
    ice_val_dict_iter_free (it);
}

/**
 * Get the key-element pair currently pointed at by the iterator. This
 * function does not increase the refcount of the element, the
 * reference is still owned by the dict.
 *
 * @param it A #ice_val_dict_iter_t.
 * @param key Pointer set to the key pointed at by the iterator.
 * @param val Pointer set to a borrowed reference to the element
 *            pointed at by the iterator.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_iter_pair (ice_val_dict_iter_t *it, const char **key,
                      ice_val_t **val)
{
    unsigned int orig;
    ice_val_t *v;

    if (!ice_val_dict_iter_valid (it)) {
        return 0;
    }

    /* FIXME: avoid leaking abstraction! */
    orig = it->lit->position;

    if (key) {
        ice_val_list_iter_entry (it->lit, &v);
        ice_val_get_string (v, key);
    }

    if (val) {
        ice_val_list_iter_next (it->lit);
        ice_val_list_iter_entry (it->lit, val);
    }

    it->lit->position = orig;

    return 1;
}

/**
 * Check whether the iterator is valid and points to a valid pair.
 *
 * @param it A #ice_val_dict_iter_t.
 * @return 1 if the iterator is valid, 0 otherwise
 */
int
ice_val_dict_iter_valid (ice_val_dict_iter_t *it)
{
    return it && ice_val_list_iter_valid (it->lit);
}

/**
 * Rewind the iterator to the start of the dict.
 *
 * @param it A #ice_val_dict_iter_t.
 * @return 1 upon success otherwise 0
 */
void
ice_val_dict_iter_first (ice_val_dict_iter_t *it)
{
    frost_return_if_fail (it);

    ice_val_list_iter_first (it->lit);
}

/**
 * Advance the iterator to the next pair in the dict.
 *
 * @param it A #ice_val_dict_iter_t.
 * @return 1 upon success otherwise 0
 */
void
ice_val_dict_iter_next (ice_val_dict_iter_t *it)
{
    frost_return_if_fail (it);

    /* skip a pair */
    ice_val_list_iter_next (it->lit);
    ice_val_list_iter_next (it->lit);
}

/**
 * Move the iterator to the pair with the given key (if it exists)
 * or move it to the position where the key would have to be
 * put (if it doesn't exist yet).
 *
 * @param it A #ice_val_dict_iter_t.
 * @param key The key to seek for.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_iter_find (ice_val_dict_iter_t *it, const char *key)
{
    ice_val_t *val;
    const char *k;
    int s, dict_size, cmp, left, right;

    frost_return_val_if_fail (it, 0);
    frost_return_val_if_fail (key, 0);

    /* how many key-value pairs does this dictionary contain? */
    dict_size = it->parent->flatlist->size / 2;

    /* if it's empty, point the iterator at the beginning of
     * the list and report failure.
     */
    if (!dict_size) {
        ice_val_list_iter_seek (it->lit, 0);

        return 0;
    }

    /* perform binary search for the given key */
    left = 0;
    right = dict_size - 1;

    while (left <= right) {
        int mid = left + ((right - left) / 2);

        /* jump to the middle of the current search area */
        ice_val_list_iter_seek (it->lit, mid * 2);
        ice_val_list_iter_entry (it->lit, &val);

        /* get the key at this slot */
        s = ice_val_get_string (val, &k);
        frost_return_val_if_fail (s, 0);

        /* and compare it to the given key */
        cmp = strcmp (k, key);

        /* hooray, we found the key. */
        if (cmp == 0)
            return 1;

        /* go on searching the left or the right hand side. */
        if (cmp < 0) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    /* if we get down here, we failed to find the key
     * in the dictionary.
     * now, move the iterator so that it points to the slot
     * where the key would be inserted.
     */
    if (cmp < 0) {
        ice_val_list_iter_next (it->lit);
        ice_val_list_iter_next (it->lit);
    }

    return 0;
}

/**
 * Replace the element of the pair currently pointed to by the
 * iterator.
 *
 * @param it A #ice_val_dict_iter_t.
 * @param val The element to set in the pair.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_iter_set (ice_val_dict_iter_t *it, ice_val_t *val)
{
    unsigned int orig;
    int ret;

    frost_return_val_if_fail (ice_val_dict_iter_valid (it), 0);

    /* FIXME: avoid leaking abstraction! */
    orig = it->lit->position;

    ice_val_list_iter_next (it->lit);
    ice_val_list_iter_remove (it->lit);
    ret = ice_val_list_iter_insert (it->lit, val);
    /* FIXME: check remove success, swap operations? */

    it->lit->position = orig;

    return ret;
}

/**
 * Remove the pair in the dict pointed at by the iterator.
 *
 * @param it A #ice_val_dict_iter_t.
 * @return 1 upon success otherwise 0
 */
int
ice_val_dict_iter_remove (ice_val_dict_iter_t *it)
{
    int ret = 0;

    ret = ice_val_list_iter_remove (it->lit) &&
          ice_val_list_iter_remove (it->lit);
    /* FIXME: cleanup if only the first fails */

    return ret;
}

ice_val_t *
ice_val_decode_url (const ice_val_t *inv)
{
    int i = 0, j = 0;
    const char *ins;
    unsigned char *url;
    ice_val_t *ret;

    if (!ice_val_get_string (inv, &ins)) {
        return NULL;
    }

    url = (unsigned char *)x_malloc (strlen (ins));
    if (!url) {
        x_oom ();
        return NULL;
    }

    while (ins[i]) {
        unsigned char chr = ins[i++];

        if (chr == '+') {
            chr = ' ';
        } else if (chr == '%') {
            char ts[3];
            char *t;

            ts[0] = ins[i++];
            if (!ts[0])
                goto err;
            ts[1] = ins[i++];
            if (!ts[1])
                goto err;
            ts[2] = '\0';

            chr = strtoul (ts, &t, 16);

            if (t != &ts[2])
                goto err;
        }

        url[j++] = chr;
    }

    ret = ice_val_new_bin (url, j);
    free (url);

    return ret;

err:
    free (url);
    return NULL;
}

ice_val_t *
ice_val_build_dict (const char *firstkey, ...)
{
    va_list ap;
    const char *key;
    ice_val_t *val, *res;

    res = ice_val_new_dict ();
    if (!res)
        return NULL;

    va_start (ap, firstkey);

    key = firstkey;
    do {
        val = va_arg (ap, ice_val_t *);

        if (!ice_val_dict_set (res, key, val)) {
            ice_val_unref (res);
            res = NULL;
            break;
        }
        ice_val_unref (val);
        key = va_arg (ap, const char *);
    } while (key);

    va_end (ap);

    return res;
}

ice_val_t *
ice_val_build_list_va (ice_val_t *first_entry, va_list ap)
{
    ice_val_t *val, *res;

    res = ice_val_new_list ();
    if (!res)
        return NULL;

    val = first_entry;

    while (val) {
        if (!ice_val_list_append (res, val)) {
            ice_val_unref (res);
            res = NULL;
            break;
        }

        ice_val_unref (val);

        val = va_arg (ap, ice_val_t *);
    }

    return res;
}

ice_val_t *
ice_val_build_list (ice_val_t *first_entry, ...)
{
    va_list ap;
    ice_val_t *res;

    va_start (ap, first_entry);
    res = ice_val_build_list_va (first_entry, ap);
    va_end (ap);

    return res;
}


/**
 * This function will make a pretty string about the information in
 * ice_val dict.
 *
 * @param target A allocated char *
 * @param len Length of target
 * @param fmt A format string to use. You can insert items from the dict by
 * using specialformat "${field}".
 * @param val The #ice_val_t that contains the dict.
 *
 * @returns The number of chars written to target
 */
int
ice_val_dict_format (char *target, int len, const char *fmt, ice_val_t *val)
{
    const char *pos;

    if (!target) {
        return 0;
    }

    if (!fmt) {
        return 0;
    }

    memset (target, 0, len);

    pos = fmt;
    while (strlen (target) + 1 < len) {
        char *next_key, *key, *end;
        int keylen;
        ice_val_dict_iter_t *it;
        ice_val_t *v;

        next_key = strstr (pos, "${");
        if (!next_key) {
            strncat (target, pos, len - strlen (target) - 1);
            break;
        }

        strncat (target, pos, MIN (next_key - pos, len - strlen (target) - 1));
        keylen = strcspn (next_key + 2, "}");
        key = (char *)malloc (keylen + 1);

        if (!key) {
            fprintf (stderr, "Unable to allocate %u bytes of memory, OOM?", keylen);
            break;
        }

        memset (key, 0, keylen + 1);
        strncpy (key, next_key + 2, keylen);

        ice_val_get_dict_iter (val, &it);

        if (strcmp (key, "seconds") == 0) {
            int duration;

            if (ice_val_dict_iter_find (it, "duration")) {
                ice_val_dict_iter_pair (it, NULL, &v);
                ice_val_get_int (v, &duration);
            } else {
                duration = 0;
            }

            if (!duration) {
                strncat (target, "00", len - strlen (target) - 1);
            } else {
                char seconds[10];
                /* rounding */
                duration += 500;
                snprintf (seconds, sizeof (seconds), "%02d", (duration/1000)%60);
                strncat (target, seconds, len - strlen (target) - 1);
            }
        } else if (strcmp (key, "minutes") == 0) {
            int duration;

            if (ice_val_dict_iter_find (it, "duration")) {
                ice_val_dict_iter_pair (it, NULL, &v);
                ice_val_get_int (v, &duration);
            } else {
                duration = 0;
            }

            if (!duration) {
                strncat (target, "00", len - strlen (target) - 1);
            } else {
                char minutes[10];
                /* rounding */
                duration += 500;
                snprintf (minutes, sizeof (minutes), "%02d", duration/60000);
                strncat (target, minutes, len - strlen (target) - 1);
            }
        } else {
            const char *result = NULL;
            char tmp[12];

            if (ice_val_dict_iter_find (it, key)) {
                ice_val_dict_iter_pair (it, NULL, &v);

                ice_val_type_t type = ice_val_get_type (v);
                if (type == ICE_VAL_TYPE_STRING) {
                    ice_val_get_string (v, &result);
                } else if (type == ICE_VAL_TYPE_UINT32) {
                    uint32_t ui;
                    ice_val_get_uint (v, &ui);
                    snprintf (tmp, 12, "%u", ui);
                    result = tmp;
                } else if (type == ICE_VAL_TYPE_INT32) {
                    int32_t i;
                    ice_val_get_int (v, &i);
                    snprintf (tmp, 12, "%d", i);
                    result = tmp;
                }
            }

            if (result)
                strncat (target, result, len - strlen (target) - 1);
        }

        free (key);
        end = strchr (next_key, '}');

        if (!end) {
            break;
        }

        pos = end + 1;
    }

    return strlen (target);
}

static int
_ice_val_utf8_charlen (unsigned char c)
{
    if ((c & 0x80) == 0) {
        return 1;
    } else if ((c & 0x60) == 0x40) {
        return 2;
    } else if ((c & 0x70) == 0x60) {
        return 3;
    } else if ((c & 0x78) == 0x70) {
        return 4;
    }
    return 0;
}


/**
 * Check if a string is valid UTF-8.
 *
 */
int
ice_val_utf8_validate (const char *str)
{
    int i = 0;

    for (;;) {
        unsigned char c = str[i++];
        int l;
        if (!c) {
            /* NUL - end of string */
            return 1;
        }

        l = _ice_val_utf8_charlen (c);
        if (l == 0)
            return 0;
        while (l-- > 1) {
            if ((str[i++] & 0xC0) != 0x80)
                return 0;
        }
    }
}



/**
 * @internal
 */
int
absolutify_and_validate_pos (int *pos, int size, int allow_append)
{
    frost_return_val_if_fail (size >= 0, 0);

    if (*pos < 0) {
        if (-*pos > size)
            return 0;
        *pos = size + *pos;
    }

    if (*pos > size)
        return 0;

    if (!allow_append && *pos == size)
        return 0;

    return 1;
}

int
ice_val_dict_has_key (ice_val_t *dictv, const char *key)
{
    return ice_val_dict_get (dictv, key, NULL);
}


ice_val_t *
ice_val_bitbuffer_new_ro (const unsigned char *v, int len)
{
    ice_val_t *val;

    val = ice_val_new (ICE_VAL_TYPE_BITBUFFER);
    val->value.bit.buf = (unsigned char *) v;
    val->value.bit.len = len * 8;
    val->value.bit.ro = true;
    return val;
}

ice_val_t *
ice_val_bitbuffer_new (void)
{
    ice_val_t *val;

    val = ice_val_new (ICE_VAL_TYPE_BITBUFFER);
    val->value.bit.buf = NULL;
    val->value.bit.len = 0;
    val->value.bit.ro = false;
    return val;
}


int
ice_val_bitbuffer_get_bits (ice_val_t *v, int bits, int *res)
{
    int i, t, r;

    x_api_error_if (bits < 1, "less than one bit requested", 0);

    if (bits == 1) {
        int pos = v->value.bit.pos;

        if (pos >= v->value.bit.len)
            return 0;
        r = (v->value.bit.buf[pos / 8] >> (7-(pos % 8)) & 1);
        v->value.bit.pos += 1;
        *res = r;
        return 1;
    }

    r = 0;
    for (i = 0; i < bits; i++) {
        t = 0;
        if (!ice_val_bitbuffer_get_bits (v, 1, &t))
            return 0;
        r = (r << 1) | t;
    }
    *res = r;
    return 1;
}

int
ice_val_bitbuffer_get_data (ice_val_t *v, unsigned char *b, int len)
{
    while (len) {
        int t;
        if (!ice_val_bitbuffer_get_bits (v, 8, &t))
            return 0;
        *b = t;
        b++;
        len--;
    }
    return 1;
}

int
ice_val_bitbuffer_put_bits (ice_val_t *v, int bits, int d)
{
    unsigned char t;
    int pos;
    int i;

    x_api_error_if (v->value.bit.ro, "write to readonly bitbuffer", 0);
    x_api_error_if (bits < 1, "less than one bit requested", 0);

    if (bits == 1) {
        pos = v->value.bit.pos;

        if (pos >= v->value.bit.alloclen) {
            int ol, nl;
            nl = v->value.bit.alloclen * 2;
            ol = v->value.bit.alloclen;
            nl = nl < 128 ? 128 : nl;
            nl = (nl + 7) & ~7;
            v->value.bit.buf = (unsigned char*)realloc (v->value.bit.buf, nl / 8);
            memset (v->value.bit.buf + ol / 8, 0, (nl - ol) / 8);
            v->value.bit.alloclen = nl;
        }
        t = v->value.bit.buf[pos / 8];

        t = (t & (~(1<<(7-(pos % 8))))) | (d << (7-(pos % 8)));

        v->value.bit.buf[pos / 8] = t;

        v->value.bit.pos += 1;
        if (v->value.bit.pos > v->value.bit.len)
            v->value.bit.len = v->value.bit.pos;
        return 1;
    }

    for (i = 0; i < bits; i++) {
        if (!ice_val_bitbuffer_put_bits (v, 1, !!(d & (1 << (bits-i-1)))))
            return 0;
    }

    return 1;
}

int
ice_val_bitbuffer_put_bits_at (ice_val_t *v, int bits, int d, int offset)
{
    int prevpos;
    prevpos = ice_val_bitbuffer_pos (v);
    if (!ice_val_bitbuffer_goto (v, offset))
        return 0;
    if (!ice_val_bitbuffer_put_bits (v, bits, d))
        return 0;
    return ice_val_bitbuffer_goto (v, prevpos);
}

int
ice_val_bitbuffer_put_data (ice_val_t *v, const unsigned char *b, int len)
{
    while (len) {
        int t;
        t = *b;
        if (!ice_val_bitbuffer_put_bits (v, 8, t))
            return 0;
        b++;
        len--;
    }
    return 1;
}

int
ice_val_bitbuffer_align (ice_val_t *v)
{
    v->value.bit.pos = (v->value.bit.pos + 7) % 8;
    return 1;
}

int
ice_val_bitbuffer_goto (ice_val_t *v, int pos)
{
    x_api_error_if (pos < 0, "negative position", 0);
    x_api_error_if (pos > v->value.bit.len, "position after buffer end", 0);

    v->value.bit.pos = pos;
    return 1;
}

int
ice_val_bitbuffer_pos (ice_val_t *v)
{
    return v->value.bit.pos;
}

int
ice_val_bitbuffer_rewind (ice_val_t *v)
{
    return ice_val_bitbuffer_goto (v, 0);
}

int
ice_val_bitbuffer_end (ice_val_t *v)
{
    return ice_val_bitbuffer_goto (v, v->value.bit.len);
}

int
ice_val_bitbuffer_len (ice_val_t *v)
{
    return v->value.bit.len;
}

const unsigned char *
ice_val_bitbuffer_buffer (ice_val_t *v)
{
    return v->value.bit.buf;
}

struct ice_val_coll_St {

    /* refcounting */
    int ref;

    ice_val_coll_type_t type;
    ice_val_t *operands;
    ice_val_t *attributes;
    ice_val_t *idlist;

    int32_t *legacy_idlist;
};


static void ice_val_coll_free (ice_val_coll_t *coll);


/**
 * @defgroup CollectionStructure CollectionStructure
 * @ingroup Collections
 * @brief The API to be used to work with collection structures.
 *
 * @{
 */

/**
 * Increases the references for the #ice_val_coll_t
 *
 * @param coll the collection to reference.
 * @return coll
 */
ice_val_coll_t *
ice_val_coll_ref (ice_val_coll_t *coll)
{
    frost_return_val_if_fail (coll, NULL);

    coll->ref++;

    return coll;
}

/**
 * Allocate a new collection of the given type.
 * The pointer will have to be deallocated using #ice_val_coll_unref.
 *
 * @param type the #ice_val_coll_type_t specifying the type of collection to create.
 * @return a pointer to the newly created collection, or NULL if the type is invalid.
 */
ice_val_coll_t*
ice_val_coll_new (ice_val_coll_type_t type)
{
    ice_val_coll_t *coll;

    frost_return_val_if_fail (type <= ICE_COLLECTION_TYPE_LAST, NULL);

    coll = (ice_val_coll_t *)x_new0 (ice_val_coll_t, 1);
    if (!coll) {
        x_oom ();
        return NULL;
    }

    coll->ref  = 0;
    coll->type = type;

    coll->idlist = ice_val_new_list ();
    ice_val_list_restrict_type (coll->idlist, ICE_VAL_TYPE_INT32);

    coll->operands = ice_val_new_list ();
    ice_val_list_restrict_type (coll->operands, ICE_VAL_TYPE_COLL);

    coll->attributes = ice_val_new_dict ();

    coll->legacy_idlist = NULL;

    /* user must give this back */
    ice_val_coll_ref (coll);

    return coll;
}

/**
 * Free the memory owned by the collection.
 * You probably want to use #ice_val_coll_unref instead, which handles
 * reference counting.
 *
 * @param coll the collection to free.
 */
static void
ice_val_coll_free (ice_val_coll_t *coll)
{
    frost_return_if_fail (coll);

    /* Unref all the operands and attributes */
    ice_val_unref (coll->operands);
    ice_val_unref (coll->attributes);
    ice_val_unref (coll->idlist);
    if (coll->legacy_idlist) {
        free (coll->legacy_idlist);
    }

    free (coll);
}

/**
 * Decreases the references for the #ice_val_coll_t
 * When the number of references reaches 0 it will
 * be freed and all its operands unreferenced as well.
 *
 * @param coll the collection to unref.
 */
void
ice_val_coll_unref (ice_val_coll_t *coll)
{
    frost_return_if_fail (coll);
    x_api_error_if (coll->ref < 1, "with a freed collection",);

    coll->ref--;
    if (coll->ref == 0) {
        ice_val_coll_free (coll);
    }
}


/**
 * Set the list of ids in the given collection.
 * The list must be 0-terminated.
 * Note that the idlist is only relevant for idlist collections.
 *
 * @param coll the collection to modify.
 * @param ids  the 0-terminated list of ids to store in the collection.
 */
void
ice_val_coll_set_idlist (ice_val_coll_t *coll, int ids[])
{
    unsigned int i;

    ice_val_list_clear (coll->idlist);
    for (i = 0; ids[i]; i++) {
        ice_val_list_append_int (coll->idlist, ids[i]);
    }
}

static int
_ice_val_coll_operand_find (ice_val_list_iter_t *it, ice_val_coll_t *op)
{
    ice_val_coll_t *c;
    ice_val_t *v;

    while (ice_val_list_iter_valid (it)) {
        ice_val_list_iter_entry (it, &v);
        if (ice_val_get_coll (v, &c)) {
            if (c == op) {
                return 1;
            }
        }
        ice_val_list_iter_next (it);
    }
    return 0;
}

/**
 * Add the operand to the given collection.
 * @param coll  The collection to add the operand to.
 * @param op    The operand to add.
 */
void
ice_val_coll_add_operand (ice_val_coll_t *coll, ice_val_coll_t *op)
{
    ice_val_list_iter_t *it;
    ice_val_t *v;
    frost_return_if_fail (coll);
    frost_return_if_fail (op);

    /* we used to check if it already existed here before */
    if (!ice_val_get_list_iter (coll->operands, &it))
        return;

    if (_ice_val_coll_operand_find (it, op)) {
        x_api_warning ("with an operand already in operand list");
        ice_val_list_iter_explicit_destroy (it);
        return;
    }

    ice_val_list_iter_explicit_destroy (it);

    v = ice_val_new_coll (op);
    frost_return_if_fail (v);
    ice_val_list_append (coll->operands, v);
    ice_val_unref (v);
}

/**
 * Remove all the occurences of the operand in the given collection.
 * @param coll  The collection to remove the operand from.
 * @param op    The operand to remove.
 */
void
ice_val_coll_remove_operand (ice_val_coll_t *coll, ice_val_coll_t *op)
{
    ice_val_list_iter_t *it;

    frost_return_if_fail (coll);
    frost_return_if_fail (op);

    if (!ice_val_get_list_iter (coll->operands, &it))
        return;

    if (_ice_val_coll_operand_find (it, op)) {
        ice_val_list_iter_remove (it);
    } else {
        x_api_warning ("with an operand not in operand list");
    }
    ice_val_list_iter_explicit_destroy (it);
}


/**
 * Append a value to the idlist.
 * @param coll  The collection to update.
 * @param id    The id to append to the idlist.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_append (ice_val_coll_t *coll, int id)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_append_int (coll->idlist, id);
}

/**
 * Insert a value at a given position in the idlist.
 * @param coll  The collection to update.
 * @param id    The id to insert in the idlist.
 * @param index The position at which to insert the value.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_insert (ice_val_coll_t *coll, int index, int id)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_insert_int (coll->idlist, index, id);
}

/**
 * Move a value of the idlist to a new position.
 * @param coll  The collection to update.
 * @param index The index of the value to move.
 * @param newindex The newindex to which to move the value.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_move (ice_val_coll_t *coll, int index, int newindex)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_move (coll->idlist, index, newindex);
}

/**
 * Remove the value at a given index from the idlist.
 * @param coll  The collection to update.
 * @param index The index at which to remove the value.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_remove (ice_val_coll_t *coll, int index)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_remove (coll->idlist, index);
}

/**
 * Empties the idlist.
 * @param coll  The collection to update.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_clear (ice_val_coll_t *coll)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_clear (coll->idlist);
}

/**
 * Retrieves the value at the given position in the idlist.
 * @param coll  The collection to update.
 * @param index The position of the value to retrieve.
 * @param val   The pointer at which to store the found value.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_get_index (ice_val_coll_t *coll, int index, int32_t *val)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_get_int (coll->idlist, index, val);
}

/**
 * Sets the value at the given position in the idlist.
 * @param coll  The collection to update.
 * @param index The position of the value to set.
 * @param val   The new value.
 * @return  TRUE on success, false otherwise.
 */
int
ice_val_coll_idlist_set_index (ice_val_coll_t *coll, int index, int32_t val)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_set_int (coll->idlist, index, val);
}

/**
 * Get the size of the idlist.
 * @param coll  The collection to update.
 * @return  The size of the idlist.
 */
size_t
ice_val_coll_idlist_get_size (ice_val_coll_t *coll)
{
    frost_return_val_if_fail (coll, 0);

    return ice_val_list_get_size (coll->idlist);
}



/**
 * Return the type of the collection.
 * @param coll  The collection to consider.
 * @return The #ice_val_coll_type_t of the collection, or -1 if invalid.
 */
ice_val_coll_type_t
ice_val_coll_get_type (ice_val_coll_t *coll)
{
    x_return_val_if_fail (coll, (ice_val_coll_type_t)-1);

    return coll->type;
}

/**
 * Return the list of ids stored in the collection.
 * The list is owned by the collection.
 * Note that this must not be confused with the content of the
 * collection, which must be queried using ice_coll_coll_query_ids!
 *
 * Also note that this function is deprecated (use ice_val_coll_idlist_get
 * instead) and that changes to the returned array will be ignored. The array
 * is also not updated when the idlist is changed using the supplied functions.
 * Additionally every call to this function allocates a new array, so calling
 * it repetitively will be a performance penalty.
 *
 * @param coll  The collection to consider.
 * @return The 0-terminated list of ids.
 */
const int32_t*
ice_val_coll_get_idlist (ice_val_coll_t *coll)
{
    ice_val_list_iter_t *it;
    unsigned int i;
    int32_t entry;

    x_return_null_if_fail (coll);

    /* free and allocate a new legacy list */
    if (coll->legacy_idlist) {
        free (coll->legacy_idlist);
    }
    coll->legacy_idlist = (int32_t*)calloc (ice_val_coll_idlist_get_size (coll) + 1,
                                  sizeof (int32_t));

    /* copy contents to legacy list */
    for (ice_val_get_list_iter (coll->idlist, &it), i = 0;
         ice_val_list_iter_valid (it);
         ice_val_list_iter_next (it), i++) {
        ice_val_list_iter_entry_int (it, &entry);
        coll->legacy_idlist[i] = entry;
    }
    coll->legacy_idlist[i] = 0;

    return coll->legacy_idlist;
}

/**
 * Return the list of ids stored in the collection.
 * This function does not increase the refcount of the list, the reference is
 * still owned by the collection.
 *
 * Note that this must not be confused with the content of the collection,
 * which must be queried using ice_coll_coll_query_ids!
 *
 * @param coll  The collection to consider.
 * @return The 0-terminated list of ids.
 */
ice_val_t *
ice_val_coll_idlist_get (ice_val_coll_t *coll)
{
    x_return_null_if_fail (coll);

    return coll->idlist;
}

ice_val_t *
ice_val_coll_operands_get (ice_val_coll_t *coll)
{
    frost_return_val_if_fail (coll, NULL);

    return coll->operands;
}

ice_val_t *
ice_val_coll_attributes_get (ice_val_coll_t *coll)
{
    frost_return_val_if_fail (coll, NULL);

    return coll->attributes;
}

/**
 * Set an attribute in the given collection.
 *
 * @param coll The collection in which to set the attribute.
 * @param key  The name of the attribute to set.
 * @param value The value of the attribute.
 */
void
ice_val_coll_attribute_set (ice_val_coll_t *coll, const char *key, const char *value)
{
    ice_val_t *v;

    v = ice_val_new_string (value);
    frost_return_if_fail (v);

    ice_val_dict_set (coll->attributes, key, v);
    ice_val_unref (v);
}

/**
 * Remove an attribute from the given collection.
 * The return value indicated whether the attribute was found (and
 * removed)
 *
 * @param coll The collection to remove the attribute from.
 * @param key  The name of the attribute to remove.
 * @return 1 upon success, 0 otherwise
 */
int
ice_val_coll_attribute_remove (ice_val_coll_t *coll, const char *key)
{
    return ice_val_dict_remove (coll->attributes, key);
}

/**
 * Retrieve the value of the attribute of the given collection.
 * The return value is 1 if the attribute was found and 0 otherwise.
 * The value of the attribute is owned by the collection and must not
 * be freed by the caller.
 *
 * @param coll The collection to retrieve the attribute from.
 * @param key  The name of the attribute.
 * @param value The value of the attribute if found (owned by the collection).
 * @return 1 if the attribute was found, 0 otherwise
 */
int
ice_val_coll_attribute_get (ice_val_coll_t *coll, const char *key, char **value)
{
    if (ice_val_dict_entry_get_string (coll->attributes, key, (const char **)value)) {
        return 1;
    }
    *value = NULL;
    return 0;
}



struct attr_fe_data {
    ice_val_coll_attribute_foreach_func func;
    void *userdata;
};

static void
attr_fe_func (const char *key, ice_val_t *val, void *user_data)
{
    struct attr_fe_data *d = (struct attr_fe_data *)user_data;
    const char *v;
    int r;

    r = ice_val_get_string (val, &v);
    frost_return_if_fail (r);

    d->func (key, v, d->userdata);
}
/**
 * Iterate over all key/value-pair of the collection attributes.
 *
 * Calls specified function for each key/value-pair of the attribute list.
 *
 * void function (const char *key, const char *value, void *user_data);
 *
 * @param coll the #ice_val_coll_t.
 * @param func function that is called for each key/value-pair
 * @param user_data extra data passed to func
 */
void
ice_val_coll_attribute_foreach (ice_val_coll_t *coll,
                              ice_val_coll_attribute_foreach_func func,
                              void *user_data)
{
    struct attr_fe_data d = {func, user_data};
    ice_val_dict_foreach (coll->attributes, attr_fe_func, &d);
}

/**
 * Return a collection referencing the whole media library,
 * that is a reference to the "All Media" collection.
 * The returned structure must be unref'd using #ice_val_coll_unref
 * after usage.
 *
 * @return a collection referring to the "All Media" collection.
 */
ice_val_coll_t*
ice_val_coll_universe ()
{
    ice_val_coll_t *univ = ice_val_coll_new (ICE_COLLECTION_TYPE_REFERENCE);
    ice_val_coll_attribute_set (univ, "reference", "All Media");
    /* FIXME: namespace? */

    return univ;
}

/** @} */


