/*
 * 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 <stdio.h>
#include <string.h>
#include <time.h>
#include "iotcs.h"
#include "device_model/device_model_capability.h"
#include "iotcs_device.h"
#include "iotcs/iotcs_private.h"
#include "log/log.h"
#include "device_model/device_model_private.h"
#include "advanced/iotcs_messaging.h"
#include "messaging/msg_private.h"
#include "util/util_buffer.h"
#include "iotcs_port_system.h"
#include "policy/device_policy.h"

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

#ifndef IOTCS_POLLING_TIMEOUT_MS
#define IOTCS_POLLING_TIMEOUT_MS 5000
#endif

/* if initialized then equals 0x1; 0x0 otherwise */
static int initialized = 0x0;

#ifdef IOTCS_VIRTUALIZATION_SUPPORT
static iotcs_message_base default_base = {
    .priority = IOTCS_MESSAGE_PRIORITY_HIGH,
    .reliability = IOTCS_MESSAGE_RELIABILITY_BEST_EFFORT,
    .type = IOTCS_MESSAGE_RESPONSE
};

void cl_internal_dispatch_request(iotcs_request_message* request_message, iotcs_message* response_message) {
    iotcs_result rv;
    if (request_message == NULL || response_message == NULL) {
        return;
    }

    /* Clear the response */
    memset(response_message, 0, sizeof (iotcs_message));

    response_message->u.response.status_code = PROTOCOL_RESPONSE_CODE_OK;
    /* filter out RECONCILIATION requests */
    if (strcmp(request_message->url, "manage/resources/") == 0) {
        response_message->u.response.status_code = PROTOCOL_RESPONSE_CODE_NOT_FOUND;
    } else if (strcmp(request_message->url, "/manage/resources/") == 0) {
        response_message->u.response.status_code = PROTOCOL_RESPONSE_CODE_NOT_FOUND;
#ifdef IOTCS_IMPLICIT_EDGE_COMPUTING
    } else if (strncmp(request_message->url, "deviceModels/urn:oracle:iot:dcd:capability:device_policy/policyChanged",
                       strlen("deviceModels/urn:oracle:iot:dcd:capability:device_policy/policyChanged")) == 0) {
        dm_load_local_policy(NULL, request_message, IOTCS_TRUE);
#endif
    } else if ((rv = device_model_handle_request(request_message)) != IOTCS_RESULT_OK) {
        LOG_ERR("device_model_handle_request failed(%d): %s", rv, request_message->url);
        LOG_ERRS(request_message->body);
        response_message->u.response.status_code = PROTOCOL_RESPONSE_CODE_NOT_FOUND;
    }

    response_message->base = &default_base;
    /* link response with request.
     * request_message must be released when response is sent */
    response_message->u.response.request = request_message;
}
#endif /* #ifdef IOTCS_VIRTUALIZATION_SUPPORT */

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_DBG

static void print_library_info(void) {
    LOG_DBGS(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    LOG_DBGS("IOTCS LIBRARY CONFIGURATION:");
    LOG_DBGS("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
#ifdef IOTCS_MESSAGING_THREAD_SAFETY
    LOG_DBGS("MESSAGING_THREAD_SAFETY=true");
#else
    LOG_DBGS("MESSAGING_THREAD_SAFETY=false");
#endif
#ifdef IOTCS_MESSAGE_DISPATCHER
    LOG_DBGS("MESSAGE_DISPATCHER=true");
#else
    LOG_DBGS("MESSAGE_DISPATCHER=false");
#endif
#ifdef IOTCS_VIRTUALIZATION_SUPPORT
    LOG_DBGS("VIRTUALIZATION_SUPPORT=true");
#else
    LOG_DBGS("VIRTUALIZATION_SUPPORT=false");
#endif
#ifdef IOTCS_GATEWAY
    LOG_DBGS("GATEWAY=true");
#else
    LOG_DBGS("GATEWAY=false");
#endif
#ifdef IOTCS_USE_STATIC_HEAP
    LOG_DBGS("USE_STATIC_HEAP=true");
#else
    LOG_DBGS("USE_STATIC_HEAP=false");
#endif
#ifdef IOTCS_DISABLE_MQTT
    LOG_DBGS("DISABLE_MQTT=true");
#else
    LOG_DBGS("DISABLE_MQTT=false");
#endif
#ifdef IOTCS_DISABLE_MQTT_ACCEPTED_BYTES
    LOG_DBGS("DISABLE_MQTT_ACCEPTED_BYTES=true");
#else
    LOG_DBGS("DISABLE_MQTT_ACCEPTED_BYTES=false");
#endif
#ifdef IOTCS_DISABLE_HTTP
    LOG_DBGS("DISABLE_HTTP=true");
#else
    LOG_DBGS("DISABLE_HTTP=false");
#endif
#ifdef IOTCS_DEFAULT_TAM
    LOG_DBGS("DEFAULT_TAM=true");
#else
    LOG_DBGS("DEFAULT_TAM=false");
#endif
#ifdef IOTCS_LONG_POLLING
    LOG_DBGS("LONG_POLLING=true");
#else
    LOG_DBGS("LONG_POLLING=false");
#endif
    LOG_DBG("SHARED BUFFER SIZE %d (headers: %d, payload: %d)", UTIL_RESPONSE_BUFFER_LENGTH, UTIL_HTTP_HEADER_BUFFER_SIZE, UTIL_PAYLOAD_BUFFER_SIZE);
    LOG_DBGS("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
}
#endif

iotcs_result iotcs_init(const char* path, const char* password) {
    
    if (initialized != 0x0) {
        /* already initialized */
        LOG_ERRS("Library is already initialized. Call iotcs_client_finalize() method before initialization retry.");
        return IOTCS_RESULT_FAIL;
    }

    if (log_internal_init() != IOTCS_RESULT_OK) {
        return IOTCS_RESULT_FAIL;
    }

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_DBG
    print_library_info();
#endif

    iotcs_port_platform_init();

    if (cl_internal_init(path, password) != IOTCS_RESULT_OK) {
        log_internal_finit();
        return IOTCS_RESULT_FAIL;
    }

#ifdef IOTCS_MESSAGE_DISPATCHER
#ifndef IOTCS_VIRTUALIZATION_SUPPORT
    if (cl_internal_request_dispatcher_init() != IOTCS_RESULT_OK) {
        cl_internal_finalize();
        log_internal_finit();
        return IOTCS_RESULT_FAIL;
    }
#endif /* #ifndef IOTCS_VIRTUALIZATION_SUPPORT */
#endif

#ifdef IOTCS_VIRTUALIZATION_SUPPORT
    if (device_model_init() != IOTCS_RESULT_OK) {
        if (iotcs_is_activated()) {
            cl_internal_async_message_dispatcher_finalize();
        }
        cl_internal_finalize();
        log_internal_finit();
        return IOTCS_RESULT_FAIL;
    }
#endif /* #ifdef IOTCS_VIRTUALIZATION_SUPPORT */
    
    initialized = 0x1;

    return IOTCS_RESULT_OK;
}

void iotcs_finalize(void) {
    if (initialized != 0x1) {
        return;
    }
    LOG_INFOS("finalize library");
#if defined IOTCS_LONG_POLLING && !defined IOTCS_MESSAGE_DISPATCHER 
    cl_long_polling_receive_should_be_stopped();
#endif
    device_model_capability_finalize();
#ifdef IOTCS_MESSAGE_DISPATCHER
    if (iotcs_is_activated()) {
        cl_internal_async_message_dispatcher_finalize();
    }
    cl_internal_request_dispatcher_finalize();
#endif /* #ifdef IOTCS_MESSAGE_DISPATCHER */
    cl_internal_finalize();
#ifdef IOTCS_VIRTUALIZATION_SUPPORT
    device_model_finalize();
#endif /* #ifdef IOTCS_VIRTUALIZATION_SUPPORT */
    iotcs_port_platform_finilize();
    LOG_INFOS("finalize library DONE");
    log_internal_finit();
    initialized = 0x0;
}
