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

#include "../mecha/mecha.h"

#include "stringdictionary.h"
#include "serializationinfo.h"
#include "type.h"

struct s_StringDictionary
{
    hshTvoid_list   *table;
};

static bool stringdictionary_serialize (void *field, SerializationInfo *info);
static bool stringdictionary_deserialize (void *field, SerializationInfo *info);
static dynTstring * stringdictionary_serializeTable (StringDictionary *dict);
static StringDictionary * strictdictionary_deserializeFromString (dynTstring *string);

Type type_declaration_StringDictionary = { "StringDictionary", stringdictionary_serialize, stringdictionary_deserialize };

StringDictionary *
stringdictionary_new()
{
    StringDictionary *info = (StringDictionary *) malloc (sizeof (StringDictionary));
    info->table = hshFvoid_init ((void (*) (void *)) dynFfree);

    return (info);
}

void
stringdictionary_destroy (void *ptr)
{
    StringDictionary *info = (StringDictionary *) ptr;

    hshFvoid_destroy (info->table);

    free (info);
}

bool
stringdictionary_addValue (StringDictionary *info, const char *key, dynTstring *value)
{
    if (hshFvoid_add (info->table, key, value))
    {
        return (TRUE);
    }
    else
    {
        return (FALSE);
    }
}

void
stringdictionary_setValue (StringDictionary *info, const char *key, dynTstring *value)
{
    hshFvoid_replace (info->table, key, (void *) value);
}

dynTstring *
stringdictionary_getValue (StringDictionary *info, const char *key)
{
    return ((dynTstring *) hshFvoid_find (info->table, key));
}

hshTiterator *
stringdictionary_getIterator (StringDictionary *dict)
{
    return (hshFiterator_init (dict->table));
}

static bool
stringdictionary_serialize (void *field, SerializationInfo *info)
{
    StringDictionary *dict = DEREFERENCE_TYPE (StringDictionary, field);

    serializationinfo_addValue (info, "table",
            stringdictionary_serializeTable (dict));

    return (TRUE);
}

static bool
stringdictionary_deserialize (void *field, SerializationInfo *info)
{
    StringDictionary *dict;

    dict = strictdictionary_deserializeFromString (
            serializationinfo_getValue (info, "table"));

    if (dict == NULL)
    {
        return (FALSE);
    }
    else
    {
        StringDictionary **fieldPtr = (StringDictionary **) field;

        *fieldPtr = dict;

        /* DEREFERENCE_TYPE (StringDictionary, field) = dict; */

        return (TRUE);
    }
}

static const char *
stringdictionary_deserializeGetString (const char *data, dynTstring *string)
{
    char *endptr;
    int len = strtol (data, &endptr, 10);

    if (*endptr != '[')
    {
        printf ("-- unknown string: %s\n", data);

        return (NULL);
    }

    dynFappend (string, endptr + 1, len);

    /*
     * data looks like:
     * nn[ssssssss]
     *
     * where
     *   n = "number"
     *   s = "string"
     *
     * endptr will be at "[" and the number in "nn" will be the length of
     * "sss", so all we need to do is add 1 to get to "sss", ad len, and add 1
     * to get to the end of the string
     */
    endptr += 1 + len + 1;

    return (endptr);
}

static dynTstring *
stringdictionary_serializeTable (StringDictionary *dict)
{
    hshTiterator *iter = hshFiterator_init (dict->table);
    dynTstring *string = dynFinit();

    dynFsappend (string, "<sd>");

    while (hshFiterator_next (iter) == 1)
    {
        char *key = (char *) hshFiterator_current_key (iter);
        dynTstring *value = (dynTstring *) hshFiterator_current_value (iter);
        int key_len = strlen (key);

        dynFappend_print (string, "%d", key_len);
        dynFappend (string, "[", 1);
        dynFappend (string, key, key_len);
        dynFappend (string, "]", 1);

        dynFappend_print (string, "%d", dynFgetlen (value));
        dynFappend (string, "[", 1);
        dynFappend (string, dynFgetstr (value), dynFgetlen (value));
        dynFappend (string, "]", 1);
    }

    hshFiterator_free (iter);

    return (string);
}


static StringDictionary *
strictdictionary_deserializeFromString (dynTstring *string)
{
    StringDictionary *dict;
    int len = dynFgetlen (string);
    const char *str_loc = dynFgetstr (string);

    if (strncmp ("<sd>", str_loc, 4) != 0)
    {
        printf ("-- not a SerializationInfo object: %s", dynFgetstr (string));

        return (NULL);
    }
    else
    {
        str_loc += 4;
    }

    dict = stringdictionary_new();

    while (str_loc != NULL && ((str_loc - dynFgetstr (string)) < len))
    {
        dynTstring *key = dynFinit();
        dynTstring *value;

        str_loc = stringdictionary_deserializeGetString (str_loc, key);

        if (str_loc == NULL)
        {
            printf ("-- wrong number of params in: %s\n", dynFgetstr (string));

            stringdictionary_destroy (dict);

            dynFfree (key);

            return (NULL);
        }

        value = dynFinit();

        str_loc = stringdictionary_deserializeGetString (str_loc, value);

        /* 
         * we're passing the value dynTstring to the SerializationInfo object,
         * so we don't need to free it ourselves in this call.
         */
        stringdictionary_addValue (dict, dynFgetstr (key), value);

        dynFfree (key);
    }

    if (str_loc == NULL)
    {
        printf ("-- error in data: %s\n", dynFgetstr (string));
        stringdictionary_destroy (dict);

        return (NULL);
    }

    return (dict);
}


