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

/**
 * @brief Common private header
 */

#ifndef DEVICE_MODEL_PRIVATE_H
#define DEVICE_MODEL_PRIVATE_H

#ifdef __cplusplus
extern "C" {
#endif
#include "device_model_private_ext.h"
#include "iotcs_virtual_device.h"
#include "advanced/iotcs_message.h"

#include "json/json_helper.h"

#ifdef IOTCS_STORAGE_SUPPORT
#include "advanced/iotcs_storage_object.h"
#include "iotcs_storage_object.h"
#include "scs/storage_dispatcher_private.h"

    void dm_free_uri_object(struct iotcs_uri_object_t *storage_object_handle);
    iotcs_result dm_create_uri_object_by_uri(const char* uri, struct iotcs_uri_object_t **storage_object_handle);
    iotcs_result dm_create_storage_object(const char* endpoint_id, const char* name, const char* content_type,
            struct iotcs_uri_object_t **storage_object_handle);
#endif

    /**
     * @struct dm_attribute
     * @brief Attribute structure. 
     * 
     * The attributes of a Device Model represent the basic variables that the device supports, such as temperature, humidity, 
     * flow rate, valve position, and so forth. Appropriate message formats and web resources are automatically derived from 
     * these attribute definitions and automatically installed in /iot/api/v2/formats and in the resource directory, as appropriate. 
     * Some device models will be defined solely by attributes. Most however will be defined by some combination of attributes 
     * and ancillary alert message formats. 
     */
    // TODO: Make range[2] immediate value, not a pointer
    // TODO: model_urn never used

    typedef struct {
        const char *model_name; /**< Device model name.*/
        const char *model_urn; /**< Device model urn.*/
        const char *name; /**< Attribute name.*/
        const char *description; /**< Description of the attribute. Optional.*/
        iotcs_value_type type; /**< Type of the attribute value.*/
        iotcs_value* range[2]; /**< For NUMBERs and INTEGERs only, defines the acceptable range of values such as "1,10". This range is inclusive.*/
        iotcs_bool is_writable; /**< Defines whether the attribute is writable. Default is false, meaning it is read-only by default.*/
        const char *alias; /**< Optional alternative name.*/
        iotcs_value default_value; /**< Optional: default value of the attribute. Used in device virtualization to get attribute value of a device when actual value was never reported*/
        iotcs_bool has_default_value; /**< Default value for attribute exists.*/
    } dm_attribute;

    /**
     * @struct dm_action
     * @brief Action structure. 
     * 
     * Actions are essentially remotely invocable methods on devices. They are similar to attributes, but are not readable state, 
     * but rather, invocable state. They can have 0 or 1 arguments.
     */
    // TODO: model_urn never used

    typedef struct {
        const char *model_name; /**< Device model name.*/
        const char *model_urn; /**< Device model urn.*/
        const char *name; /**< Action name.*/
        const char *description; /**< Description of the action. Optional.*/
        iotcs_value_type arg_type; /**< Type of the action argument value.*/
        const char *alias; /**< Optional alternative name.*/
    } dm_action;

    /**
     * @enum dm_format_type
     * @brief Device Model Format type
     */
    typedef enum {
        DM_DATA_FORMAT_TYPE = 0,
        DM_ALERT_FORMAT_TYPE = 1
    } dm_format_type;

    /**
     * @enum dm_device_handle_update_state_t
     * @brief Device Model Format type
     */
    typedef enum {
        DM_DEVICE_UPDATE_OFF = 0,
        DM_DEVICE_UPDATE_ON = 1,
        DM_DEVICE_IS_UPDATED = 2
    } dm_device_handle_update_state_t;

    struct dm_device_handle_type;

    typedef struct dm_endpoint_node_type {
        const char *endpoint_id;
        struct dm_device_handle_type *device_list;
        struct dm_endpoint_node_type *next;
        iotcs_message_base data_message_base;
        iotcs_message_base alert_message_base;
    } dm_endpoint_node_t;

    //device model formats----------------------------

    typedef struct dm_format_field {
        const char* name;
        iotcs_value_type type;
        iotcs_bool optional;
    } dm_format_field_t;

    typedef struct dm_format {
        const char* urn;
        const char* name;
        const char* description;
        dm_format_type type;

        union {
            iotcs_alert_message_base alert_message_base;
            iotcs_data_message_base data_message_base;
        } u;
        dm_format_field_t* fields;
        int fields_len;
    } dm_format_t;

    typedef struct dm_formats_handle {
        dm_format_t* formats;
        int formats_len;
    } dm_formats_handle_t;
    //device model formats----------------------------

    //device model handle----------------------------

    typedef struct dm_attributes {
        dm_attribute* desc;
        iotcs_data_message_base message_props; /* used for data message */
        int count;
    } dm_attributes_t;

    typedef struct dm_actions {
        dm_action* desc;
        int count;
    } dm_actions_t;

    typedef struct device_model_type {
        const char* urn;
        const char* name;
        const char* description;
        dm_attributes_t attributes;
        dm_actions_t actions;
        dm_formats_handle_t formats;
        struct device_model_type *next;
        int ref_cnt;
    } iotcs_device_model_handle_t;
    //device model handle----------------------------

    typedef struct {
        iotcs_value value;
        iotcs_virtual_device_changed_callback changed_cb;
        iotcs_virtual_device_error_callback error_cb;
        iotcs_bool is_dirty;
        iotcs_bool is_offered;
        iotcs_bool is_filled;
    } dm_attribute_value_t;

    typedef struct {
        iotcs_value value; // TODO: ??? looks like extra info
        iotcs_action_callback action_cb;
        iotcs_action_callback_cpp action_cb_cpp;
    } dm_action_value_t;
    //device handle----------------------------------

    typedef struct dm_device_handle_type {
        dm_endpoint_node_t *endpoint_node;
        iotcs_device_model_handle_t* model_handle;
        dm_attribute_value_t* attributes;
        dm_action_value_t* actions;
        struct dm_device_handle_type *next;
        int ref_cnt;
        iotcs_virtual_device_changed_callback attribute_cb;
        iotcs_virtual_device_error_callback attribute_err_cb;
        dm_device_handle_update_state_t update_state;
    } iotcs_virtual_device_handle_t;


    typedef struct dm_format_handle {
        iotcs_virtual_device_handle_t *device;
        dm_format_t *format;
        dm_format_type type;
        iotcs_value *values;
        iotcs_virtual_device_error_callback error_callback;
        iotcs_virtual_device_error_internal_callback error_callback_cpp;
        int32_t is_dirty[1];
    } dm_format_handle_t;

    typedef struct dm_format_handle iotcs_alert_handle_t;
    typedef struct dm_format_handle iotcs_data_handle_t;

    // init/finit
    iotcs_result device_model_init(void);
    void device_model_finalize(void);

    // handlers ----------------------------------
    void device_model_handle_send(iotcs_message *message, iotcs_result result, const char *fail_reason);
    iotcs_result device_model_handle_request(iotcs_request_message *message);

    //internal free methods
    void dm_free_virtual_device(iotcs_virtual_device_handle_t* device_handle);
    void dm_remove_device_from_endpoint(iotcs_virtual_device_handle_t* device_handle);
    void dm_release_endpoint(dm_endpoint_node_t* endpoint_node);
    void dm_release_model(iotcs_device_model_handle_t* model_handle);
    void dm_free_model(iotcs_device_model_handle_t* model_handle);
    void dm_free_formats(dm_formats_handle_t* formats);
    void dm_free_attributes(dm_attributes_t* attributes_desc);
    void dm_free_actions(dm_actions_t* actions_desc);
    iotcs_result dm_make_update_message(iotcs_virtual_device_handle_t* device, iotcs_message *msg);
    iotcs_result dm_make_offer_message(iotcs_virtual_device_handle_t* device, iotcs_message *msg);
    iotcs_result dm_make_update_offer_message(iotcs_virtual_device_handle_t* device, iotcs_message *msg, iotcs_bool is_offered);
    //root - full json from server
    iotcs_device_model_handle_t* device_model_fromJSON(json_tokens_t* tok);
    //private methods
    void dm_update_value(iotcs_value* attribute_value, iotcs_typed_value *ptyped_value);
    iotcs_result dm_validate_value(dm_attribute* attribute_desc, iotcs_typed_value *ptyped_value);

    void alert_message_release(iotcs_message *alert_msg);
    void data_message_release(iotcs_message *data_msg);
    int get_attribute_idx(iotcs_device_model_handle_t* hmodel, const char *name);
    iotcs_result set_value(dm_format_handle_t *hformat, const char *field_name, iotcs_typed_value *ptyped_value);

    //v2
    iotcs_result dm_get_device_model(const char* device_model_urn, char** device_model_json);


#ifdef __cplusplus
}
#endif

#endif /* DEVICE_MODEL_PRIVATE_H */

