/*
 * Copyright (c) 2017, 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_storage_object.h
 * @brief Methods for storage object
 */

#ifndef IOTCS_STORAGE_OBJECT_H_
#define IOTCS_STORAGE_OBJECT_H_

#include "iotcs.h"
#include "advanced/iotcs_messaging.h"

#ifdef __cplusplus
extern "C" {
#endif

    /**
     * @typedef iotcs_storage_dispatcher_progress_state
     * @brief Provides progress of sync
     */
    typedef enum iotcs_storage_dispatcher_progress_state_t {
        IOTCS_CANCELED = 0, /**< Up/download was canceled before it completed */
        IOTCS_COMPLETED = 1, /**< Up/download completed successfully */
        IOTCS_FAILED = 2, /**< Up/download failed without completing */
        IOTCS_IN_PROGRESS = 3, /**< Up/download is currently in progress */
        IOTCS_INITIATED = 4, /**< Initial state after upload() or download() is called */
        IOTCS_QUEUED = 5, /**< Up/download is queued and not yet started */
    } iotcs_storage_dispatcher_progress_state;

    /**
     * @typedef iotcs_storage_dispatcher_progress_callback
     *
     * @brief Callback to receive the result of an upload or download.
     *
     * @param storage pointer to #iotcs_storage_object
     * @param current dispathcer state #iotcs_storage_dispatcher_progress_state 
     * @param transfered_bytes amount of bytes read or written
     * @param fail_reason string with fail reason or null on success
     */
    typedef void(*iotcs_storage_dispatcher_progress_callback)(iotcs_storage_object *storage_object,
            iotcs_storage_dispatcher_progress_state state,
            int64_t bytes_transfered, const char *fail_reason);

    /**
     * @brief Create a new #iotcs_storage_object wthat will have a name with the given
     * object name prefixed with the device's endpoint ID and a directory
     * separator. The prefix addition can be disabled by setting the
     * DISABLE_STORAGE_OBJECT_PREFIX to true. Storage object is mutable by default.
     *
     * @param name Name of the attribute.
     * @param content_type Type of the content.
     * @param storage_object_handle pointer to receive a storage_object. The storage will be allocated .
     *     by the library and user must release it by calling iotcs_release_storage_object(storage).
     *     Some fields could be empty or null in case if they weren't set before and there is no default value.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if name is NULL.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_create_storage_object(const char* name, const char* content_type,
            iotcs_storage_object **storage_object);

    /**
     * @brief Create a new #iotcs_storage_object wthat will have a name with the given
     * object name prefixed with the device's endpoint ID and a directory
     * separator. The prefix addition can be disabled by setting the
     * DISABLE_STORAGE_OBJECT_PREFIX to true
     *
     * @param name Name of the attribute.
     * @param content_type Type of the content.
     * @param is_mutable mutable {@code IOTCS_TRUE} means mutable, {@code IOTCS_FALSE} means immutable
     *        If {@code mutable} = IOTCS_TRUE, then the StorageObject will be made immutable by setting
     *        it’s Write-Once-Read-Many (WORM) policy when uploading it to the container to prevent the users
     *        from deleting it for a 7 days and modifying it.
     *        Number of days is 7 by default and can be overridden by setting property
     *        compilation property IOTCS_WORM_DAYS. If the property is "0" then no WORM policy will be applied.
     * @param storage_object_handle pointer to receive a storage_object. The storage will be allocated .
     *     by the library and user must release it by calling iotcs_release_storage_object(storage).
     *     Some fields could be empty or null in case if they weren't set before and there is no default value.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if name is NULL.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_create_storage_object_ext(const char* name, const char* content_type,
            iotcs_bool is_mutable, iotcs_storage_object **storage_object);

    /**
     * @brief Set the time to live for Storage Object in seconds
     * Time to live will be set to the Storage Object while file uploading.
     *
     * @param object pointer to receive a storage_object. The storage will be allocated .
     *     by the library and user must release it by calling iotcs_release_storage_object_handle(storage_object_handle).
     *     Some fields could be empty or null in case if they weren't set before and there is no default value.
     * @param ttl  Time to live in unit
     * 
     * @return IOTCS_RESULT_INVALID_ARGUMENT if timeToLive less than, equal to 0
     * @return IOTCS_RESULT_INVALID_ARGUMENT IllegalStateException if storage object is pending
     * @return IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_storage_object_set_ttl(iotcs_storage_object* storage_object, int ttl);
    
    /**
     * Sets the metadata for the StorageObject. All metdata will be added to Storage Cloud Service as custom metadata
     * with the "X-Object-Meta-KeyName" header.
     * @param object pointer to receive a storage_object. The storage will be allocated .
     *     by the library and user must release it by calling iotcs_release_storage_object_handle(storage_object_handle).
     *     Some fields could be empty or null in case if they weren't set before and there is no default value.
     * @param key The metadata key
     * @param value The metadata value
     *
     * @return IOTCS_RESULT_INVALID_ARGUMENT if the key or value is empty
     * @return IOTCS_RESULT_INVALID_ARGUMENT if the key or value is {@code NULL}
     * @return IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_storage_object_set_custom_metadata(iotcs_storage_object* storage_object,
        const char* name, const char* value);

#if !defined IOTCS_DOXYGEN
    /**
     * @brief Create a new #iotcs_external_object with the given URI.
     * @param uri Link.
     * @param external_object pointer to receive a external_object.
     *     Users must release it by calling iotcs_free_external_object(external_object).
     * @retval IOTCS_RESULT_OUT_OF_MEMORY  if there is no memory available.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if given uri is empty or NULL.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_create_external_object(const char* uri,
            iotcs_external_object **external_object);
#endif

    /**
     * @brief Create a new #iotcs_storage_object from the URI for a named object in storage_object.
     * @param uri the URI of the object in the storage cloud.
     * @param storage_object_handle pointer to receive a storage_object. The storage will be allocated .
     *     by the library and use must release it by calling iotcs_release_storage_object(storage).
     *     Some fields could be empty or null in case if they weren't set before and there is no default value.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if uri is NULL or doesn't correct URI
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_create_storage_object_by_uri(const char* uri, iotcs_storage_object **storage_object);

    /**
     * @brief Release Storage Object created by library.
     * @param storage pointer to #iotcs_storage_object 
     */
    void iotcs_free_storage_object(iotcs_storage_object *storage_object);

#if !defined IOTCS_DOXYGEN
    /**
     * @brief Release External Object created by library.
     * @param external_object Pointer to external object #iotcs_external_object
     */
    void iotcs_free_external_object(iotcs_external_object *external_object);
#endif

    /**
     * @brief Cancel syncronization of storage object
     * @param storage pointer to #iotcs_storage_object
     */
    void iotcs_storage_object_cancel(iotcs_storage_object *storage_object);

#if !defined IOTCS_VIRTUAL_STORAGE_OBJECT_H_ || defined IOTCS_DOXYGEN

#if !defined IOTCS_DOXYGEN
    /**
     * @brief Return resource URI
     * @param external_object Pointer to external object #iotcs_external_object
     * @retval URI - link to an object
     * @retval NULL, if it was not set
     */
    const char* iotcs_external_object_get_uri(const iotcs_external_object *external_object);
#endif

    /**
     * @brief Set input file for content to be uploaded.
     * @param storage pointer to #iotcs_storage_object 
     * @param input_path Path to the file.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if given input_path or storage_object are NULL.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_storage_object_set_input_path(iotcs_storage_object *storage_object, const char *input_path);

    /**
     * @brief Set output file for content to be downloaded.
     * @param storage pointer to #iotcs_storage_object 
     * @param callback Callback for notifications about synchronization
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if given output_path or storage_object are NULL.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if storage_object is syncing.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_storage_object_set_output_path(iotcs_storage_object *storage_object, const char *output_path);

    /**
     * @brief Return input path if it was set
     * @param storage pointer to #iotcs_storage_object 
     * @retval Input path
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_input_path(const iotcs_storage_object *storage_object);

    /**
     * @brief Return output path if it was set
     * @param storage pointer to #iotcs_storage_object 
     * @retval Output path
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_output_path(const iotcs_storage_object *storage_object);

    /**
     * @brief Return name of storage object
     * @param storage pointer to #iotcs_storage_object 
     * @retval Name
     */
    const char* iotcs_storage_object_get_name(const iotcs_storage_object *storage_object);

    /**
     * @brief Return resource URI
     * @param storage pointer to #iotcs_storage_object 
     * @retval URI
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_uri(const iotcs_storage_object *storage_object);

    /**
     * @brief IoT CS secure file synchronous sync method.
     * 
     * Synchronizes storage content with the storage cloud. E.g. uploading or
     * downloading if required.
     *
     * @param storage pointer to #iotcs_storage_object
     *
     * @note This procedure should not be called from the callback 
     * implementation due to the possible deadlock on waiting for the message
     * sending status from the current thread.
     *
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if storage_object is NULL.
     * @retval IOTCS_RESULT_OK if succeeds.
     * @retval IOTCS_RESULT_CANNOT_AUTHORIZE if there are problems with authorization.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_FAIL otherwise.
     */
    iotcs_result iotcs_storage_object_sync(iotcs_storage_object* storage_object);

    /**
     * @brief Get the length of the content in bytes.
     * @param storage pointer to #iotcs_storage_object 
     * @retval Size in bytes
     * @retval 0, if it was not gotten
     */
    long iotcs_storage_object_get_length(const iotcs_storage_object* storage_object);

    /**
     * @brief Get the mime-type of the content.
     * @param storage pointer to #iotcs_storage_object 
     * @retval Type
     * @retval "application/octet-stream", if it was not set
     */
    const char*  iotcs_storage_object_get_type(const iotcs_storage_object* storage_object);

    /**
     * @brief Get the encoding scheme of the content
     * @param storage pointer to #iotcs_storage_object 
     * @retval Encoding
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_encoding(const iotcs_storage_object* storage_object);

    /**
     * @brief Get the date and time the content was created or last modified in cloud storage
     * @param storage pointer to #iotcs_storage_object 
     * @retval number of milliseconds that have elapsed since January 1, 1970 (midnight UTC/GMT)
     * @retval 0, if it was not defined
     */
    iotcs_date_time iotcs_storage_object_get_date(const iotcs_storage_object* storage_object);
#endif

    /**
     * @brief IoT CS secure file asynchronous sync method.
     *
     * Synchronizes storage content with the storage cloud. E.g. uploading or
     * downloading if required.
     *
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if storage_object is NULL.
     * @retval IOTCS_RESULT_OK if succeeds.
     * @retval IOTCS_RESULT_OUT_OF_MEMORY if there is no memory available.
     * @retval IOTCS_RESULT_FAIL otherwise.
     */
    iotcs_result iotcs_storage_dispatcher_queue(iotcs_storage_object* storage_object);

    /**
     * @brief Set a callback to be notified as the transfer progresses.
     *
     * @param callback - callback to invoke, if null, the existing callback will be removed
     *        #iotcs_storage_dispatcher_progress_callback
     */
    void iotcs_storage_dispatcher_set_callback(iotcs_storage_dispatcher_progress_callback callback);
#ifdef __cplusplus
}
#endif

#endif /* IOTCS_STORAGE_OBJECT_H_ */
