/*! \file file_io.h
 *
 * \brief Library to handle I/O operations with files.
 */
#ifndef INCLUDE_FILE_IO_H_
#define INCLUDE_FILE_IO_H_

/*! \class FileSchema
 *
 * \brief File content descriptor.
 *
 * Accessing binary files requires detailed knowledge of their contents. The `FileSchema`
 * class is intended to encapsulate this information and use it as a wrapper to control
 * I/O operations towards different file formats. Any file can be thought of as a sequence
 * of records, which may further contain arbitrarily complex structures. By describing the
 * structure of records, it is possible to support virtually any type of format.
 */
class FileSchema {
 protected:
  //! \brief Number of records conained in the file.
  int num_records;
  //! \brief Array of record names.
  std::string *record_names;
  //! \brief Array of record descriptors.
  std::string *record_types;

 public:
  /*! \brief FileSchema instance constructor.
   *
   * \param num_rec: `int` Number of records in the file.
   * \param rec_types: `string *` Description of the records in the file.
   * \param rec_names: `string *` Names of the records in the file.
   */
  FileSchema(int num_rec, const std::string *rec_types, const std::string *rec_names=NULL);

  /*! \brief FileSchema instance destroyer.
   */
  ~FileSchema();

  /*! \brief Get the number of records in file.
   *
   * \return num_records: `int` The number of records contained in the file.
   */
  int get_record_number() { return num_records; }

  /*! \brief Get a copy of the record names.
   *
   * \return rec_types: `string *` A new vector of strings with description of records.
   */
  std::string *get_record_names();
  
  /*! \brief Get a copy of the record types.
   *
   * \return rec_names: `string *` A new vector of strings with record names.
   */
  std::string *get_record_types();
};

/*! \class HDFFile
 *
 * \brief HDF5 I/O wrapper class.
 *
 * This class manages I/O operations toward HDF5 format files.
 */
class HDFFile {
 protected:
  //! \brief Identifier list.
  List<hid_t> *id_list;
  //! \brief Name of the file.
  std::string file_name;
  //! \brief Flag for the open file status.
  bool file_open_flag;
  //! File identifier handle.
  hid_t file_id;
  //! Return status of the last operation.
  herr_t status;

 public:
  /*! \brief HDFFile instance constructor.
   *
   * \param name: `string` Name of the file.
   * \param flags: `unsigned int` File access flags (default is `H5F_ACC_EXCL`).
   * \param fcpl_id: `hid_t` File creation property list identifier (default is `H5P_DEFAULT`).
   * \param fapl_id: `hid_t` File access property list identifier (default is `H5P_DEFAULT`).
   */
  HDFFile(
	  const std::string& name, unsigned int flags=H5F_ACC_EXCL,
	  hid_t fcpl_id=H5P_DEFAULT, hid_t fapl_id=H5P_DEFAULT
  );

  /*! \brief HDFFile instance destroyer.
   */
  ~HDFFile();
  
  /*! \brief Close the current file.
   */
  herr_t close();

  /*! \brief Create an empty file from a `FileSchema` instance.
   *
   * \param schema: `FileSchema &` Reference to `FileSchema` instance.
   * \param name: `string` Name of the file.
   * \param flags: `unsigned int` File access flags (default is `H5F_ACC_EXCL`).
   * \param fcpl_id: `hid_t` File creation property list identifier (default is `H5P_DEFAULT`).
   * \param fapl_id: `hid_t` File access property list identifier (default is `H5P_DEFAULT`).
   * \return hdf_file: `HDFFile *` Pointer to a new, open HDF5 file.
   */
  static HDFFile* from_schema(
			      FileSchema &schema, const std::string& name, unsigned int flags=H5F_ACC_EXCL,
			      hid_t fcpl_id=H5P_DEFAULT, hid_t fapl_id=H5P_DEFAULT
  );

  /*! \brief Get current status.
   */
  hid_t get_file_id() { return file_id; }
  
  /*! \brief Get current status.
   */
  herr_t get_status() { return status; }
  
  /*! \brief Check whether the attached file is currently open.
   */
  bool is_open() { return file_open_flag; }
  
  /*! \brief Read data from attached file.
   *
   * \param dataset_name: `string` Name of the dataset to read from.
   * \param data_type: `string` Memory data type identifier.
   * \param buffer: `hid_t` Starting address of the memory sector to store the data.
   * \param mem_space_id: `hid_t` Memory data space identifier (defaults to `H5S_ALL`).
   * \param file_space_id: `hid_t` File space identifier (defaults to `H5S_ALL`).
   * \param dapl_id: `hid_t` Data access property list identifier (defaults to `H5P_DEFAULT`).
   * \param dxpl_id: `hid_t` Data transfer property list identifier (defaults to `H5P_DEFAULT`).
   * \return status: `herr_t` Exit status of the operation.
   */
  herr_t read(
	       const std::string& dataset_name, const std::string& data_type, void *buffer,
	       hid_t mem_space_id=H5S_ALL, hid_t file_space_id=H5S_ALL,
	       hid_t dapl_id=H5P_DEFAULT, hid_t dxpl_id=H5P_DEFAULT
  );

  /*! \brief Write data to attached file.
   *
   * \param dataset_name: `string` Name of the dataset to write to.
   * \param data_type: `string` Memory data type identifier.
   * \param buffer: `hid_t` Starting address of the memory sector to be written.
   * \param mem_space_id: `hid_t` Memory data space identifier (defaults to `H5S_ALL`).
   * \param file_space_id: `hid_t` File space identifier (defaults to `H5S_ALL`).
   * \param dapl_id: `hid_t` Data access property list identifier (defaults to `H5P_DEFAULT`).
   * \param dxpl_id: `hid_t` Data transfer property list identifier (defaults to `H5P_DEFAULT`).
   * \return status: `herr_t` Exit status of the operation.
   */
  herr_t write(
	       const std::string& dataset_name, const std::string& data_type, const void *buffer,
	       hid_t mem_space_id=H5S_ALL, hid_t file_space_id=H5S_ALL,
	       hid_t dapl_id=H5P_DEFAULT, hid_t dxpl_id=H5P_DEFAULT
  );
};
#endif
