#ifndef TATAMI_HDF5_LOAD_DENSE_MATRIX_HPP
#define TATAMI_HDF5_LOAD_DENSE_MATRIX_HPP

#include "utils.hpp"

#include <string>
#include <vector>
#include <memory>

#include "H5Cpp.h"
#include "tatami/tatami.hpp"
#include "sanisizer/sanisizer.hpp"

/**
 * @file load_dense_matrix.hpp
 *
 * @brief Load a HDF5 dataset into a dense in-memory matrix.
 */

namespace tatami_hdf5 {

/**
 * Create a `tatami::DenseMatrix` from a HDF5 DataSet.
 *
 * @tparam Value_ Type of the matrix values in the `tatami::Matrix` interface.
 * @tparam Index_Type of the row/column indices.
 * @tparam ValueStorage_ Vector type for storing the matrix values.
 * This may be different from `Value_` for more efficient storage.
 *
 * @param file Path to the HDF5 file.
 * @param name Name of the dataset inside the file.
 * This should refer to a 2-dimensional dataset of integer or floating-point type.
 * @param transpose Whether the dataset is transposed in its storage order, i.e., rows in HDF5 are columns in the matrix.
 * This may be true for HDF5 files generated by frameworks that use column-major matrices,
 * where preserving the data layout between memory and disk is more efficient (see, e.g., the **rhdf5** Bioconductor package).
 *
 * @return Pointer to a `tatami::DenseMatrix` where all values are in memory.
 * This differs from a `tatami_hdf5::DenseMatrix`, where the loading of data is deferred until requested.
 */
template<typename Value_, typename Index_, class ValueStorage_ = std::vector<Value_> >
std::shared_ptr<tatami::Matrix<Value_, Index_> > load_dense_matrix(const std::string& file, const std::string& name, bool transpose) {
    H5::H5File fhandle(file, H5F_ACC_RDONLY);
    auto dhandle = open_and_check_dataset<false>(fhandle, name);

    auto dims = get_array_dimensions<2>(dhandle, name);
    ValueStorage_ values(sanisizer::product<decltype(std::declval<ValueStorage_>().size())>(dims[0], dims[1]));
    dhandle.read(values.data(), define_mem_type<tatami::ElementType<ValueStorage_> >());

    if (transpose) {
        return std::make_shared<tatami::DenseMatrix<Value_, Index_, ValueStorage_> >(dims[1], dims[0], std::move(values), false);
    } else {
        return std::make_shared<tatami::DenseMatrix<Value_, Index_, ValueStorage_> >(dims[0], dims[1], std::move(values), true);
    }
}

}

#endif
