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

#include <mecha.h>

#include "serialization.h"
#include "stringdictionary.h"
#include "message.h"

struct s_Message
{
    char *command;
    int sequence;
    MessageType type;
    StringDictionary   *params;
};

static bool message_serialize (void *field, SerializationInfo *info);
static bool message_deserialize (void *field, SerializationInfo *info);

Type type_declaration_Message = { "Message", message_serialize, message_deserialize };


Message *
message_new (const char *command, MessageType type)
{
    Message *message = malloc (sizeof (Message));

    message->command = strdup (command);
    message->type = type;
    message->params = stringdictionary_new();

    return (message);
}

void
message_destroy (void *ptr)
{
    Message *message = (Message *) ptr;

    free (message->command);

    stringdictionary_destroy (message->params);

    free (message);
}

Message *
message_newRequest (const char *command)
{
    return (message_new (command, MessageTypeRequest));
}

MessageType
message_getType (Message *message)
{
    return (message->type);
}

Message *
message_makeResponse (Message *request)
{
    Message *response = message_new (message_getCommand (request), MessageTypeResponse);

    response->sequence = request->sequence;

    return (response);
}

Message *
message_makeErrorResponse (Message *request)
{
    Message *response = message_new (message_getCommand (request), MessageTypeErrorResponse);

    response->sequence = request->sequence;

    return (response);
}

int
message_getSequence (Message *message)
{
    return (message->sequence);
}

void
message_setSequence (Message *message, int sequence)
{
    message->sequence = sequence;
}

void
message_setParameter (Message *message, const char *key, const char *value)
{
    dynTstring *stringValue = dynFinit();

    dynFsappend (stringValue, value);

    stringdictionary_setValue (message->params, key, stringValue);
}

void
message_setParameterInt (Message *message, const char *key, const int value)
{
    dynTstring *stringValue = dynFinit();

    dynFappend_print (stringValue, "%d", value);

    stringdictionary_setValue (message->params, key, stringValue);
}

void
message_setParameterString (Message *message, const char *key, const dynTstring *value)
{
    dynTstring *stringValue = dynFinit();

    dynFappend_string (stringValue, value);

    stringdictionary_setValue (message->params, key, stringValue);
}

void
message_setParameterBinary (Message *message, const char *key, const char *value, int valueLen)
{
    dynTstring *stringValue = dynFinit();

    dynFappend (stringValue, value, valueLen);

    stringdictionary_setValue (message->params, key, stringValue);
}

dynTstring *
message_getParameterString (Message *message, const char *key)
{
    return (stringdictionary_getValue (message->params, key));
}

const char *
message_getParameter (Message *message, const char *key)
{
    dynTstring *stringValue = stringdictionary_getValue (message->params, key);

    if (stringValue != NULL)
    {
        return (dynFgetstr (stringValue));
    }
    else
    {
        return (NULL);
    }
}

const char *
message_getCommand (Message *message)
{
    return (message->command);
}

void
message_print (Message *message)
{
    hshTiterator *iter;

    printf (" command: %s\n", message->command);
    printf (" sequence: %d\n", message->sequence);
    printf (" type: %d\n", message->type);
    printf (" params:\n");

    iter = stringdictionary_getIterator (message->params);

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

        printf ("   %s: %s\n", key, dynFgetstr (value));
    }

    hshFiterator_free (iter);
}

static bool
message_serialize (void *field, SerializationInfo *info)
{
    Message *message = DEREFERENCE_TYPE (Message, field);

    serializationinfo_addValue (info, "command",
            serialization_serializeType ("String", &message->command));

    serializationinfo_addValue (info, "sequence",
            serialization_serializeType ("Int", &message->sequence));

    serializationinfo_addValue (info, "type",
            serialization_serializeType ("Int", &message->type));

    serializationinfo_addValue (info, "params",
            serialization_serializeType ("StringDictionary", &message->params));

    return (true);
}

static bool
message_deserialize (void *field, SerializationInfo *info)
{
    Message *message = malloc (sizeof (Message));
    Message **fieldPtr = (Message **) field;
    
    *fieldPtr = message;
    /* DEREFERENCE_TYPE (Message, field) = message; */

    serialization_deserializeType ("String", &message->command,
            serializationinfo_getValue (info, "command"));

    serialization_deserializeType ("Int", &message->sequence,
            serializationinfo_getValue (info, "sequence"));

    serialization_deserializeType ("Int", &message->type,
            serializationinfo_getValue (info, "type"));

    serialization_deserializeType ("StringDictionary", &message->params,
            serializationinfo_getValue (info, "params"));

    return (true);
}

