text: /*
  # <<BEGIN-copyright>>
  # Copyright 2019, Lawrence Livermore National Security, LLC.
  # See the top-level COPYRIGHT file for details.
  # 
  # SPDX-License-Identifier: MIT
  # <<END-copyright>>
  */
  
  #ifndef HAPI_hpp_included
  #define HAPI_hpp_included 1
  
  #include <string>
  #include <stdlib.h>
  #include <vector>
  #include <stdexcept>
  
  #include <nf_buffer.h>
  
  
  #ifdef HAPI_USE_PUGIXML
  #include <pugixml.hpp>
  #include <nf_utilities.h>
  #endif
  
  #ifdef HAPI_USE_HDF5
  #include <hdf5.h>
  #include <time.h>
  #endif
  
  namespace HAPI {
  
  // container classes for reading in from various data sources:
  
  /*
  ============================================================
  ========================= Attribute ========================
  ============================================================
   */
  
  class Node_internal;
  class Attribute {
  
      private:
          Node_internal *m_node;
          std::string m_name;
          //std::string m_value;
  
      public:
          inline Attribute() : m_node(nullptr), m_name() {}
  
          inline Attribute(Node_internal *a_node, std::string const a_name) :
            m_node(a_node),
            m_name(a_name)
          {
          }
          ~Attribute() = default;
          //std::string const &name() const { return( m_name ); }
          inline std::string const value() const;
          inline int as_int() const;
          inline long as_long() const;
          inline double as_double() const;
  };
  
  /*
  ============================================================
  =========================== Text ===========================
  ============================================================
   */
  class Text {
  
      private:
          std::string m_text;
  
      public:
          Text();
          Text(std::string const a_text);
          ~Text();
          std::string const &get() const { return( m_text ); }
  };
  
  /*
  ============================================================
  ================== Data_internal (base class) ==============
  ============================================================
   */
  class Data_internal {
  
      public:
          Data_internal() { };
          virtual ~Data_internal() = 0;
          //std::string const getDataType();
          //virtual template <typename T> T read() = 0;
          virtual void getDoubles(nf_Buffer<double> &buffer) = 0;
          virtual void getInts(nf_Buffer<int> &buffer) = 0;
          virtual int length() const = 0;
  };
  
  /*
  ============================================================
  =================== Node_internal (base class) =============
  ============================================================
   */
  class Node_internal {
  
      public:
          Node_internal() { };
          virtual ~Node_internal() = 0;
          virtual std::string attribute(const char* name) = 0;
          virtual int attribute_as_int(const char* name) = 0;
          virtual long attribute_as_long(const char* name) = 0;
          virtual double attribute_as_double(const char* name) = 0;
          virtual Node_internal *child(const char* name) = 0;
          virtual Node_internal *first_child() = 0;
          virtual Node_internal *next_sibling() = 0;
          virtual void to_next_sibling() = 0;
          virtual Node_internal *copy() = 0;
          virtual std::string name() const = 0;
          virtual bool empty() const = 0;
          virtual Text text() const = 0;
          virtual Data_internal *data() const = 0;
  };
  
  
  inline std::string const Attribute::value() const { return( m_node->attribute( m_name.c_str()) ); }
  inline int Attribute::as_int() const { return( m_node->attribute_as_int(m_name.c_str()) ); }
  inline long Attribute::as_long() const { return( m_node->attribute_as_long(m_name.c_str()) ); }
  inline double Attribute::as_double() const { return( m_node->attribute_as_double(m_name.c_str()) ); }
  
  /*
  ============================================================
  =========================== Data ===========================
  ============================================================
   */
  class Data {
  
      private:
          Data_internal *m_data;
  
      public:
          Data();
          Data( Data_internal *a_data );
          ~Data();
          void getDoubles(nf_Buffer<double> &buffer);
          void getInts(nf_Buffer<int> &buffer);
          int length() const;
  };
  
  /*
  ============================================================
  ============================ Node ==========================
  ============================================================
   */
  class Node {
  
      private:
          Node_internal *m_node;
  
      public:
          Node();
          Node( Node_internal *a_node );
          ~Node();
          inline Attribute attribute(const char* a_name) const{
              return Attribute(m_node, a_name);
          }
          inline std::string attribute_as_string(const char* a_name) const{
              if(m_node == nullptr){
                return "";
              }
              return m_node->attribute(a_name);
          }
          inline int attribute_as_int(const char* a_name) const{
            if(m_node == nullptr){
              return 0;
            }
            return m_node->attribute_as_int(a_name);
          }
          inline long attribute_as_long(const char* a_name) const{
            if(m_node == nullptr){
              return 0;
            }
            return m_node->attribute_as_long(a_name);
          }
          inline double attribute_as_double(const char* a_name) const{
            if(m_node == nullptr){
              return 0.0;
            }
            return m_node->attribute_as_double(a_name);
          }
          Node child(const char* name) const;
          Node first_child() const;
          Node next_sibling() const;
          void to_next_sibling() const;
          Node &operator=(const Node &other);
          std::string name() const;
          bool empty() const;
          Text text() const;
          Data data() const;
  };
  
  
  /*
  ============================================================
  ======================= File (base class) ==================
  ============================================================
   */
  class File {
  
      public:
          File() { };
          virtual ~File() = 0;
          virtual Node child(const char* name) = 0;
          virtual Node first_child() = 0;
          virtual std::string name() const = 0;
  };
  
  
  /*
  ============================================================
  =============== Data Manager (for hybrid files) ============
  ============================================================
   */
  class DataManager {
  
      public:
          DataManager() {};
          virtual ~DataManager() {};
          static DataManager* m_instance;
      
      public:
          virtual void getDoubles(nf_Buffer<double> &result, size_t startIndex, size_t endIndex) = 0;
          virtual void getInts(nf_Buffer<int> &result, size_t startIndex, size_t endIndex) = 0;
  };
  
  
  #ifdef HAPI_USE_PUGIXML
  /*
  ============================================================
  ===================== XML using Pugi =======================
  ============================================================
   */
  class PugiXMLNode : public Node_internal {
  
      private:
          pugi::xml_node m_node;
  
      public:
          PugiXMLNode();
          PugiXMLNode(pugi::xml_node a_node);
          PugiXMLNode(const PugiXMLNode &other);
          virtual ~PugiXMLNode();
          std::string attribute(const char* name);
          int attribute_as_int(const char* name);
          long attribute_as_long(const char* name);
          double attribute_as_double(const char* name);
          Node_internal *child(char const *name);
          Node_internal *first_child();
          Node_internal *next_sibling();
          void to_next_sibling();
          Node_internal *copy();
          Node_internal &operator=(const PugiXMLNode &other);
          std::string name() const;
          bool empty() const;
          Text text() const;
          Data_internal *data() const;
  };
  
  class PugiXMLData : public Data_internal {
  
      private:
          pugi::xml_node m_node;
          int m_length;
  
      public:
          PugiXMLData();
          PugiXMLData(pugi::xml_node a_node);
          virtual ~PugiXMLData();
          void getDoubles(nf_Buffer<double> &buffer);
          void getInts(nf_Buffer<int> &buffer);
          int length() const;
  };
  
  class PugiXMLFile : public File {
  
      private:
          std::string m_name;
          pugi::xml_document m_doc;
  
      public:
          PugiXMLFile();
          PugiXMLFile(char const *filename);
          virtual ~PugiXMLFile();
          Node child(char const *name);
          Node first_child();
          std::string name() const;
  };
  #endif
  
  
  #ifdef HAPI_USE_HDF5
  /*
  ============================================================
  =========================== HDF ============================
  ============================================================
   */
  typedef struct {
      std::string name;
      std::string xmlName;  // HDF sometimes mangles names, need original name here
      size_t index;
      hid_t node_id;
  } childInfo;
  
  class HDFNode : public Node_internal {
  
      private:
          hid_t m_node_id;
          hid_t m_parent_id;
          size_t m_index;
          std::vector<childInfo> m_siblings;
          std::vector<childInfo> m_children;
  
      public:
          HDFNode();
          HDFNode(hid_t a_node_id, hid_t a_parent_id, size_t a_index, std::vector<childInfo> a_siblings);
          explicit HDFNode(hid_t a_file_id);
          HDFNode(const HDFNode &other);
          virtual ~HDFNode();
          //Attribute attribute(char const *name);
          std::string attribute(const char* name);
          int attribute_as_int(const char* name);
          long attribute_as_long(const char* name);
          double attribute_as_double(const char* name);
          Node_internal *child(char const *name);
          Node_internal *first_child();
          Node_internal *next_sibling();
          void to_next_sibling();
          Node_internal *copy();
          Node_internal &operator=(const HDFNode &other);
          std::string name() const;
          bool empty() const;
          Text text() const;
          Data_internal *data() const;
  
  };
  
  class HDFData : public Data_internal {
  
      private:
          hid_t m_node_id;
          hid_t m_dataspace_id;
          int m_length;
  
      public:
          HDFData();
          explicit HDFData(hid_t node_id);
          virtual ~HDFData();
          void getDoubles(nf_Buffer<double> &buffer);
          void getInts(nf_Buffer<int> &buffer);
          int length() const;
  };
  
  class HDFFile : public File {
  
      private:
          std::string m_name;
          hid_t m_doc;
          HDFNode *m_doc_as_node;
  
      public:
          HDFFile();
          explicit HDFFile(char const *filename);
          virtual ~HDFFile();
          Node child(char const *name);
          Node first_child();
          std::string name() const;
  };
  
  class HDFDataManager : public DataManager{
  
      private:
          hid_t m_file_id;
          hid_t m_dataset_ints, m_dataset_doubles;
          hid_t m_dataspace_ints, m_dataspace_doubles;
  
          hsize_t m_stride[1], m_block[1];
  
          size_t m_num_double_reads;
          size_t m_num_double_elem;
          size_t m_num_int_reads;
          size_t m_num_int_elem;
  
          /* for timing
          using TimeType = timespec;
          TimeType m_tstart;
           */
      public:
          HDFDataManager(std::string filename);
          virtual ~HDFDataManager();
          virtual void getDoubles(nf_Buffer<double> &result, size_t startIndex, size_t endIndex);
          virtual void getInts(nf_Buffer<int> &result, size_t startIndex, size_t endIndex);
  };
  #endif
  
  }               // end of namespace 'HAPI'
  
  #endif		// End of HAPI_hpp_included
  