/*
 * Copyright (c) 2015, 2018, 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.
 */


/**
 * @brief Common private header
 */

#ifndef IOTCS_PRIVATE_H
#define    IOTCS_PRIVATE_H

#include <time.h>
#include "iotcs_device.h"
#include "advanced/iotcs_messaging.h"
#include "advanced/iotcs_message.h"
#include "protocol/protocol_request.h"
#include "device_model/device_model_private.h"
#include "util/util_buffer.h"

#ifndef IOTCS_DISABLE_MQTT
#include "protocol/mqtt/mqtt_wrapper.h"
#endif

#ifndef IOTCS_DISABLE_HTTP
#include "protocol/http/http_wrapper.h"
#endif

#ifdef    __cplusplus
extern "C" {
#endif

#ifndef IOTCS_MAX_ACCEPTED_BYTES
#define IOTCS_MAX_ACCEPTED_BYTES UTIL_RESPONSE_BUFFER_LENGTH
#endif

#define CL_RECEIVE_QUEUE_LIMIT 10

    /*
    Because length of the urn couldn't be greater then 1000 and 25 is /iot/api/v2/deviceModels
     */
#define IOTCS_DEVICE_MODEL_URL_SIZE 1025 

/*
 * This string use to mark that request was created inside CL and should not be responsed
 */
#define INTERNAL_REQUEST_SOURCE "internal request"

    static const size_t CL_MAX_STRING_LENGTH = 2048; /**< Value of the maximum possible string length. */

    typedef struct activation_policy {
        const char* key_type;
        const char* hash_algorithm;
        int64_t key_size;
    } cl_activation_policy;

    typedef struct {
        char *buf;
        int len;
    } cl_fail_string_desc;

    typedef struct {
        int64_t expires_in;
        char value[UTIL_TOKEN_STRING_MAX_LENGTH];
        int64_t created;
    } cl_access_token;

    typedef iotcs_result(*cl_proceed_request_function)(
            protocol_request request,
            protocol_response* response);

    extern cl_proceed_request_function cl_proceed_request;

    typedef void(*cl_check_min_accept_bytes_function)(protocol_response* response);

    iotcs_result cl_get_token(void);
    cl_access_token* cl_get_access_token_ptr(void);
    iotcs_result cl_serialize_messages(iotcs_message messages[], size_t count, int first, int *serialized, int *guaranted_uid, int **loaded_uids);
    iotcs_result cl_process_message_from_json_unsafe(char* json);
    iotcs_result cl_process_message_from_json(char* json);
    void cl_reset_token(void);
    void cl_get_requests_messages(void);
    void cl_put_special_request_message(iotcs_request_message *request, int32_t timeout);
    iotcs_request_message* cl_get_request_message_blocking(int32_t timeout);
    iotcs_result cl_internal_request_dispatcher_init(void);
    void cl_internal_request_dispatcher_finalize(void);
    void cl_internal_response_message_free_unsafe(iotcs_message *response);
    iotcs_result cl_internal_init();
    void cl_internal_finalize(void);
    iotcs_request_message* cl_internal_receive(int32_t timeout);
    void cl_internal_request_message_free(iotcs_request_message* message);
    void cl_internal_request_message_free_unsafe(iotcs_request_message* message);
    void cl_internal_request_dispatcher_dispatch(iotcs_request_message* request_message, iotcs_message* response_message);

#if  defined(IOTCS_MESSAGE_DISPATCHER) && (IOTCS_REQUEST_HANDLER_THREAD_POOL_SIZE > 1)

    iotcs_result cl_internal_request_handler_thread_pool_init();

    void cl_internal_request_handler_dispatcher_finalize(void);

    iotcs_result cl_internal_request_handler_pool_dispatcher_dispatch(iotcs_request_handler handler, 
                                                                      iotcs_request_message* request_message);

#endif

    iotcs_result cl_post_activation(void);

#ifdef IOTCS_MESSAGE_DISPATCHER

    typedef struct {
        float load;
        int64_t total_messages_sent;
        int64_t total_messages_received;
        int64_t total_messages_retried;
        int64_t total_bytes_sent;
        int64_t total_bytes_received;
        int64_t total_protocols_errors;
    } cl_message_dispatcher_counters;

    cl_message_dispatcher_counters* cl_message_dispatcher_counters_get(void);
    void cl_message_dispatcher_counters_reset();
    void cl_message_dispatcher_counters_add_bytes_sent(int64_t value);
    void cl_message_dispatcher_counters_add_bytes_received(int64_t value);
    int64_t cl_message_dispatcher_get_polling_interval(void);
    void cl_message_dispatcher_set_polling_interval(int64_t value);
    iotcs_bool cl_is_settle_time_passed();

    iotcs_result cl_internal_async_message_dispatcher_init(iotcs_message_dispatcher_delivery_callback delivery_cb,
            iotcs_message_dispatcher_error_callback error_cb, int polling_timeout_ms, iotcs_bool long_polling_request_required);
    void cl_internal_async_message_dispatcher_process_response_message(iotcs_request_message *request, iotcs_message* message);
    void cl_internal_async_message_dispatcher_finalize(void);
#endif

#ifdef IOTCS_VIRTUALIZATION_SUPPORT
    void cl_internal_dispatch_request(iotcs_request_message* request_message, iotcs_message* response_message);
#endif

    iotcs_result cl_post_messages(const char* json_string, char** response_buf, cl_fail_string_desc *fail_reason);

    const char* cl_get_server_host();

    iotcs_result cl_internal_send_unsafe(iotcs_message messages[], int count, int* posted, cl_fail_string_desc *fail_reason);
    iotcs_result cl_internal_send(iotcs_message messages[], int count, int* posted, cl_fail_string_desc *fail_reason);

    iotcs_result cl_get_activation_policy(cl_activation_policy* policy);

#ifdef IOTCS_LONG_POLLING
#ifndef IOTCS_LP_BIG_TIMEOUT_MS
#define IOTCS_LP_BIG_TIMEOUT_MS 500000
#endif

#ifndef IOTCS_LP_SMALL_TIMEOUT_MS
#define IOTCS_LP_SMALL_TIMEOUT_MS 100
#endif

#ifndef IOTCS_LP_TIMEOUT_OFFSET_MS
#define IOTCS_LP_TIMEOUT_OFFSET_MS 100
#endif

    void cl_long_polling_receive_should_be_stopped();
    iotcs_bool cl_is_long_polling_receive_should_be_stopped();
    void cl_long_polling_receive_should_be_interrupted();
    iotcs_bool cl_is_long_polling_receive_should_be_interrupted();
    iotcs_result cl_post_long_polling_request(char** response_buf, cl_fail_string_desc *fail_reason, int32_t timeout_ms);
#endif

#ifdef    __cplusplus
}
#endif

#endif    /* IOTCS_PRIVATE_H */

