

#include "ice_streamer.h"

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

#include "utilities_debug/cabot_assert.h"
#include "utilities_debug/trace.h"
#include "frost_mem.h"
#include "frost_tree.h"
#include "frost_basicstring.h"

TRACE_IMPLEMENT("ice/ice_streamer_object");

static ice_val_t *ice_create_ice_val_list (FROST_List *list);
static ice_val_t *ice_streamer_create_ice_val_dict (FROST_Tree *dict);
static void create_ice_val_list_foreach (frost_pointer data, frost_pointer userdata);
static frost_bool create_ice_val_dict_foreach (frost_pointer key, frost_pointer data, frost_pointer userdata);


/** @defgroup Object Object
  * @ingroup ICE_STREAMER_Server
  * @brief Object representation in ICE STREAMER server. A object can
  * be used to emit signals.
  * @{
  */

/**
 * A signal handler and it's data.
 */
typedef struct {
    ice_streamer_object_handler_t handler;
    frost_pointer userdata;
} ice_streamer_object_handler_entry_t;

static frost_bool
cleanup_signal_list (frost_pointer key, frost_pointer value, frost_pointer data)
{
    FROST_List *list = (FROST_List *)value;

    while (list) {
        frost_free (list->data);
        list = frost_list_delete_link (list, list);
    }

    return frost_false; /* keep going */
}

/**
 * Cleanup all the resources for the object
 */
void
ice_streamer_object_cleanup (ice_streamer_object_t *object)
{
    frost_return_if_fail (object);
    frost_return_if_fail (ICEST_IS_OBJECT (object));

    if (object->signals) {
        /* destroy the tree manually (ie not via a value_destroy_func
         * callback since we're often "replacing" values when we're
         * adding new elements to the signal lists. and we don't want
         * the value to be destroyed in those cases :)
         */
        frost_tree_foreach (object->signals, cleanup_signal_list, NULL);
        frost_tree_destroy (object->signals);
    }

    if (object->cmds) {
        /* We don't need to free the commands themselves -- they are
         * stored in read-only memory.
         */
        frost_tree_destroy (object->cmds);
    }

    FROST_MutexDestroy (object->mutex);
}

static frost_int
compare_signal_key (frost_constpointer a, frost_constpointer b)
{
    frost_int aa = frost_pointer_TO_INT (a);
    frost_int bb = frost_pointer_TO_INT (b);

    if (aa < bb)
        return -1;
    else if (aa > bb)
        return 1;
    else
        return 0;
}

/**
  * Connect to a signal that is emitted by this object.
  * You can connect many handlers to the same signal as long as
  * the handler address is unique.
  *
  * @todo fix the need for a unique handler adress?
  *
  * @param object the object that will emit the signal
  * @param signalid the signalid to connect to
  * @param handler the Callback function to be called when signal is emited.
  * @param userdata data to the callback function
  */

void
ice_streamer_object_connect (ice_streamer_object_t *object, frost_uint32 signalid,
                     ice_streamer_object_handler_t handler, frost_pointer userdata)
{
    FROST_List *list = NULL;
    ice_streamer_object_handler_entry_t *entry;

    frost_return_if_fail (object);
    frost_return_if_fail (ICEST_IS_OBJECT (object));
    frost_return_if_fail (handler);

    entry = frost_new0 (ice_streamer_object_handler_entry_t, 1);
    entry->handler = handler;
    entry->userdata = userdata;

    if (!object->signals)
        object->signals = frost_tree_new (compare_signal_key);
    else
        list = (FROST_List *)frost_tree_lookup (object->signals,
                              frost_int_TO_POINTER (signalid));

    list = frost_list_prepend (list, entry);

    /* store the list's new head in the tree */
    frost_tree_insert (object->signals, frost_int_TO_POINTER (signalid), list);
}

/**
  * Disconnect from a signal
  */

void
ice_streamer_object_disconnect (ice_streamer_object_t *object, frost_uint32 signalid,
                        ice_streamer_object_handler_t handler, frost_pointer userdata)
{
    FROST_List *list, *node = NULL;
    ice_streamer_object_handler_entry_t *entry;

    frost_return_if_fail (object);
    frost_return_if_fail (ICEST_IS_OBJECT (object));
    frost_return_if_fail (handler);

    FROST_MutexLock (object->mutex);

    if (object->signals) {
        list = (FROST_List *)frost_tree_lookup (object->signals,
                              frost_int_TO_POINTER (signalid));

        for (node = list; node; node = frost_list_next (node)) {
            entry = (ice_streamer_object_handler_entry_t *)node->data;

            if (entry->handler == handler && entry->userdata == userdata)
                break;
        }

        if (node) {
            list = frost_list_remove_link (list, node);

            /* store the list's new head in the tree */
            frost_tree_insert (object->signals,
                           frost_int_TO_POINTER (signalid), list);
        }
    }

    FROST_MutexUnlock (object->mutex);

    frost_return_if_fail (node);

    frost_free (node->data);
    frost_list_free_1 (node);
}

/**
  * Emit a signal and thus call all the handlers that are connected.
  *
  * @param object the object to signal on.
  * @param signalid the signalid to emit
  * @param data the data that should be sent to the handler.
  */

void
ice_streamer_object_emit (ice_streamer_object_t *object, frost_uint32 signalid, ice_val_t *data)
{
    FROST_List *list, *node, *list2 = NULL;
    ice_streamer_object_handler_entry_t *entry;

    frost_return_if_fail (object);
    frost_return_if_fail (ICEST_IS_OBJECT (object));

    FROST_MutexLock (object->mutex);

    if (object->signals) {
        list = (FROST_List *)frost_tree_lookup (object->signals,
                              frost_int_TO_POINTER (signalid));

        for (node = list; node; node = frost_list_next (node)) {
            entry = (ice_streamer_object_handler_entry_t *)node->data;

            list2 = frost_list_prepend (list2, entry);
        }
    }

    FROST_MutexUnlock (object->mutex);

    while (list2) {
        entry = (ice_streamer_object_handler_entry_t *)list2->data;

        /* NULL entries may never be added to the trees. */
        CABOT_ASSERT (entry);
        CABOT_ASSERT (entry->handler);

        entry->handler (object, data, entry->userdata);

        list2 = frost_list_delete_link (list2, list2);
    }
}

/**
 * Initialize a command argument.
 */

void
ice_streamer_object_cmd_arg_init (ice_streamer_object_cmd_arg_t *arg)
{
    frost_return_if_fail (arg);

    memset (arg, 0, sizeof (ice_streamer_object_cmd_arg_t));
    ice_streamer_error_reset (&arg->error);
}

/**
 * Emits a signal on the current object. This is like ice_streamer_object_emit
 * but you don't have to create the #ice_streamer_object_cmd_arg_t yourself.
 * Use this when you creating non-complex signal arguments.
 *
 * @param object Object to signal on.
 * @param signalid Signal to emit.
 * @param type the argument type to emit followed by the argument data.
 *
 */

void
ice_streamer_object_emit_f (ice_streamer_object_t *object, frost_uint32 signalid,
                    ice_streamer_vtype_t type, ...)
{
    va_list ap;
    ice_val_t *arg;

    va_start (ap, type);

    switch (type) {
        case ICE_STREAMER_V_TYPE_NONE:
            arg = ice_val_new_none ();
            break;
        case ICE_STREAMER_V_TYPE_INT32:
            arg = ice_val_new_int (va_arg (ap, frost_int32));
            break;
        case ICE_STREAMER_V_TYPE_STRING:
            arg = ice_val_new_string (va_arg (ap, char *));
            break;
        case ICE_STREAMER_V_TYPE_DICT:
            arg = ice_streamer_create_ice_val_dict (va_arg (ap, FROST_Tree *));
            break;
        case ICE_STREAMER_V_TYPE_END:
        default:
            TRACE_INFO(("OBJECT: trying to emit value of unsupported type (%d)!", (int)type));
            break;
    }
    va_end (ap);

    ice_streamer_object_emit (object, signalid, arg);

    /* In all cases above, we created a new ice_val_t, which we
     * now destroy.
     * In some cases, those ice_val_t's are created from GLib objects,
     * such as FROST_Trees. Here we must not destroy those GLib objects,
     * because the caller wants to do that. However, the ice_val_t's
     * don't hold onto those GLib objects, so unreffing the
     * ice_val_t doesn't kill the GLib object.
     */
    ice_val_unref (arg);
}

static frost_int
compare_cmd_key (frost_constpointer a, frost_constpointer b)
{
    frost_uint aa = frost_pointer_TO_INT (a);
    frost_uint bb = frost_pointer_TO_INT (b);

    if (aa < bb)
        return -1;
    else if (aa > bb)
        return 1;
    else
        return 0;
}

/**
  * Add a command that could be called from the client API to a object.
  *
  * @param object The object that should have the method.
  * @param cmdid A command id.
  * @param desc A command description.
  */
void
ice_streamer_object_cmd_add (ice_streamer_object_t *object, frost_uint cmdid,
                     const ice_streamer_object_cmd_func_t func)
{
    frost_return_if_fail (object);
    frost_return_if_fail (func);

    if (!object->cmds)
        object->cmds = frost_tree_new (compare_cmd_key);

    frost_tree_insert (object->cmds, frost_uint_TO_POINTER (cmdid),
                   (frost_pointer) func);
}

/**
  * Call a command with argument.
  */

void
ice_streamer_object_cmd_call (ice_streamer_object_t *object, frost_uint cmdid, ice_streamer_object_cmd_arg_t *arg)
{
    ice_streamer_object_cmd_func_t func;

    frost_return_if_fail (object);

    if (object->cmds) {
        func = (ice_streamer_object_cmd_func_t )frost_tree_lookup (object->cmds, frost_uint_TO_POINTER (cmdid));

        if (func)
            func (object, arg);
    }
}


/**
 * Create a new #ice_val_t list initialized with the argument.
 * @param list The list of values to initially fill the #ice_val_t with.
 * @return a new #ice_val_t list.
 */
static ice_val_t *
ice_create_ice_val_list (FROST_List *list)
{
    ice_val_t *v = ice_val_new_list ();
    frost_list_foreach (list, create_ice_val_list_foreach, (frost_pointer) v);
    return v;
}

ice_val_t *
ice_convert_and_kill_list (FROST_List *list)
{
    ice_val_t *v;

    v = ice_create_ice_val_list (list);
    frost_list_free (list);

    return v;
}

/**
 * Create a new #ice_val_t dict initialized with the argument.
 * @param dict The dict of values to initially fill the #ice_val_t with.
 * @return a new #ice_val_t dict.
 */
static ice_val_t *
ice_streamer_create_ice_val_dict (FROST_Tree *dict)
{
    ice_val_t *v = NULL;
    if (dict) {
        v = ice_val_new_dict ();
        frost_tree_foreach (dict, create_ice_val_dict_foreach, (frost_pointer) v);
    }
    return v;
}

ice_val_t *
ice_streamer_convert_and_kill_dict (FROST_Tree *dict)
{
    ice_val_t *v;

    v = ice_streamer_create_ice_val_dict (dict);

    if (dict) {
        frost_tree_destroy (dict);
    }

    return v;
}

ice_val_t *
ice_streamer_convert_and_kill_string (char *str)
{
    ice_val_t *v = NULL;

    if (str) {
        v = ice_val_new_string (str);
        frost_free (str);
    }

    return v;
}

/** @} */

static void
create_ice_val_list_foreach (frost_pointer data, frost_pointer userdata)
{
    ice_val_t *v = (ice_val_t *) data;
    ice_val_t *l = (ice_val_t *) userdata;

    ice_val_list_append (l, v);

    /* Transfer ownership of 'v' from the FROST_List to the
     * ice_val_list.
     */
    ice_val_unref (v);
}

static frost_bool
create_ice_val_dict_foreach (frost_pointer key, frost_pointer data, frost_pointer userdata)
{
    const char *k = (const char *) key;
    ice_val_t *v = (ice_val_t *) data;
    ice_val_t *l = (ice_val_t *) userdata;
    ice_val_dict_set (l, k, v);
    return frost_false;
}

int
ice_bin_to_gstring (ice_val_t *value, frost_basic_string **gs)
{
    const unsigned char *str;
    frost_uint len;
    if (!ice_val_get_bin (value, &str, &len)) {
        return 0;
    }
    *gs = frost_string_new_len ((const char *) str, len);
    return 1;
}

int
dummy_identity (ice_val_t *value, ice_val_t **arg)
{
    *arg = value;
    return 1;
}

/**
 * Checks that the list only contains string values.
 */
frost_bool
check_string_list (ice_val_t *list)
{
    ice_val_t *valstr;
    ice_val_list_iter_t *it;

    for (ice_val_get_list_iter (list, &it);
         ice_val_list_iter_valid (it);
         ice_val_list_iter_next (it)) {
        ice_val_list_iter_entry (it, &valstr);
        if (ice_val_get_type (valstr) != ICE_STREAMER_V_TYPE_STRING) {
            return frost_false;
        }
    }

    return frost_true;
}

static FROST_Mutex *frost_atomic_mutex = 0;

void
frost_atomic_int_add (volatile frost_int __attribute__((may_alias)) *atomic,
          frost_int           val)
{
    if(!frost_atomic_mutex)
    {
        frost_atomic_mutex = FROST_MutexCreate() ;
    }

    FROST_MutexLock (frost_atomic_mutex);
    *atomic += val;
    FROST_MutexUnlock (frost_atomic_mutex);
}

frost_int
frost_atomic_int_exchange_and_add (volatile frost_int __attribute__((may_alias)) *atomic,
                   frost_int           val)
{
    if(!frost_atomic_mutex)
    {
        frost_atomic_mutex = FROST_MutexCreate() ;
    }

    frost_int result;

    FROST_MutexLock (frost_atomic_mutex);
    result = *atomic;
    *atomic += val;
    FROST_MutexUnlock (frost_atomic_mutex);

    return result;
}


void
__int_ice_streamer_object_unref (ice_streamer_object_t *object)
{
    frost_return_if_fail (object->ref > 0);
    if (frost_atomic_int_dec_and_test (&(object->ref))) {
        if (object->destroy_func)
            object->destroy_func (object);
        ice_streamer_object_cleanup (object);
        frost_free (object);
    }
}

ice_streamer_object_t *
__int_ice_streamer_object_new (frost_int size, ice_streamer_object_destroy_func_t destfunc)
{
    ice_streamer_object_t *ret;

    ret = (ice_streamer_object_t *)frost_calloc (size);
    ret->destroy_func = destfunc;
    ret->id = ICE_STREAMER_OBJECT_MID;

    ret->mutex = FROST_MutexCreate ();

    /* don't create the trees for the signals and the commands yet.
     * instead we instantiate those when we need them the first
     * time.
     */

    ice_streamer_object_ref (ret);

    return ret;
}

