/*
 * 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 "json/json_helper.h"
#include "advanced/iotcs_message.h"
#include "messaging/msg_private.h"
#include "util/util_resource_private.h"
#include "iotcs_port_crypto.h"
#include "util/util.h"
#include "util/util_memory.h"
#include "trusted_assets_manager/iotcs_tam.h"

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

// see iotcs_resource_message_type
static const char* CL_RD_MESSAGE_TYPE_STRING[] = {
    "UPDATE", // 0
    "DELETE", // 1
    "RECONCILIATION" // 2
};

static int methods_to_string(json_buf *buf, int pos, iotcs_request_method methods) {
    int rv;
    int original_pos = pos;
    if (methods) {
        if (methods & PROTOCOL_REQUEST_METHOD_DELETE) {
            rv = json_write_string_escaped(buf, pos, protocol_request_methods_to_string(PROTOCOL_REQUEST_METHOD_DELETE));
            JSON_MOVE_POS(pos, rv);
            JSON_PUT_SYMBOL(buf, pos, ',');
        }
        if (methods & PROTOCOL_REQUEST_METHOD_GET) {
            rv = json_write_string_escaped(buf, pos, protocol_request_methods_to_string(PROTOCOL_REQUEST_METHOD_GET));
            JSON_MOVE_POS(pos, rv);
            JSON_PUT_SYMBOL(buf, pos, ',');
        }
        if (methods & PROTOCOL_REQUEST_METHOD_HEAD) {
            rv = json_write_string_escaped(buf, pos, protocol_request_methods_to_string(PROTOCOL_REQUEST_METHOD_HEAD));
            JSON_MOVE_POS(pos, rv);
            JSON_PUT_SYMBOL(buf, pos, ',');
        }
        if (methods & PROTOCOL_REQUEST_METHOD_POST) {
            rv = json_write_string_escaped(buf, pos, protocol_request_methods_to_string(PROTOCOL_REQUEST_METHOD_POST));
            JSON_MOVE_POS(pos, rv);
            JSON_PUT_SYMBOL(buf, pos, ',');
        }
        if (methods & PROTOCOL_REQUEST_METHOD_PUT) {
            rv = json_write_string_escaped(buf, pos, protocol_request_methods_to_string(PROTOCOL_REQUEST_METHOD_PUT));
            JSON_MOVE_POS(pos, rv);
            JSON_PUT_SYMBOL(buf, pos, ',');
        }
        --pos; /* remove last comma */
    }

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

static int resource_toJSON_callback(json_buf *buf, int pos, void *data) {
    int rv;
    int original_pos = pos;
    const iotcs_resource_message_base* resource = (const iotcs_resource_message_base*) data;


    rv = json_write_string_field(buf, pos, "name", resource->name);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "path", resource->path);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "description", resource->description);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string(buf, pos, "methods");
    JSON_MOVE_POS(pos, rv);

    JSON_PUT_SYMBOL(buf, pos, ':');

    JSON_PUT_SYMBOL(buf, pos, '"');
    rv = methods_to_string(buf, pos, resource->methods);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, '"');

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

static int resource_message_resources_callback(json_buf *buf, int pos, void *data) {
    int rv, i;
    int original_pos = pos;
    const iotcs_resource_message* resource_msg = (const iotcs_resource_message*) data;

    if (resource_msg->resource_len == 0) {
        return -IOTCS_RESULT_INVALID_ARGUMENT;
    }

    for (i = 0; i < resource_msg->resource_len; ++i) {
        rv = json_write_object(buf, pos, resource_toJSON_callback, (void*) &resource_msg->base[i]);
        JSON_MOVE_POS(pos, rv);
        JSON_PUT_SYMBOL(buf, pos, ',');
    }

    --pos; /* remove last comma */

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

static int resource_message_value_callback(json_buf *buf, int pos, void *data) {
    int rv;
    char resource_md5[IOTCS_MD5_DIGEST_LENGTH * 2 + 1];
    int original_pos = pos;
    const iotcs_resource_message* resource_msg = (const iotcs_resource_message*) data;

    rv = json_write_string_field(buf, pos, "reportType", CL_RD_MESSAGE_TYPE_STRING[resource_msg->report_type]);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "endpointName", resource_msg->endpointName && strlen(resource_msg->endpointName) > 0 ? resource_msg->endpointName : tam_get_endpoint_id());
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');
    
    GOTO_ERR(IOTCS_RESULT_OK != util_resources_get_md5(resource_md5, resource_msg->base, resource_msg->resource_len));
  
    rv = json_write_string_field(buf, pos, "reconciliationMark", resource_md5);
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_string_field(buf, pos, "headers", "");
    JSON_MOVE_POS(pos, rv);
    JSON_PUT_SYMBOL(buf, pos, ',');

    rv = json_write_array_field(buf, pos, "resources", resource_message_resources_callback, (void*) resource_msg);
    JSON_MOVE_POS(pos, rv);

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

int msg_resource_toJSON(json_buf *buf, int pos, void *data) {
    int rv;
    iotcs_message* message = (iotcs_message*) data;
    int original_pos = pos;

    if (!message || message->base->type != IOTCS_MESSAGE_RESOURCE) {
        return -IOTCS_RESULT_INVALID_ARGUMENT;
    }

    rv = json_write_object_field(buf, pos, "value", resource_message_value_callback, &message->u.resource);
    JSON_MOVE_POS(pos, rv);

    return pos - original_pos;
}

void msg_resource_delete(iotcs_message* message) {
    iotcs_resource_message* message_t = (iotcs_resource_message*) message;
    if (!message_t) {
        return;
    }

    util_free(message_t);
    return;
}