#ifndef METADATA_H
#define METADATA_H

#if defined(__APPLE__) || defined(__linux__)
#define EXPORT_SYMBOL __attribute__((visibility("default")))
#else
#define EXPORT_SYMBOL
#endif

#include <string>
#include <vector>
#include <cstddef>
#include <iostream>

/**
 * @file Metadata.h
 * @brief Public interface for the Metadata class used in the dobj module.
 *
 * The Metadata class provides descriptive information about the data encapsulated
 * within a dobj, including size, type, dimensions, and debugging flags.
 */

/**
 * @class Metadata
 * @brief Represents metadata information for data objects in the dobj module.
 *
 * The Metadata class encapsulates details such as data size, type, dimensions,
 * and optional debugging flags. It is designed to provide descriptive attributes
 * in a lightweight and efficient manner.
 */
class EXPORT_SYMBOL Metadata {
public:
    /**
     * @brief Default constructor for Metadata.
     */
    Metadata() = default;

    /**
     * @brief Constructor to initialize Metadata with specific attributes.
     * 
     * @param byteSize The total size of the data in bytes.
     * @param dataType The type of the data (e.g., "float", "double").
     * @param dimensions A vector representing the size of each dimension (e.g., {3, 4} for a 3x4 matrix).
     * @param debugFlag Whether debugging information is enabled for this Metadata instance.
     */
    Metadata(std::size_t byteSize, std::string dataType, std::vector<std::size_t> dimensions, bool debugFlag = false);

    /**
     * @brief Gets the total size of the data in bytes.
     * @return The total byte size of the data.
     */
    [[nodiscard]] std::size_t getByteSize() const noexcept;

    /**
     * @brief Sets the total size of the data in bytes.
     * @param byteSize The total byte size to set.
     */
    void setByteSize(std::size_t byteSize) noexcept;

    /**
     * @brief Gets the type of the data.
     * @return A string representing the data type.
     */
    [[nodiscard]] const std::string& getDataType() const noexcept;

    /**
     * @brief Sets the type of the data.
     * @param dataType A string representing the data type.
     */
    void setDataType(const std::string& dataType);

    /**
     * @brief Gets the dimensions of the data.
     * @return A vector representing the size of each dimension.
     */
    [[nodiscard]] const std::vector<std::size_t>& getDimensions() const noexcept;

    /**
     * @brief Sets the dimensions of the data.
     * @param dimensions A vector representing the size of each dimension.
     */
    void setDimensions(const std::vector<std::size_t>& dimensions);

    /**
     * @brief Checks if debugging information is enabled.
     * @return True if debugging is enabled, false otherwise.
     */
    [[nodiscard]] bool isDebugEnabled() const noexcept;

    /**
     * @brief Sets the debugging flag.
     * @param debugFlag Whether debugging is enabled.
     */
    void setDebugEnabled(bool debugFlag) noexcept;

    /**
     * @brief Prints the metadata information for debugging purposes.
     * 
     * @param os The output stream to print to.
     * @param metadata The Metadata object to print.
     * @return A reference to the output stream.
     */
    friend std::ostream& operator<<(std::ostream& os, const Metadata& metadata);

private:
    int byteSize_ = 0;                      ///< Total size of the data in bytes.
    std::string dataType_;                          ///< Type of the data (e.g., "float", "double").
    std::vector<std::size_t> dimensions_;           ///< Dimensions of the data (e.g., {3, 4} for a 3x4 matrix).
    bool debugFlag_ = false;                        ///< Indicates whether debugging is enabled.
};

#endif // METADATA_H
