/*
 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL). See the LICENSE file in the root
 * directory for license terms. You may choose either license, or both.
 */

#include <string.h>
#include <time.h>
#include <stdio.h>
#include "advanced/iotcs_message.h"
#include "msg_private.h"
#include "json/json_helper.h"
#include "json/json_writer.h"
#include "trusted_assets_manager/iotcs_tam.h"
#include "util/util.h"
#include "iotcs_port_system.h"

#include "log/log.h"
#define IOTCSP_MODULE_LOG_CHANNEL LOG_CHANNEL_MSG
#include "log/log_template.h"

// see iotcs_message_priority
static const char* CL_MESSAGE_PRIORITY_STRING[] = {
    "LOWEST", // 0
    "LOW", // 1
    "MEDIUM", // 2
    "HIGH", // 3
    "HIGHEST" // 4
};

// see iotcs_message_reliability
static const char* CL_MESSAGE_RELIABILITY_STRING[] = {
    "NO_GUARANTEE", // 0
    "BEST_EFFORT", // 1
    "GUARANTEED_DELIVERY" // 2
};

// see iotcs_message_type
static const char* CL_MESSAGE_TYPE_STRING[] = {
    "DATA", // 0 Data message (attribute)
    "ALERT", // 1
    "REQUEST", // 2
    "RESPONSE", // 3
    "RESOURCES_REPORT", // 4
    "DATA", //5 Custom data message (format)
};

static int (*toJSON[])(json_buf *, int, void*) = {
    msg_data_toJSON, //data message (attribute)
    msg_alert_toJSON,
    msg_request_toJSON,
    msg_response_toJSON,
    msg_resource_toJSON,
    msg_data_toJSON //custom data message (format)
};

static int propertie_values_callback(json_buf *buf, int pos, void *data) {
    int rv;
    int original_pos = pos;
    const char * const *prop_values = (const char * const *) data;

    if (NULL == prop_values) {
        return 0;
    }

    if (*prop_values) {
        rv = json_write_string(buf, pos, *prop_values++);
        JSON_MOVE_POS(pos, rv);
        while (*prop_values) {
            JSON_PUT_SYMBOL(buf, pos, ',');
            rv = json_write_string(buf, pos, *prop_values++);
            JSON_MOVE_POS(pos, rv);
        }
    }
    return pos - original_pos;
error:
    return -IOTCS_RESULT_OUT_OF_MEMORY;
}

static int properties_callback(json_buf *buf, int pos, void *data) {
    int rv;
    int original_pos = pos;
    const iotcs_message_property *props = (const iotcs_message_property *) data;

    while (props->key) {
        rv = json_write_array_field(buf, pos, props->key, propertie_values_callback, (void*) props);
        JSON_MOVE_POS(pos, rv);
        props++;
    }
    return pos - original_pos;
}

static int diagnostic_callback(json_buf *buf, int pos, void *data) {
    int rv;
    int original_pos = pos;
    const iotcs_message_diagnostic *diag = (const iotcs_message_diagnostic *) data;

    while (diag->key) {
        rv = json_write_iotcs_value_field(buf, pos, diag->key, diag->value, diag->type);
        JSON_MOVE_POS(pos, rv);
        diag++;
    }
    return pos - original_pos;
}

static int write_message_callback(json_buf *buf, int pos, void *data) {
    int rv;
    int original_pos = pos;
    iotcs_message *message = (iotcs_message*) data;
    const char *tmp;
    rv = json_write_string_field(buf, pos, "id", message->id);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    tmp = message->base->source;
    rv = json_write_string_field(buf, pos, "source", tmp ? tmp : tam_get_endpoint_id());
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    tmp = message->base->sender;
    rv = json_write_string_field(buf, pos, "sender", tmp ? tmp : tam_get_endpoint_id());
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    tmp = message->base->destination;
    rv = json_write_string_field(buf, pos, "destination", tmp);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    {
        int64_t event_time = message->event_time;
        if (event_time == 0) {
            event_time = iotcs_port_get_current_time_millis();
        }
        rv = json_write_int_field(buf, pos, "eventTime", event_time);
        JSON_MOVE_POS(pos, rv);
        JSON_PUT_SYMBOL(buf, pos, ',');
    }
    rv = json_write_string_field(buf, pos, "priority", CL_MESSAGE_PRIORITY_STRING[message->base->priority]);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "reliability", CL_MESSAGE_RELIABILITY_STRING[message->base->reliability]);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "type", CL_MESSAGE_TYPE_STRING[message->base->type]);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    if (message->properties) {
        rv = json_write_object_field(buf, pos, "properties", properties_callback, (void*) message->properties);
        JSON_MOVE_POS(pos, rv);
        JSON_PUT_SYMBOL(buf, pos, ',');
    }

    if (message->diagnostics) {
        rv = json_write_object_field(buf, pos, "diagnostics", diagnostic_callback, (void*) message->diagnostics);
        JSON_MOVE_POS(pos, rv);
        JSON_PUT_SYMBOL(buf, pos, ',');
    }
    rv = json_write_object_field(buf, pos, "payload", toJSON[message->base->type], message);
    JSON_MOVE_POS(pos, rv);

    return pos - original_pos;
error:
    return -IOTCS_RESULT_OUT_OF_MEMORY;
}

int msg_toJSON(iotcs_message* message, char* buffer, int buffer_length) {
    json_buf buffer_desc = {buffer, buffer_length};
    return json_write_object_null_terminated(&buffer_desc, 0, write_message_callback, message);
}

void msg_delete(iotcs_message* message) {
    if (!message) {
        return;
    }

    if (message->base->type == IOTCS_MESSAGE_RESOURCE) {
        msg_resource_delete(message);
        return;
    }
}