//--------------------------------------------------------------------------------------------------
// Copyright (c) id3 Technologies
// All Rights Reserved.
//--------------------------------------------------------------------------------------------------
#pragma once
#include <id3BiosealCppWrapper/id3BiosealException.hpp>
#include <id3BiosealCppWrapper/id3BiosealDateTime.hpp>
#include <id3BiosealCppWrapper/id3BiosealTypes.hpp>
#include <id3BiosealCppWrapper/id3BiosealStringArray.hpp>
#include <id3BiosealCppWrapper/id3BiosealDateTimeArray.hpp>
#include <id3BiosealCppWrapper/id3BiosealExtension.hpp>
#include <id3BiosealCppWrapper/id3BiosealField.hpp>
#include <id3Bioseal/id3BiosealField.h>
#include "vector"

namespace id3BiosealCppWrapper
{

/**
 * Represents a field or a dictionary of fields.
 */
class Field
{
private:
    ID3_BIOSEAL_FIELD handle_{};

public:
    Field() {
        check_error(id3BiosealField_Initialize(&handle_));
    }

    virtual ~Field() {
        if (handle_) {
            id3BiosealField_Dispose(&handle_);
        }
    }


    Field(Field &&src) noexcept: handle_(src.handle_) {
        src.handle_ = nullptr;
    }

    ID3_BIOSEAL_FIELD getHandle() const { return handle_; }
    operator ID3_BIOSEAL_FIELD () const { return handle_; }

    // public getters and setters
    /**
    * Gets the the field type.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field type.
    */
    id3BiosealFieldType getFieldType() {
        id3BiosealFieldType field_type;
        check_error(id3BiosealField_GetFieldType(handle_, &field_type));
        return field_type;
    }

    /**
    * Gets the the field name.
    * @param name [out] The field name.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field name.
    */
    void getName(std::string & name) {
        int nameSize = -1;
        auto err = id3BiosealField_GetName(handle_, name.data(), &nameSize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            name.resize(nameSize);
            err = id3BiosealField_GetName(handle_, name.data(), &nameSize);
            if (err == 0) { name.resize(nameSize); }
        }
        check_error(err);
    }

    /**
    * Gets the the field name.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field name.
    */
    std::string getName() {
        std::string name;
        getName(name);
        return name;
    }

    /**
    * Gets the the field value as a 64-bit integer.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a 64-bit integer.
    */
    long long getValueAsInteger() {
        long long value_as_integer;
        check_error(id3BiosealField_GetValueAsInteger(handle_, &value_as_integer));
        return value_as_integer;
    }

    /**
    * Gets the the field value as a boolean.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a boolean.
    */
    bool getValueAsBoolean() {
        bool value_as_boolean;
        check_error(id3BiosealField_GetValueAsBoolean(handle_, &value_as_boolean));
        return value_as_boolean;
    }

    /**
    * Gets the the field value as a float.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a float.
    */
    float getValueAsFloat() {
        float value_as_float;
        check_error(id3BiosealField_GetValueAsFloat(handle_, &value_as_float));
        return value_as_float;
    }

    /**
    * Gets the the field value as a string.
    * @param valueAsString [out] The field value as a string.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a string.
    */
    void getValueAsString(std::string & valueAsString) {
        int valueAsStringSize = -1;
        auto err = id3BiosealField_GetValueAsString(handle_, valueAsString.data(), &valueAsStringSize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsString.resize(valueAsStringSize);
            err = id3BiosealField_GetValueAsString(handle_, valueAsString.data(), &valueAsStringSize);
            if (err == 0) { valueAsString.resize(valueAsStringSize); }
        }
        check_error(err);
    }

    /**
    * Gets the the field value as a string.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a string.
    */
    std::string getValueAsString() {
        std::string value_as_string;
        getValueAsString(value_as_string);
        return value_as_string;
    }

    /**
    * Gets the the data as a binary array.
    * @param valueAsBinary [out] The data as a binary array.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The data as a binary array.
    */
    void getValueAsBinary(std::vector<unsigned char> & valueAsBinary) {
        int valueAsBinarySize = -1;
        auto err = id3BiosealField_GetValueAsBinary(handle_, valueAsBinary.data(), &valueAsBinarySize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsBinary.resize(valueAsBinarySize);
            err = id3BiosealField_GetValueAsBinary(handle_, valueAsBinary.data(), &valueAsBinarySize);
        }
        check_error(err);
    }

    /**
    * Gets the the data as a binary array.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The data as a binary array.
    */
    std::vector<unsigned char> getValueAsBinary() {
        std::vector<unsigned char> value_as_binary;
        getValueAsBinary(value_as_binary);
        return value_as_binary;
    }

    /**
    * Gets the the field value as a date.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a date.
    */
    DateTime getValueAsDate() {
        DateTime value_as_date;
        check_error(id3BiosealField_GetValueAsDate(handle_, value_as_date));
        return value_as_date;
    }

    /**
    * Gets the the field value as a time.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a time.
    */
    DateTime getValueAsTime() {
        DateTime value_as_time;
        check_error(id3BiosealField_GetValueAsTime(handle_, value_as_time));
        return value_as_time;
    }

    /**
    * Gets the the field value as a date/time.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a date/time.
    */
    DateTime getValueAsDateTime() {
        DateTime value_as_date_time;
        check_error(id3BiosealField_GetValueAsDateTime(handle_, value_as_date_time));
        return value_as_date_time;
    }

    /**
    * Gets the the field value as a timestamp (without timezone computation).
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as a timestamp (without timezone computation).
    */
    long long getValueAsTimestamp() {
        long long value_as_timestamp;
        check_error(id3BiosealField_GetValueAsTimestamp(handle_, &value_as_timestamp));
        return value_as_timestamp;
    }

    /**
    * Gets the the field value as an array of 64-bit integers.
    * @param valueAsIntegerArray [out] The field value as an array of 64-bit integers.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of 64-bit integers.
    */
    void getValueAsIntegerArray(std::vector<long long> & valueAsIntegerArray) {
        int valueAsIntegerArraySize = -1;
        auto err = id3BiosealField_GetValueAsIntegerArray(handle_, valueAsIntegerArray.data(), &valueAsIntegerArraySize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsIntegerArray.resize(valueAsIntegerArraySize);
            err = id3BiosealField_GetValueAsIntegerArray(handle_, valueAsIntegerArray.data(), &valueAsIntegerArraySize);
        }
        check_error(err);
    }

    /**
    * Gets the the field value as an array of 64-bit integers.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of 64-bit integers.
    */
    std::vector<long long> getValueAsIntegerArray() {
        std::vector<long long> value_as_integer_array;
        getValueAsIntegerArray(value_as_integer_array);
        return value_as_integer_array;
    }

    /**
    * Gets the the field value as an array of booleans.
    * @param valueAsBooleanArray [out] The field value as an array of booleans.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of booleans.
    */
    void getValueAsBooleanArray(std::vector<bool_storage_t> & valueAsBooleanArray) {
        int valueAsBooleanArraySize = -1;
        auto err = id3BiosealField_GetValueAsBooleanArray(handle_, reinterpret_cast<bool *>(valueAsBooleanArray.data()), &valueAsBooleanArraySize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsBooleanArray.resize(valueAsBooleanArraySize);
            err = id3BiosealField_GetValueAsBooleanArray(handle_, reinterpret_cast<bool *>(valueAsBooleanArray.data()), &valueAsBooleanArraySize);
        }
        check_error(err);
    }

    /**
    * Gets the the field value as an array of booleans.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of booleans.
    */
    std::vector<bool_storage_t> getValueAsBooleanArray() {
        std::vector<bool_storage_t> value_as_boolean_array;
        getValueAsBooleanArray(value_as_boolean_array);
        return value_as_boolean_array;
    }

    /**
    * Gets the the field value as an array of floats.
    * @param valueAsFloatArray [out] The field value as an array of floats.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of floats.
    */
    void getValueAsFloatArray(std::vector<float> & valueAsFloatArray) {
        int valueAsFloatArraySize = -1;
        auto err = id3BiosealField_GetValueAsFloatArray(handle_, valueAsFloatArray.data(), &valueAsFloatArraySize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsFloatArray.resize(valueAsFloatArraySize);
            err = id3BiosealField_GetValueAsFloatArray(handle_, valueAsFloatArray.data(), &valueAsFloatArraySize);
        }
        check_error(err);
    }

    /**
    * Gets the the field value as an array of floats.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of floats.
    */
    std::vector<float> getValueAsFloatArray() {
        std::vector<float> value_as_float_array;
        getValueAsFloatArray(value_as_float_array);
        return value_as_float_array;
    }

    /**
    * Gets the the field value as an array of strings.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of strings.
    */
    StringArray getValueAsStringArray() {
        StringArray value_as_string_array;
        check_error(id3BiosealField_GetValueAsStringArray(handle_, value_as_string_array));
        return value_as_string_array;
    }

    /**
    * Gets the the field value as an array of dates.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of dates.
    */
    DateTimeArray getValueAsDateTimeArray() {
        DateTimeArray value_as_date_time_array;
        check_error(id3BiosealField_GetValueAsDateTimeArray(handle_, value_as_date_time_array));
        return value_as_date_time_array;
    }

    /**
    * Gets the the field value as an array of timestamp.
    * @param valueAsTimestampArray [out] The field value as an array of timestamp.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of timestamp.
    */
    void getValueAsTimestampArray(std::vector<long long> & valueAsTimestampArray) {
        int valueAsTimestampArraySize = -1;
        auto err = id3BiosealField_GetValueAsTimestampArray(handle_, valueAsTimestampArray.data(), &valueAsTimestampArraySize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            valueAsTimestampArray.resize(valueAsTimestampArraySize);
            err = id3BiosealField_GetValueAsTimestampArray(handle_, valueAsTimestampArray.data(), &valueAsTimestampArraySize);
        }
        check_error(err);
    }

    /**
    * Gets the the field value as an array of timestamp.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field value as an array of timestamp.
    */
    std::vector<long long> getValueAsTimestampArray() {
        std::vector<long long> value_as_timestamp_array;
        getValueAsTimestampArray(value_as_timestamp_array);
        return value_as_timestamp_array;
    }

    /**
    * Gets the A value indicating whether if the field is null.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval A value indicating whether if the field is null.
    */
    bool getIsNull() {
        bool is_null;
        check_error(id3BiosealField_GetIsNull(handle_, &is_null));
        return is_null;
    }

    /**
    * Gets the A value indicating whether the field (or one of its child fields) contains biometric data.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval A value indicating whether the field (or one of its child fields) contains biometric data.
    */
    bool getContainsBiometrics() {
        bool contains_biometrics;
        check_error(id3BiosealField_GetContainsBiometrics(handle_, &contains_biometrics));
        return contains_biometrics;
    }

    /**
    * Gets the the field extension type.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field extension type.
    */
    id3BiosealFieldExtensionType getExtensionType() {
        id3BiosealFieldExtensionType extension_type;
        check_error(id3BiosealField_GetExtensionType(handle_, &extension_type));
        return extension_type;
    }

    /**
    * Gets the the field extension.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field extension.
    */
    Extension getFieldExtension() {
        Extension field_extension;
        check_error(id3BiosealField_GetFieldExtension(handle_, field_extension));
        return field_extension;
    }

    /**
    * Gets the the visibility condition.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The visibility condition.
    */
    id3BiosealVisibilityCondition getVisibilityCondition() {
        id3BiosealVisibilityCondition visibility_condition;
        check_error(id3BiosealField_GetVisibilityCondition(handle_, &visibility_condition));
        return visibility_condition;
    }

    /**
    * Gets the gets a string list containing the keys in the dict.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval Gets a string list containing the keys in the dict.
    */
    StringArray getKeys() {
        StringArray keys;
        check_error(id3BiosealField_GetKeys(handle_, keys));
        return keys;
    }

    // public methods
    /**
    * Gets an item of the Field object.
    * @param key [in] Unique key of the Field item to get.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval Field item to get.
    */
    Field get(const char *key) {
        Field field_item;
        check_error(id3BiosealField_Get(handle_, key, field_item));
        return field_item;
    }

    /**
    * Gets the number of elements in the Field object.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval Number of elements in the Field object.
    */
    int getCount() {
        int count;
        check_error(id3BiosealField_GetCount(handle_, &count));
        return count;
    }

    /**
    * Determines whether the Fieldobject contains the specified key.
    * @param key [in] The key to locate in the Field object.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval true if the Field object contains an element with the specified key, otherwise false.
    */
    bool containsKey(const char *key) {
        bool result;
        check_error(id3BiosealField_ContainsKey(handle_, key, &result));
        return result;
    }

    /**
    * Gets the object at the specified index.
    * @param index [in] The index of the object to retrieve.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The field that contains the object.
    */
    Field getObjectAtIndex(int index) {
        Field item;
        check_error(id3BiosealField_GetObjectAtIndex(handle_, index, item));
        return item;
    }

    /**
    * Gets the binary data at the specified index.
    * @param index [in] The index of the binary field to retrieve.
    * @param item [out] The binary data.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The binary data.
    */
    void getBinaryAtIndex(int index, std::vector<unsigned char> & item) {
        int itemSize = -1;
        auto err = id3BiosealField_GetBinaryAtIndex(handle_, index, item.data(), &itemSize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            item.resize(itemSize);
            err = id3BiosealField_GetBinaryAtIndex(handle_, index, item.data(), &itemSize);
        }
        check_error(err);
    }

    /**
    * Gets the binary data at the specified index.
    * @param index [in] The index of the binary field to retrieve.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The binary data.
    */
    std::vector<unsigned char> getBinaryAtIndex(int index) {
        std::vector<unsigned char> item;
        getBinaryAtIndex(index, item);
        return item;
    }

    /**
    * Set the time zone offset in seconds.
    * @param offsetInSeconds [in] The time zone offset in seconds.
    * @exception BiosealException An error has occured during Bioseal library execution.
    */
    void setTimeZoneOffset(int offsetInSeconds) {
        check_error(id3BiosealField_SetTimeZoneOffset(handle_, offsetInSeconds));
    }

    /**
    * Returns a value indicating whether the field (or one of its child fields) contains an extension of the specified type.
    * @param extensionType [in] The field extension type.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval A value indicating whether the field (or one of its child fields) contains the specified extension.
    */
    bool containsExtension(id3BiosealFieldExtensionType extensionType) {
        bool result;
        check_error(id3BiosealField_ContainsExtension(handle_, extensionType, &result));
        return result;
    }

    /**
    * Returns a value indicating whether the field (or one of its child fields) contains an extension with specified name.
    * @param extensionName [in] A string that identifies the extension.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval A value indicating whether the field (or one of its child fields) contains the specified extension.
    */
    bool containsExtensionByName(const char *extensionName) {
        bool result;
        check_error(id3BiosealField_ContainsExtensionByName(handle_, extensionName, &result));
        return result;
    }

    /**
    * Returns a value indicating whether the field (or one of its child fields) contains biometric data of the specified type and/or format.
    * @param biometricDataType [in] The biometric data type.
    * @param biometricFormat [in] The biometric data format.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval A value indicating whether the field (or one of its child fields) contains biometric data of the specified type and/or format.
    */
    bool containsBiometricData(id3BiosealBiometricDataType biometricDataType, const char *biometricFormat) {
        bool result;
        check_error(id3BiosealField_ContainsBiometricData(handle_, biometricDataType, biometricFormat, &result));
        return result;
    }

    /**
    * Retrieves the localized label based on the provided language code.
    * @param language [in] The language code for which the label should be localized (e.g., 'en' for English, 'fr' for French).
    * @param label [out] The localized label corresponding to the provided language code.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The localized label corresponding to the provided language code.
    */
    void getLocalizedLabel(const char *language, std::string & label) {
        int labelSize = -1;
        auto err = id3BiosealField_GetLocalizedLabel(handle_, language, label.data(), &labelSize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            label.resize(labelSize);
            err = id3BiosealField_GetLocalizedLabel(handle_, language, label.data(), &labelSize);
            if (err == 0) { label.resize(labelSize); }
        }
        check_error(err);
    }

    /**
    * Retrieves the localized label based on the provided language code.
    * @param language [in] The language code for which the label should be localized (e.g., 'en' for English, 'fr' for French).
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The localized label corresponding to the provided language code.
    */
    std::string getLocalizedLabel(const char *language) {
        std::string label;
        getLocalizedLabel(language, label);
        return label;
    }

    /**
    * Retrieves the localized value based on the provided language code.
    * @param language [in] The language code for which the value should be localized (e.g., 'en' for English, 'fr' for French).
    * @param value [out] The localized value corresponding to the provided language code.
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The localized value corresponding to the provided language code.
    */
    void getLocalizedValue(const char *language, std::string & value) {
        int valueSize = -1;
        auto err = id3BiosealField_GetLocalizedValue(handle_, language, value.data(), &valueSize);
        if (err == (int)id3BiosealError_InsufficientBuffer) {
            value.resize(valueSize);
            err = id3BiosealField_GetLocalizedValue(handle_, language, value.data(), &valueSize);
            if (err == 0) { value.resize(valueSize); }
        }
        check_error(err);
    }

    /**
    * Retrieves the localized value based on the provided language code.
    * @param language [in] The language code for which the value should be localized (e.g., 'en' for English, 'fr' for French).
    * @exception BiosealException An error has occured during Bioseal library execution.
    * @retval The localized value corresponding to the provided language code.
    */
    std::string getLocalizedValue(const char *language) {
        std::string value;
        getLocalizedValue(language, value);
        return value;
    }

};
};