/*
 * 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.
 */

/**
 * @file iotcs_port_queue.h
 * @brief The file contains porting layer for implementation of the blocking queue.
 */

#ifndef IOTCS_PORT_QUEUE_H
#define IOTCS_PORT_QUEUE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include "iotcs.h"
#include "advanced/iotcs_message.h"

    /**
     * Blocking queue that contains iotcs_message elements
     * Queue size is build time constant IOTCS_PORT_MESSAGE_QUEUE_SIZE which is
     * number of elements not a bytes count.
     */
    typedef void* iotcs_port_message_queue;

#ifndef IOTCS_PORT_MESSAGE_QUEUE_SIZE
#define IOTCS_PORT_MESSAGE_QUEUE_SIZE 16
#endif

#ifndef IOTCS_PORT_REQUEST_QUEUE_SIZE
#define IOTCS_PORT_REQUEST_QUEUE_SIZE 8
#endif

#ifdef IOTCS_STORAGE_SUPPORT
/**
 * iotcs_storage_request instance describes file upload or download operation.
 */
typedef struct iotcs_storage_request_t {
    struct iotcs_uri_object_t* storage; /** Storage contains location and description of a file in cloud storage*/
} iotcs_storage_request;
#ifndef IOTCS_PORT_MEDIA_STORAGE_REQUEST_QUEUE_SIZE
#define IOTCS_PORT_MEDIA_STORAGE_REQUEST_QUEUE_SIZE 8
#endif
#endif

    /**
     * @brief Creates queue
     * Queue size shell be IOTCS_PORT_MESSAGE_QUEUE_SIZE elements. Element's
     * size is \a sizeof(iotcs_message) bytes. If IOTCS_MESSAGING_THREAD_SAFETY
     * is defined then queue shell be blocking. That means
     * iotcs_port_message_queue_put and iotcs_port_message_queue_get calls
     * shall be thread-safe. Also these calls shall block if operation can't be
     * done (no free space or no messages) and use \a timeout_ms argument as a
     * maximum blocking time.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @return NULL in case of fail
     */
    iotcs_port_message_queue iotcs_port_message_queue_create();

    /**
     * @brief Destroys iotcs_port_message_queue
     * If a \a queue is NULL, no operation is performed.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @param queue a iotcs_port_message_queue handle
     */
    void iotcs_port_message_queue_destroy(iotcs_port_message_queue queue);

    /**
     * @brief Copies iotcs_message structure to the queue
     * The whole iotcs_message structure pointed to by \a message is copied into
     * the queue not just a pointer.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is full then the call shall block waiting for
     * free space using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @param queue a non NULL queue handle
     * @param message an address of iotcs_message structure to copy into the queue
     * @param timeout_ms a timeout in milliseconds. If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_message_queue_put(iotcs_port_message_queue queue, iotcs_message *message, int32_t timeout_ms);

    /**
     * @brief Copies iotcs_message structure from the queue to given address
     * The whole iotcs_message from queue is copied into structure pointed to by
     * \a message not just a pointer.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is empty then the call shall block waiting for
     * a message using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @param queue a non NULL queue handle
     * @param message an address of iotcs_message structure to copy from the queue
     * @param timeout_ms a timeout in milliseconds.If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_message_queue_get(iotcs_port_message_queue queue, iotcs_message *message /* OUT */, int32_t timeout_ms);

    /**
     * @brief Returns quantity of items in iotcs_port_message_queue
     * Returned value is used to calculate message queue load factor.
     * Calculated load factor is sent to IoT server in a response to \a counters
     * request of \a urn:oracle:iot:dcd:capability:message_dispatcher device
     * model as a value of \a load property.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @param queue a iotcs_port_message_queue handle
     * @return a quantity of items in iotcs_port_message_queue
     */
    int iotcs_port_message_queue_count(iotcs_port_message_queue queue);

    /**
     * Blocking queue that contains pointers to iotcs_request_message.
     * Queue size is build time constant IOTCS_PORT_REQUEST_QUEUE_SIZE which is
     * number of elements not a bytes count.
     */
    typedef void* iotcs_port_request_queue;

    /**
     * Blocking queue that contains pointers to iotcs_request_handler_message.
     * Queue size is build time constant IOTCS_REQUEST_HANDLER_THREAD_POOL_SIZE which is
     * number of elements not a bytes count.
     */
    typedef void* iotcs_port_request_handler_queue;

    /**
     * @brief Creates queue
     * Queue size shell be IOTCS_PORT_REQUEST_QUEUE_SIZE elements. Element's
     * size is \a sizeof(iotcs_request_message*) bytes.
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then queue shell be blocking.
     * That means iotcs_port_request_queue_put and iotcs_port_request_queue_get
     * calls shall be thread-safe. Also these calls shall block if operation
     * can't be done (no free space or no messages) and use \a timeout_ms
     * argument as a maximum blocking time.
     * @note Mandatory API. Called by the Library in any configuration.
     * @return NULL in case of fail
     */
    iotcs_port_request_queue iotcs_port_request_queue_create();

    /**
     * @brief Destroys iotcs_port_request_queue
     * If a \a queue is NULL, no operation is performed.
     * @note Mandatory API. Called by the Library in any configuration.
     * @param queue a iotcs_port_request_queue handle
     */
    void iotcs_port_request_queue_destroy(iotcs_port_request_queue queue);

    /**
     * @brief Writes address of iotcs_request_message structure to the queue
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is full then the call shall block waiting for
     * free space using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Mandatory API. Called by the Library in any configuration.
     * @param queue a non NULL queue handle
     * @param request an address of iotcs_request_message structure to put to the queue
     * @param timeout_ms a timeout in milliseconds. If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_request_queue_put(iotcs_port_request_queue queue, iotcs_request_message *request, int32_t timeout_ms);

    /**
     * @brief Gets iotcs_request_message address from the queue to given address
     * If queue is not empty then call dequeue iotcs_request_message pointer
     * from the queue and writes this pointer to \a *request address.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is empty then the call shall block waiting for
     * a message using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Mandatory API. Called by the Library in any configuration.
     * @param queue a non NULL queue handle
     * @param request an address of pointer to iotcs_request_message structure to return
     * @param timeout_ms a timeout in milliseconds.If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_request_queue_get(iotcs_port_request_queue queue, iotcs_request_message **request /* OUT */, int32_t timeout_ms);


    /**
     * @brief Returns quantity of items in iotcs_port_request_queue
     * Returned value is used to calculate request queue load factor.
     * @note Optional API. Called by the Library if IOTCS_MESSAGE_DISPATCHER option is defined.
     * @param queue a iotcs_port_request_queue handle
     * @return a quantity of items in iotcs_port_request_queue
     */
    int iotcs_port_request_queue_count(iotcs_port_request_queue queue);

#if defined IOTCS_STORAGE_SUPPORT || defined IOTCS_DOXYGEN   

    /**
     * Blocking queue that contains pointers to iotcs_storage_request.
     * Queue size is build time constant IOTCS_PORT_MEDIA_STORAGE_REQUEST_QUEUE_SIZE which is
     * number of elements not a bytes count.
     */
    typedef void* iotcs_port_storage_request_queue;

    /**
     * @brief Creates queue
     * Queue size shell be IOTCS_PORT_MEDIA_STORAGE_REQUEST_QUEUE_SIZE elements. Element's
     * size is \a sizeof(iotcs_storage_request*) bytes.
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then queue shell be blocking.
     * That means iotcs_port_storage_request_queue_put and iotcs_port_storage_request_queue_get
     * calls shall be thread-safe. Also these calls shall block if operation
     * can't be done (no free space or no messages) and use \a timeout_ms
     * argument as a maximum blocking time.
     * @note Optional API. Called by the Library when STORAGE_SUPPORT enabled.
     * @return NULL in case of fail
     */
    iotcs_port_request_queue iotcs_port_storage_request_queue_create();

    /**
     * @brief Destroys iotcs_port_storage_request_queue
     * If a \a queue is NULL, no operation is performed.
     * @note Optional API. Called by the Library when STORAGE_SUPPORT enabled.
     * @param queue a iotcs_port_storage_request_queue handle
     */
    void iotcs_port_storage_request_queue_destroy(iotcs_port_storage_request_queue queue);

    /**
     * @brief Writes address of iotcs_storage_request structure to the queue
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is full then the call shall block waiting for
     * free space using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Optional API. Called by the Library when STORAGE_SUPPORT enabled.
     * @param queue a non NULL queue handle
     * @param request an address of iotcs_storage_request structure to put to the queue
     * @param timeout_ms a timeout in milliseconds. If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_storage_request_queue_put(iotcs_port_storage_request_queue queue, iotcs_storage_request *request, int32_t timeout_ms);

    /**
     * @brief Gets iotcs_storage_object_handle address from the queue to given address
     * If queue is not empty then call dequeue iotcs_storage_request pointer
     * from the queue and writes this pointer to \a *request address.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is defined then the call shall be
     * thread-safe. Also if queue is empty then the call shall block waiting for
     * a message using \a timeout_ms argument as a maximum blocking time.
     * If timeout is expired then error code \a IOTCS_RESULT_FAIL must be
     * returned.
     * 
     * If IOTCS_MESSAGING_THREAD_SAFETY is NOT defined then \a timeout_ms
     * argument shell be ignored treating it like a zero timeout.
     * @note Optional API. Called by the Library when STORAGE_SUPPORT enabled.
     * @param queue a non NULL queue handle
     * @param request an address of pointer to iotcs_storage_request structure to return
     * @param timeout_ms a timeout in milliseconds.If it is equal to 0 call
     * shell not block. Negative number means infinite timeout.
     * @return IOTCS_RESULT_OK success
     * @return IOTCS_RESULT_FAIL timeout expired
     * @return IOTCS_RESULT_OUT_OF_MEMORY failed to allocate dynamic memory
     */
    iotcs_result iotcs_port_storage_request_queue_get(iotcs_port_storage_request_queue queue, iotcs_storage_request *request /* OUT */, int32_t timeout_ms);
#endif
    
#if  defined(IOTCS_MESSAGE_DISPATCHER) && (IOTCS_REQUEST_HANDLER_THREAD_POOL_SIZE > 1)

    typedef void (*iotcs_request_handler) (iotcs_request_message* message, iotcs_message* response_message);

    typedef struct iotcs_request_handler_message_t {
        iotcs_request_handler handler;
        iotcs_request_message* request_message;
    } iotcs_request_handler_message;

    iotcs_port_request_handler_queue iotcs_port_request_handler_queue_create();

    void iotcs_port_request_handler_queue_destroy(iotcs_port_request_handler_queue queue);

    iotcs_result iotcs_port_request_handler_queue_put(iotcs_port_request_handler_queue queue, iotcs_request_handler_message *request, int32_t timeout_ms);

    iotcs_result iotcs_port_request_handler_queue_get(iotcs_port_request_handler_queue queue, iotcs_request_handler_message *request /* OUT */, int32_t timeout_ms);

    int iotcs_port_request_handler_queue_count(iotcs_port_request_handler_queue queue);

#endif

#ifdef __cplusplus
}
#endif

#endif /* IOTCS_PORT_QUEUE_H */

