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

#ifndef IOTCS_VIRTUAL_STORAGE_OBJECT_H_
#define IOTCS_VIRTUAL_STORAGE_OBJECT_H_

#include <stdint.h>

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

#ifdef __cplusplus
extern "C" {
#endif

    /**
     * @typedef iotcs_storage_object_sync_status
     * @brief Provides status of sync
     */
    typedef enum iotcs_storage_object_sync_status_t{
        IOTCS_IN_SYNC = 0, /**< The content is in sync with the storage cloud */
        IOTCS_NOT_IN_SYNC = 1, /**< The content is not in sync with the storage cloud */
        IOTCS_SYNC_FAILED = 2, /**< The content is not in sync with the storage cloud because the upload or download failed */
        IOTCS_SYNC_PENDING = 3, /**< The content is not in sync with the storage cloud, but sync is pending */
    } iotcs_storage_object_sync_status;

    /**
     * @typedef iotcs_storage_object_sync_event_t
     * @brief Change event
     */
    typedef struct iotcs_storage_object_sync_event_t {
        const char *name; /**< The name of the attribute, action, or format that this event is associated with */
        iotcs_storage_object_handle source; /**< Storage handle */
        iotcs_virtual_device_handle virtual_device; /**< The device generated this event */
    } iotcs_storage_object_sync_event;
    /**< Typedef for #iotcs_storage_object_sync_event_t structure */

    /**
     * @typedef iotcs_storage_on_sync_callback
     * @brief Callback to receive the result of the synchronization.
     * @param event instance of iotcs_storage_object_sync_event
     */
    typedef void(*iotcs_storage_object_sync_callback)(iotcs_storage_object_sync_event *event);

    /**
     * @brief Create a new #iotcs_storage_object_handle that 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_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.
     * @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_handle(const char* name, const char* content_type,
        iotcs_storage_object_handle *storage_object_handle);

    /**
     * @brief Create a new #iotcs_storage_object_handle that 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_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.
     * @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_handle(const char* name, const char* content_type,
        iotcs_bool is_mutable, iotcs_storage_object_handle *storage_object_handle);

    /**
     * @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_handle storage_object_handle, const 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_handle storage_object_handle,
        const char* name, const char* value);

#if !defined IOTCS_DOXYGEN
    /**
     * @brief Create a new #iotcs_external_object_handle 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_handle(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_handle(const char* uri,
            iotcs_external_object_handle *external_object_handle);
#endif

    /**
     * @brief Release Storage Object Attribute created by library.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     */
    void iotcs_free_storage_object_handle(iotcs_storage_object_handle storage_object_handle);

#if !defined IOTCS_DOXYGEN
    /**
     * @brief Release External Object.
     * @param external_object External object #iotcs_external_object_handle
     */
    void iotcs_free_external_object_handle(iotcs_external_object_handle external_object_handle);
#endif

#if !defined IOTCS_STORAGE_OBJECT_H_ || defined IOTCS_DOXYGEN
#if !defined IOTCS_DOXYGEN
    /**
     * @brief Return resource URI
     * @param external_object External object handle #iotcs_external_object_handle
     * @retval URI - link to an object
     * @retval NULL, if it was not set
     */
    const char* iotcs_external_object_get_uri(const iotcs_external_object_handle external_object_handle);
#endif

    /**
     * @brief Set input file for content to be uploaded.
     * @param storage_object_handle storage object #iotcs_storage_object_handle
     * @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_handle storage_object_handle, const char *input_path);

    /**
     * @brief Set output file for content to be downloaded.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @param output_path Path to the file.
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if given output_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_output_path(iotcs_storage_object_handle storage_object_handle, const char *output_path);

    /**
     * @brief Set a callback that is invoked when the content referred to by this #iotcs_storage_object_handle is synchronized.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @param callback Callback for notifications about synchronization
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if given output_path is NULL.
     * @retval IOTCS_RESULT_OK if succeeds.
     */
    iotcs_result iotcs_storage_object_set_callback(iotcs_storage_object_handle storage_object_handle, iotcs_storage_object_sync_callback callback);

    /**
     * @brief Return synchronization status for current storage object
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Synchronization status
     * @retval IOTCS_NOT_IN_SYNC for illegal or NULL objects
     */
    iotcs_storage_object_sync_status iotcs_storage_object_get_sync_status(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Return input path if it was set
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Input path
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_input_path(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Return output path if it was set
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Output path
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_output_path(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Return name of storage object
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Name
     */
    const char* iotcs_storage_object_get_name(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Return resource URI
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval URI - link to an object
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_uri(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Get the length of the content in bytes.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Size in bytes
     * @retval 0, if it was not gotten
     */
    long iotcs_storage_object_get_length(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Get the mime-type of the content.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Type
     * @retval "application/octet-stream", if it was not set
     */
    const char* iotcs_storage_object_get_type(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Get the encoding scheme of the content
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval Encoding
     * @retval NULL, if it was not set
     */
    const char* iotcs_storage_object_get_encoding(const iotcs_storage_object_handle storage_object_handle);

    /**
     * @brief Get the date and time the content was created or last modified in cloud storage
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @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_handle storage_object_handle);

    /**
     * @brief Notify the library to sync content with the storage cloud.
     * @param storage_object_handle Storage object handle #iotcs_storage_object_handle
     * @retval IOTCS_RESULT_INVALID_ARGUMENT if virtual_device_handle or attribute_name are 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_object_sync(iotcs_storage_object_handle storage_object_handle);
#endif

#ifdef __cplusplus
}
#endif

#endif /* IOTCS_VIRTUAL_STORAGE_OBJECT_H_ */