#include "Metadata.h"

/**
 * @file Metadata.cpp
 * @brief Implementation of the Metadata class used in the dobj module.
 *
 * Provides methods to manage metadata for data objects, including size, type,
 * dimensions, and debugging flags.
 */


/**
 * @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::Metadata(std::size_t byteSize, std::string dataType, std::vector<std::size_t> dimensions, bool debugFlag)
    : byteSize_(byteSize), dataType_(std::move(dataType)), dimensions_(std::move(dimensions)), debugFlag_(debugFlag) {}

/**
 * @brief Gets the total size of the data in bytes.
 * 
 * The size is often required for memory allocation and validation in numerical routines.
 * 
 * @return The total byte size of the data.
 */
std::size_t Metadata::getByteSize() const noexcept {
    return byteSize_;
}

/**
 * @brief Sets the total size of the data in bytes.
 * 
 * It's important to ensure this matches the actual data size in memory to prevent overflows
 * or incorrect computations downstream.
 * 
 * @param byteSize The total byte size to set.
 */
void Metadata::setByteSize(std::size_t byteSize) noexcept {
    byteSize_ = byteSize;
}

/**
 * @brief Gets the type of the data.
 * 
 * The type (e.g., "float", "double") is critical for casting raw data or interfacing with libraries
 * that require specific types.
 * 
 * @return A string representing the data type.
 */
const std::string& Metadata::getDataType() const noexcept {
    return dataType_;
}

/**
 * @brief Sets the type of the data.
 * 
 * When setting the data type, ensure it aligns with the underlying data representation.
 * Mismatched types can lead to undefined behavior in numerical calculations.
 * 
 * @param dataType A string representing the data type.
 */
void Metadata::setDataType(const std::string& dataType) {
    dataType_ = dataType;
}

/**
 * @brief Gets the dimensions of the data.
 * 
 * Dimensions define the shape of the data (e.g., 2D arrays, 3D matrices). This is essential
 * for ensuring that operations (e.g., matrix multiplication) are performed correctly.
 * 
 * @return A vector representing the size of each dimension.
 */
const std::vector<std::size_t>& Metadata::getDimensions() const noexcept {
    return dimensions_;
}

/**
 * @brief Sets the dimensions of the data.
 * 
 * When modifying dimensions, verify that they are consistent with the actual data representation.
 * 
 * @param dimensions A vector representing the size of each dimension.
 */
void Metadata::setDimensions(const std::vector<std::size_t>& dimensions) {
    dimensions_ = dimensions;
}

/**
 * @brief Checks if debugging information is enabled.
 * 
 * Debugging flags can be useful for tracking performance metrics or error provenance.
 * 
 * @return True if debugging is enabled, false otherwise.
 */
bool Metadata::isDebugEnabled() const noexcept {
    return debugFlag_;
}

/**
 * @brief Sets the debugging flag.
 * 
 * Enabling debugging can introduce performance overhead but provides valuable insights
 * during development or testing.
 * 
 * @param debugFlag Whether debugging is enabled.
 */
void Metadata::setDebugEnabled(bool debugFlag) noexcept {
    debugFlag_ = debugFlag;
}

/**
 * @brief Prints the metadata information for debugging purposes.
 * 
 * This function provides a human-readable summary of the metadata. Useful for quick checks
 * or logging during debugging sessions.
 * 
 * @param os The output stream to print to.
 * @param metadata The Metadata object to print.
 * @return A reference to the output stream.
 */
std::ostream& operator<<(std::ostream& os, const Metadata& metadata) {
    os << "Metadata Information:\n";
    os << "  Byte Size: " << metadata.byteSize_ << " bytes\n";
    os << "  Data Type: " << metadata.dataType_ << "\n";
    os << "  Dimensions: [";
    for (size_t i = 0; i < metadata.dimensions_.size(); ++i) {
        os << metadata.dimensions_[i];
        if (i < metadata.dimensions_.size() - 1) os << ", ";
    }
    os << "]\n";
    os << "  Debug Enabled: " << (metadata.debugFlag_ ? "Yes" : "No") << "\n";
    return os;
}
