#ifndef GC_DATAPOINT_VALUE_H
#define GC_DATAPOINT_VALUE_H
#include <stdbool.h>
#include <stdint.h>

typedef uint64_t GCDatapointIdentifier;
typedef uint16_t GCDatapointValueQuality;

/// @cond
enum eGCDatapointValueQuality
{
    GC_DATAPOINT_QUALITY_GOOD = 0x0,
    GC_DATAPOINT_QUALITY_INVALID = 0x1,
    GC_DATAPOINT_QUALITY_RESERVED = 0x2,
    GC_DATAPOINT_QUALITY_QUESTIONABLE = 0x3,

    GC_DATAPOINT_QUALITY_FIRST_TWO_BITS_MASK = 0x3,

    GC_DATAPOINT_QUALITY_OVERFLOW = 0x4,
    GC_DATAPOINT_QUALITY_OUT_OF_RANGE = 0x8,
    GC_DATAPOINT_QUALITY_BAD_REFERENCE = 0x10,
    GC_DATAPOINT_QUALITY_OSCILLATORY = 0x20,
    GC_DATAPOINT_QUALITY_FAILURE = 0x40,
    GC_DATAPOINT_QUALITY_OLD_DATA = 0x80,
    GC_DATAPOINT_QUALITY_INCONSISTENT = 0x100,
    GC_DATAPOINT_QUALITY_INACCURATE = 0x200,
    GC_DATAPOINT_QUALITY_SOURCE = 0x400,
    GC_DATAPOINT_QUALITY_TEST = 0x800,
    GC_DATAPOINT_QUALITY_OPERATOR_BLOCKED = 0x1000,
};
/// @endcond

/**
 * @brief The type of the datapoint value
 */
enum eGCDatapointValueType
{
    GCOS_DATAPOINT_TYPE_FLOAT32,
    GCOS_DATAPOINT_TYPE_FLOAT64,
    GCOS_DATAPOINT_TYPE_INT32,
    GCOS_DATAPOINT_TYPE_INT64,
    GCOS_DATAPOINT_TYPE_BOOLEAN,
    GCOS_DATAPOINT_TYPE_UINT32,
    GCOS_DATAPOINT_TYPE_UINT64,
    GCOS_DATAPOINT_TYPE_DOUBLEPOINT,
    GCOS_DATAPOINT_TYPE_TIMESTAMPNS,
    GCOS_DATAPOINT_TYPE_BYTE_ARRAY,
};
typedef enum eGCDatapointValueType GCDatapointValueType;

/**
 * @brief The value of a datapoint
 *
 * It is used to publish data and also to receive data from the core via subscription or trough the RealtimeDatabase.
 */
struct sGCDatapointValue
{
    /**
     * @brief The type of the datapoint value
     * This type depends on the datapoint's @ref value field.
     *
     * @see @ref eGCDatapointValueType "GCDatapointValueType"
     */
    GCDatapointValueType type;

    /**
     * @brief The value of the datapoint
     */
    union {
        uint64_t timestampNs;
        uint64_t u64;
        uint32_t u32;
        int64_t i64;
        int32_t i32;
        float f32;
        double f64;
        uint8_t dp;
        bool b;
    } value;

    /**
     * @brief The identifier for the datapoint
     *
     * Each datapoint has it's own datapoint identifier, this is used to identify the datapoint in the system.
     * This can be retrieved from the datapoint configuration.
     */
    GCDatapointIdentifier datapoint_id;

    /**
     * @brief Represents the quality of the datapoint value
     *
     * This is a bit field mimicking the same structure as the one used in IEC-61850-8-1 for quality.
     * @see
     * [IEC-61850-8-1](https://www.researchgate.net/figure/IEC-61850-8-1-Bit-String-representation-of-Quality-at-tributes_tbl2_312327440)
     */
    GCDatapointValueQuality quality;

    /**
     * @brief The timestamp when the value was generated in nanoseconds since the Unix epoch
     */
    uint64_t timestamp;
};
typedef struct sGCDatapointValue* GCDatapointValue;

/// <div rustbindgen hide></div>
/**
 * @brief Set the quality of a datapoint value to good
 *
 * Setting the quality of a datapoint value to good means will override the following flags:
 * - Invalid
 * - Questionable
 */
void
GCDatapointValue_setQualityGood(GCDatapointValue value);

/// <div rustbindgen hide></div>

/**
 * @brief Set the quality of a datapoint value to invalid
 *
 * Setting the quality of a datapoint value to invalid will override the following flags:
 * - Good
 * - Questionable
 */
void
GCDatapointValue_setQualityInvalid(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the quality of a datapoint value to questionable
 *
 * Setting the quality of a datapoint value to questionable will override the following flags:
 * - Good
 * - Invalid
 */
void
GCDatapointValue_setQualityQuestionable(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to overflow
 */
void
GCDatapointValue_setQualityOverflow(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to out of range
 */
void
GCDatapointValue_setQualityOutOfRange(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to bad reference
 */
void
GCDatapointValue_setQualityBadReference(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to oscillatory
 */
void
GCDatapointValue_setQualityOscillatory(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to failure
 */
void
GCDatapointValue_setQualityFailure(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to old data
 */
void
GCDatapointValue_setQualityOldData(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to inconsistent
 */
void
GCDatapointValue_setQualityInconsistent(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to inaccurate
 */
void
GCDatapointValue_setQualityInaccurate(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to source processing
 */
void
GCDatapointValue_setQualitySourceProcessing(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to test
 */
void
GCDatapointValue_setQualityTest(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Set the bit flag quality of a datapoint value to operator blocked
 */
void
GCDatapointValue_setQualityOperatorBlocked(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is good
 */
bool
GCDatapointValue_isQualityGood(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is invalid
 */
bool
GCDatapointValue_isQualityInvalid(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is questionable
 */
bool
GCDatapointValue_isQualityQuestionable(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is overflow
 */
bool
GCDatapointValue_isQualityOverflow(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is out of range
 */
bool
GCDatapointValue_isQualityOutOfRange(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is bad reference
 */
bool
GCDatapointValue_isQualityBadReference(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is oscillatory
 */
bool
GCDatapointValue_isQualityOscillatory(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is failure
 */
bool
GCDatapointValue_isQualityFailure(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is old data
 */
bool
GCDatapointValue_isQualityOldData(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is inconsistent
 */
bool
GCDatapointValue_isQualityInconsistent(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is inaccurate
 */
bool
GCDatapointValue_isQualityInaccurate(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is source processing
 */
bool
GCDatapointValue_isQualitySourceProcessing(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is test
 */
bool
GCDatapointValue_isQualityTest(GCDatapointValue value);

/// <div rustbindgen hide></div>
/**
 * @brief Check if the quality of a datapoint value is operator blocked
 */
bool
GCDatapointValue_isQualityOperatorBlocked(GCDatapointValue value);

#endif // GC_DATAPOINT_VALUE_H
