// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef toolx_hdf5_T_tools
#define toolx_hdf5_T_tools

#include "hdf5_h"

#include <tools/typedefs>
#include <tools/forit>
#include <tools/num2s>

namespace toolx {
namespace hdf5 {

inline hid_t to_T_file_type(char)           {return H5T_STD_I8LE;}    //H5T_STD_I8XX()
inline hid_t to_T_file_type(short)          {return H5T_STD_I16LE;}   //HST_STD_I16XX()
inline hid_t to_T_file_type(int)            {return H5T_STD_I32LE;}   //H5T_STD_I32XX()
inline hid_t to_T_file_type(tools::int64)   {return H5T_STD_I64LE;}   //H5T_STD_I64XX()

inline hid_t to_T_file_type(float)          {return H5T_IEEE_F32LE;}  //H5T_IEEE_F32XX()
inline hid_t to_T_file_type(double)         {return H5T_IEEE_F64LE;}  //H5T_IEEE_F64XX()

inline hid_t to_T_file_type(unsigned char)  {return H5T_STD_U8LE;}
inline hid_t to_T_file_type(unsigned short) {return H5T_STD_U16LE;}
inline hid_t to_T_file_type(unsigned int)   {return H5T_STD_U32LE;}
inline hid_t to_T_file_type(tools::uint64)  {return H5T_STD_U64LE;}

inline hid_t to_T_mem_type(char)           {return H5T_NATIVE_CHAR;}
inline hid_t to_T_mem_type(short)          {return H5T_NATIVE_SHORT;}
inline hid_t to_T_mem_type(int)            {return H5T_NATIVE_INT;}
inline hid_t to_T_mem_type(tools::int64)   {return H5T_NATIVE_INT64;}

inline hid_t to_T_mem_type(float)          {return H5T_NATIVE_FLOAT;}
inline hid_t to_T_mem_type(double)         {return H5T_NATIVE_DOUBLE;}

inline hid_t to_T_mem_type(unsigned char)  {return H5T_NATIVE_UCHAR;}
inline hid_t to_T_mem_type(unsigned short) {return H5T_NATIVE_USHORT;}
inline hid_t to_T_mem_type(unsigned int)   {return H5T_NATIVE_UINT;}
inline hid_t to_T_mem_type(tools::uint64)  {return H5T_NATIVE_UINT64;}

template <class T>
inline bool write_array(hid_t a_loc,const std::string& a_name,
                        hid_t a_file_type,hid_t a_mem_type,
                        unsigned int a_chunked,unsigned int a_compress,
                        unsigned int a_size,const T a_array[]) {
  if(!a_size) return false;

  hid_t cpt = -1;
  if(a_compress || a_chunked) {
    cpt = ::H5Pcreate(H5P_DATASET_CREATE);
    if(cpt<0) return false;
    if(a_chunked) {
      if(H5Pset_layout(cpt,H5D_CHUNKED)<0) {
        ::H5Pclose(cpt);
        return false;
      }
      hsize_t cdims[1];
      cdims[0] = a_chunked;
      if(H5Pset_chunk(cpt,1,cdims)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    } else {
      if(H5Pset_layout(cpt,H5D_COMPACT)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    }
    if(a_compress) {
      if(H5Pset_deflate(cpt,a_compress>9?9:a_compress)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    }
  } else {
    cpt = H5P_DEFAULT;
  }

  hid_t dataset = -1;

 {hsize_t dims[1];
  dims[0] = a_size;
  hid_t file_space = -1;
  if(a_chunked) {
    hsize_t mx_dims[1];
    mx_dims[0] = H5S_UNLIMITED; //extendable.
    file_space = ::H5Screate_simple(1,dims,mx_dims);
  } else {
    file_space = ::H5Screate_simple(1,dims,NULL);
  }
  if(file_space<0) {if(cpt>=0) ::H5Pclose(cpt);return false;}
  dataset = toolx_H5Dcreate(a_loc,a_name.c_str(),a_file_type,file_space,cpt);
  if(cpt>=0) ::H5Pclose(cpt);
  ::H5Sclose(file_space);
  if(dataset<0) return false;}

  if(H5Dwrite(dataset,a_mem_type,H5S_ALL,H5S_ALL,H5P_DEFAULT,a_array)<0) {
    ::H5Dclose(dataset);
    return false;
  }
  ::H5Dclose(dataset);

  return true;
}

template <class T>
inline bool write_vlen(hid_t a_loc,const std::string& a_name,
                       hid_t a_file_type,hid_t a_mem_type,
                       unsigned int a_chunked,unsigned int a_compress,
                       unsigned int a_size,const T a_array[]) {
  hid_t cpt = -1;
  if(a_compress || a_chunked) {
    cpt = ::H5Pcreate(H5P_DATASET_CREATE);
    if(cpt<0) return false;
    if(a_chunked) {
      if(H5Pset_layout(cpt,H5D_CHUNKED)<0) {
        ::H5Pclose(cpt);
        return false;
      }
      hsize_t cdims[1];
      cdims[0] = a_chunked;
      if(H5Pset_chunk(cpt,1,cdims)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    } else {
      if(H5Pset_layout(cpt,H5D_COMPACT)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    }
    if(a_compress) {
      if(H5Pset_deflate(cpt,a_compress>9?9:a_compress)<0) {
        ::H5Pclose(cpt);
        return false;
      }
    }
  } else {
    cpt = H5P_DEFAULT;
  }

  hid_t dataset = -1;

 {hsize_t dims[1];
  dims[0] = 1;
  hid_t file_space = -1;
  if(a_chunked) {
    hsize_t mx_dims[1];
    mx_dims[0] = H5S_UNLIMITED; //extendable.
    file_space = ::H5Screate_simple(1,dims,mx_dims);
  } else {
    file_space = ::H5Screate_simple(1,dims,NULL);
  }
  if(file_space<0) {if(cpt>=0) ::H5Pclose(cpt);return false;}

  hid_t file_type = ::H5Tvlen_create(a_file_type);

  dataset = toolx_H5Dcreate(a_loc,a_name.c_str(),file_type,file_space,cpt);
  if(cpt>=0) ::H5Pclose(cpt);
  ::H5Sclose(file_space);
  ::H5Tclose(file_type);
  if(dataset<0) return false;}

  hid_t mem_type = ::H5Tvlen_create(a_mem_type);

  hvl_t wdata[1];
  wdata[0].len = a_size;
  wdata[0].p = (void*)a_array;

  if(H5Dwrite(dataset,mem_type,H5S_ALL,H5S_ALL,H5P_DEFAULT,wdata)<0) {
    ::H5Tclose(mem_type);
    ::H5Dclose(dataset);
    return false;
  }
  ::H5Tclose(mem_type);
  ::H5Dclose(dataset);

  return true;
}

template <class T>
inline bool write_sub_array(hid_t a_loc,const std::string& a_name,
                            hid_t a_file_type,hid_t a_mem_type,
                            bool a_create,
                            unsigned int a_chunked,unsigned int a_compress, // used if a_creat = true;
                            unsigned int a_size,
                            unsigned int a_offset,unsigned int a_number,const T a_array[]) {

  int remain = a_size-a_offset;
  int number = (int(a_number)<=remain) ? int(a_number) : remain;
  if(number<=0) return false;

  hid_t cpt = -1;

  if(a_create) {
    if(a_compress || a_chunked) {
      cpt = ::H5Pcreate(H5P_DATASET_CREATE);
      if(cpt<0) return false;
      if(a_chunked) {
        if(H5Pset_layout(cpt,H5D_CHUNKED)<0) {
          ::H5Pclose(cpt);
          return false;
        }
        hsize_t cdims[1];
        cdims[0] = a_chunked;
        if(H5Pset_chunk(cpt,1,cdims)<0) {
          ::H5Pclose(cpt);
          return false;
        }
      } else {
        if(H5Pset_layout(cpt,H5D_COMPACT)<0) {
          ::H5Pclose(cpt);
          return false;
        }
      }
      if(a_compress) {
        if(H5Pset_deflate(cpt,a_compress>9?9:a_compress)<0) {
          ::H5Pclose(cpt);
          return false;
        }
      }
    } else {
      cpt = H5P_DEFAULT;
    }
  }

  hid_t dataset = -1;

  hid_t file_space = -1;

  if(a_create) {
    hsize_t dims[1];
    dims[0] = a_size;
    file_space = ::H5Screate_simple(1,dims,NULL);
    if(file_space<0) {
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }

   {hsize_t offset[1];
    offset[0] = a_offset;
    hsize_t count[1];
    count[0] = number;
    if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
      ::H5Sclose(file_space);
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }}

    dataset = toolx_H5Dcreate(a_loc,a_name.c_str(),a_file_type,file_space,cpt);
    if(dataset<0) {
      ::H5Sclose(file_space);
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }

  } else { //open an existing dataset :
    dataset = toolx_H5Dopen(a_loc,a_name.c_str());
    if(dataset<0) {
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }

    file_space = H5Dget_space(dataset);
    if(file_space<0) {
      ::H5Dclose(dataset);
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }

   {hsize_t offset[1];
    offset[0] = a_offset;
    hsize_t count[1];
    count[0] = number;
    if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
      ::H5Sclose(file_space);
      ::H5Dclose(dataset);
      if(cpt>=0) ::H5Pclose(cpt);
      return false;
    }}

  }

  hsize_t dims[1];
  dims[0] = number;
  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    if(cpt>=0) ::H5Pclose(cpt);
    return false;
  }

  if(H5Dwrite(dataset,a_mem_type,mem_space,file_space,H5P_DEFAULT,a_array)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    if(cpt>=0) ::H5Pclose(cpt);
    return false;
  }

  ::H5Sclose(file_space);
  ::H5Dclose(dataset);
  if(cpt>=0) ::H5Pclose(cpt);

  return true;
}

template <class T>
inline bool write_append_array_dataset(hid_t a_dataset,hid_t /*a_file_type*/,hid_t a_mem_type,
                                       unsigned int a_number,const T a_array[]) {
  hsize_t old_size = 0;

 {hid_t dataspace = H5Dget_space(a_dataset);
  if(dataspace<0) return false;
  hsize_t dims[1];
  if(H5Sget_simple_extent_dims(dataspace,dims,NULL)<0) {
    ::H5Sclose(dataspace);
    return false;
  }
  old_size = dims[0];
  ::H5Sclose(dataspace);}

 {hsize_t exts[1];
  exts[0] = old_size+a_number;
  if(H5Dset_extent(a_dataset,exts)<0) return false;}

  hid_t file_space = H5Dget_space(a_dataset);
  if(file_space<0) return false;

 {hsize_t offset[1];
  offset[0] = old_size;
  hsize_t count[1];
  count[0] = a_number;
  if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
    ::H5Sclose(file_space);
    return false;
  }}

  hsize_t dims[1];
  dims[0] = a_number;
  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    return false;
  }

  if(H5Dwrite(a_dataset,a_mem_type,mem_space,file_space,H5P_DEFAULT,a_array)<0) {
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    return false;
  }

  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);

  return true;
}

template <class T>
inline bool write_append_vlen_dataset(hid_t a_dataset,hid_t /*a_file_type*/,hid_t a_mem_type,
                                      unsigned int a_number,const T a_array[]) {
  hsize_t old_size = 0;

 {hid_t dataspace = H5Dget_space(a_dataset);
  if(dataspace<0) return false;
  hsize_t dims[1];
  if(H5Sget_simple_extent_dims(dataspace,dims,NULL)<0) {
    ::H5Sclose(dataspace);
    return false;
  }
  old_size = dims[0];
  ::H5Sclose(dataspace);}

 {hsize_t exts[1];
  exts[0] = old_size+1;
  if(H5Dset_extent(a_dataset,exts)<0) return false;}

  hid_t file_space = H5Dget_space(a_dataset);
  if(file_space<0) return false;

 {hsize_t offset[1];
  offset[0] = old_size;
  hsize_t count[1];
  count[0] = 1;
  if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
    ::H5Sclose(file_space);
    return false;
  }}

  hsize_t dims[1];
  dims[0] = 1;
  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    return false;
  }

  hid_t mem_type = ::H5Tvlen_create(a_mem_type);

  hvl_t wdata[1];
  wdata[0].len = a_number;
  wdata[0].p = (void*)a_array;

  if(H5Dwrite(a_dataset,mem_type,mem_space,file_space,H5P_DEFAULT,wdata)<0) {
    ::H5Tclose(mem_type);
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    return false;
  }

  ::H5Tclose(mem_type);
  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);

  return true;
}

template <class T>
inline bool write_append_array(hid_t a_loc,const std::string& a_name,hid_t a_file_type,hid_t a_mem_type,
                               unsigned int a_number,const T a_array[]) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) return false;
  bool status = write_append_array_dataset(dataset,a_file_type,a_mem_type,a_number,a_array);
  ::H5Dclose(dataset);
  return status;
}

template <class T>
inline bool read_scalar(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,T& a_data) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) return false;

  hid_t file_space = H5Dget_space(dataset);
  if(file_space<0) {
    ::H5Dclose(dataset);
    return false;
  }

  hid_t mem_space = ::H5Screate(H5S_SCALAR);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    return false;
  }

  if(H5Dread(dataset,a_mem_type,mem_space,file_space,H5P_DEFAULT,&a_data)<0) {
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    return false;
  }

  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);
  ::H5Dclose(dataset);

  return true;
}

template <class T>
inline bool read_array(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,unsigned int& a_size,T*& a_array,bool a_alloc = true) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) {
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false; // data set not found.
  }

  hid_t file_space = H5Dget_space(dataset);
  if(file_space<0) {
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }

 {int dimn = H5Sget_simple_extent_ndims(file_space);
  if(dimn<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }
  if(dimn!=1) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }
  }

  hsize_t dims[1];
 {if(H5Sget_simple_extent_dims(file_space,dims,NULL)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }}

  a_size = (unsigned int)dims[0];
  if(!a_size) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return true; //It is ok.
  }

  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }

  if(a_alloc) a_array = new T[a_size];
  if(H5Dread(dataset,a_mem_type,mem_space,file_space,H5P_DEFAULT,a_array)<0) {
    if(a_alloc) delete [] a_array;
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    if(a_alloc) a_array = 0;
    return false;
  }


  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);
  ::H5Dclose(dataset);

  return true;
}

template <class T>
inline bool read_sub_array(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,
                           unsigned int a_offset,unsigned int a_number,
                           unsigned int& a_size,T*& a_array) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) {
    a_size = 0;
    a_array = 0;
    return false; // data set not found.
  }

  hid_t file_space = H5Dget_space(dataset);
  if(file_space<0) {
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

 {int dimn = H5Sget_simple_extent_ndims(file_space);
  if(dimn<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }
  if(dimn!=1) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }
  }

  hsize_t dims[1];
 {if(H5Sget_simple_extent_dims(file_space,dims,NULL)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }}

  unsigned int sz = (unsigned int)dims[0];
  if(!sz) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return true; //It is ok.
  }

  //  abcdef
  //  012345
  int remain = sz-a_offset;
  if(remain<=0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return a_number?false:true;
  }

  int number = (int(a_number)<=remain) ? int(a_number) : remain;
  if(number<=0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return true; //It is ok.
  }

 {hsize_t offset[1];
  offset[0] = a_offset;
  hsize_t count[1];
  count[0] = number;
  if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }}

  dims[0] = number;
  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

  a_array = new T[number];
  if(H5Dread(dataset,a_mem_type,mem_space,file_space,H5P_DEFAULT,a_array)<0) {
    delete [] a_array;
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_array = 0;
    return false;
  }

  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);
  ::H5Dclose(dataset);

  a_size = number;

  return true;
}

template <class T>
inline bool read_sub_vlen(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,
                          unsigned int a_offset,
                          unsigned int& a_size,T*& a_array) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) {
    a_size = 0;
    a_array = 0;
    return false; // data set not found.
  }

  hid_t file_space = H5Dget_space(dataset);
  if(file_space<0) {
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

 {int dimn = H5Sget_simple_extent_ndims(file_space);
  if(dimn<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }
  if(dimn!=1) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }
  }

  hsize_t dims[1];
 {if(H5Sget_simple_extent_dims(file_space,dims,NULL)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }}

  unsigned int sz = (unsigned int)dims[0];
  if(!sz) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return true; //It is ok.
  }

  //  abcdef
  //  012345
  int remain = sz-a_offset;
  if(remain<=0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

 {hsize_t offset[1];
  offset[0] = a_offset;
  hsize_t count[1];
  count[0] = 1;
  if(H5Sselect_hyperslab(file_space,H5S_SELECT_SET,offset,NULL,count,NULL)<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }}

  dims[0] = 1;
  hid_t mem_space = ::H5Screate_simple(1,dims,NULL);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

  hid_t mem_type = ::H5Tvlen_create(a_mem_type);

  hvl_t rdata[1];
  if(H5Dread(dataset,mem_type,mem_space,file_space,H5P_DEFAULT,rdata)<0) {
    ::H5Tclose(mem_type);
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    a_size = 0;
    a_array = 0;
    return false;
  }

  hsize_t len = rdata[0].len;
  if(!len) {
    a_array = 0; //it is ok.
  } else {
    a_array = new T[len];
    T* _data = (T*)rdata[0].p;
    T* pos = a_array;
    for(hsize_t index=0;index<len;index++,pos++,_data++) *pos = *_data;
  }
  a_size = len;

  ::H5Dvlen_reclaim(mem_type,mem_space,H5P_DEFAULT,rdata);

  ::H5Tclose(mem_type);
  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);
  ::H5Dclose(dataset);

  return true;
}

template <class TYPE>
inline bool read_scalar(hid_t a_loc,const std::string& a_name,TYPE& aValue) {
  return read_scalar<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),aValue);
}

template <class T>
inline bool read_std_vec(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,std::vector<T>& a_vec) {
  tools::uint64 sz;
  if(!read_scalar<tools::uint64>(a_loc,a_name+"_size",sz)) return false;
  if(!sz) {a_vec.clear();return true;} //it is ok.
  a_vec.resize((size_t)sz);
  T* data = a_vec.data();
  unsigned int _sz;
  if(!read_array(a_loc,a_name,a_mem_type,_sz,data,false)) return false; //false = do not alloc.
  if(tools::uint64(_sz)!=sz) {a_vec.clear();return false;}
  return true;
}

template <class TYPE>
inline bool read_std_vec_vec(hid_t a_loc,const std::string& a_name,hid_t a_mem_type,std::vector< std::vector<TYPE> >& a_vec_vec) {
  tools::uint64 sz;
  if(!read_scalar<tools::uint64>(a_loc,a_name+"_size",sz)) {a_vec_vec.clear();return false;}
  a_vec_vec.resize((size_t)sz);
  std::string scount;
  for(size_t count=0;count<(size_t)sz;count++) {
    tools::num2s(tools::uint64(count),scount);
    if(!read_std_vec<TYPE>(a_loc,a_name+"_elem_"+scount,a_mem_type,a_vec_vec[count])) {a_vec_vec.clear();return false;}
  }
  return true;
}

template <class T>
inline bool read_struct(hid_t a_loc,const std::string& a_name,hid_t aReadType,T& a_data) {
  hid_t dataset = toolx_H5Dopen(a_loc,a_name.c_str());
  if(dataset<0) return false;

  hid_t file_space = H5Dget_space(dataset);
  if(file_space<0) {
    ::H5Dclose(dataset);
    return false;
  }

  hid_t mem_space = ::H5Screate(H5S_SCALAR);
  if(mem_space<0) {
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    return false;
  }

  if(H5Dread(dataset,aReadType,mem_space,file_space,H5P_DEFAULT,&a_data)<0) {
    ::H5Sclose(mem_space);
    ::H5Sclose(file_space);
    ::H5Dclose(dataset);
    return false;
  }

  ::H5Sclose(mem_space);
  ::H5Sclose(file_space);
  ::H5Dclose(dataset);
  return true;
}

template <class TYPE>
inline bool write_scalar(hid_t a_loc,const std::string& a_name,const TYPE& aData) {
  hid_t scalar = ::H5Screate(H5S_SCALAR);
  if(scalar<0) return false;

  hid_t compact = ::H5Pcreate(H5P_DATASET_CREATE);
  if(compact<0) {
    ::H5Sclose(scalar);
    return false;
  }

  if(H5Pset_layout(compact,H5D_COMPACT)<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    return false;
  }

  hid_t dataset = toolx_H5Dcreate(a_loc,a_name.c_str(),to_T_file_type(TYPE()),scalar,compact);
  if(dataset<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    return false;
  }

  if(::H5Dwrite(dataset,to_T_mem_type(TYPE()),H5S_ALL,H5S_ALL,H5P_DEFAULT,&aData)<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    ::H5Dclose(dataset);
    return false;
  }

  ::H5Pclose(compact);
  ::H5Sclose(scalar);
  ::H5Dclose(dataset);
  return true;
}

template <class T>
inline bool write_struct(hid_t a_loc,const std::string& a_name,
                         hid_t a_create_type,hid_t aWriteType,const T& aData) {
  hid_t scalar = ::H5Screate(H5S_SCALAR);
  if(scalar<0) return false;

  hid_t compact = ::H5Pcreate(H5P_DATASET_CREATE);
  if(compact<0) {
    ::H5Sclose(scalar);
    return false;
  }

  if(H5Pset_layout(compact,H5D_COMPACT)<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    return false;
  }

  hid_t dataset = toolx_H5Dcreate(a_loc,a_name.c_str(),a_create_type,scalar,compact);
  if(dataset<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    return false;
  }

  if(H5Dwrite(dataset,aWriteType,H5S_ALL,H5S_ALL,H5P_DEFAULT,&aData)<0) {
    ::H5Pclose(compact);
    ::H5Sclose(scalar);
    ::H5Dclose(dataset);
    return false;
  }

  ::H5Pclose(compact);
  ::H5Sclose(scalar);
  ::H5Dclose(dataset);
  return true;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
template <class TYPE>
inline bool write_array(hid_t a_loc,const std::string& a_name,
                        unsigned int a_size,const TYPE a_array[],
                        unsigned int a_chunked = 0,unsigned int a_compress = 0) {
  return hdf5::write_array<TYPE>(a_loc,a_name,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),
                                 a_chunked,a_compress,a_size,a_array);
}

template <class TYPE>
inline bool write_vlen(hid_t a_loc,const std::string& a_name,
                       unsigned int a_size,const TYPE a_array[],
                       unsigned int a_chunked = 0,unsigned int a_compress = 0) {
  return hdf5::write_vlen<TYPE>(a_loc,a_name,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),
                                a_chunked,a_compress,a_size,a_array);
}

template <class T>
inline bool write_std_vec(hid_t a_loc,const std::string& a_name,
                          hid_t a_file_type,hid_t a_mem_type,
                          unsigned int a_chunked,unsigned int a_compress,
                          const std::vector<T>& a_vec) {
  if(!write_scalar<tools::uint64>(a_loc,a_name+"_size",a_vec.size())) return false;
  if(a_vec.empty()) return true; //it is ok.
  const T* data = a_vec.data();
  return write_array(a_loc,a_name,a_file_type,a_mem_type,a_chunked,a_compress,a_vec.size(),data);
}

template <class TYPE>
inline bool write_std_vec(hid_t a_loc,const std::string& a_name,const std::vector<TYPE>& a_array,
                          unsigned int a_chunked = 0,unsigned int a_compress = 0) {
  return hdf5::write_std_vec<TYPE>(a_loc,a_name,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),a_chunked,a_compress,a_array);
}

template <class TYPE>
inline bool write_std_vec_vec(hid_t a_loc,const std::string& a_name,const std::vector< std::vector<TYPE> >& a_vec_vec,
                              unsigned int /*a_chunked*/ = 0,unsigned int /*a_compress*/ = 0) {
  if(!write_scalar<tools::uint64>(a_loc,a_name+"_size",a_vec_vec.size())) return false;
  unsigned int count = 0; //uint for num2s.
  std::string scount;
  for(auto it = a_vec_vec.cbegin(); it != a_vec_vec.cend(); ++it) {
    tools::num2s(count,scount);
    if(!write_std_vec<TYPE>(a_loc,a_name+"_elem_"+scount,*it)) return false;
    count++;
  }
  return true;
}

template <class TYPE>
inline bool write_sub_array(hid_t a_loc,const std::string& a_name,
                     unsigned int a_size,unsigned int a_offset,unsigned int a_number,const TYPE a_array[],
                     bool a_create = true,unsigned int a_chunked = 0,unsigned int a_compress = 0) {
  return hdf5::write_sub_array<TYPE>(a_loc,a_name,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),
                                     a_create,a_chunked,a_compress,
                                     a_size,a_offset,a_number,a_array);
}

template <class TYPE>
inline bool write_append_array_dataset(hid_t a_dataset,unsigned int a_number,const TYPE a_array[]) {
  return hdf5::write_append_array_dataset<TYPE>(a_dataset,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),a_number,a_array);
}

template <class TYPE>
inline bool write_append_vlen_dataset(hid_t a_dataset,unsigned int a_number,const TYPE a_array[]) {
  return hdf5::write_append_vlen_dataset<TYPE>(a_dataset,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),a_number,a_array);
}

template <class TYPE>
inline bool write_append_array(hid_t a_loc,const std::string& a_name,unsigned int a_number,const TYPE a_array[]) {
  return hdf5::write_append_array<TYPE>(a_loc,a_name,to_T_file_type(TYPE()),to_T_mem_type(TYPE()),a_number,a_array);
}

template <class TYPE>
inline bool read_array(hid_t a_loc,const std::string& a_name,unsigned int& a_size,TYPE*& a_array) {
  return read_array<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),a_size,a_array);
}

template <class TYPE>
inline bool read_std_vec(hid_t a_loc,const std::string& a_name,std::vector<TYPE>& a_vec) {
  return read_std_vec<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),a_vec);
}

template <class TYPE>
inline bool read_std_vec_vec(hid_t a_loc,const std::string& a_name,std::vector< std::vector<TYPE> >& a_vec_vec) {
  return read_std_vec_vec<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),a_vec_vec);
}

template <class TYPE>
inline bool read_sub_array(hid_t a_loc,const std::string& a_name,unsigned int a_offset,unsigned int a_number,
                           unsigned int& a_size,TYPE*& a_array) {
  return read_sub_array<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),a_offset,a_number,a_size,a_array);
}

template <class TYPE>
inline bool read_sub_vlen(hid_t a_loc,const std::string& a_name,unsigned int a_offset,
                          unsigned int& a_size,TYPE*& a_array) {
  return read_sub_vlen<TYPE>(a_loc,a_name,to_T_mem_type(TYPE()),a_offset,a_size,a_array);
}

inline bool read_bool(hid_t a_loc,const std::string& a_name,bool& aValue) {
  unsigned char value = 0;
  if(!read_scalar<unsigned char>(a_loc,a_name,H5T_NATIVE_UCHAR,value)) {
    aValue = false;
    return false;
  }
  if((value!=0) && (value!=1)) {
    aValue = false;
    return false;
  }
  aValue = (value==1?true:false);
  return true;
}

}}

#include "atb"

namespace toolx {
namespace hdf5 {

template <class TYPE>
inline bool write_scalar_atb(hid_t aDS,const std::string& a_name,const TYPE& aData) {
  int has_attr = H5LT_find_attribute(aDS,a_name.c_str());
  if(has_attr==1)  {
    if(H5Adelete(aDS,a_name.c_str())<0) return false;
  }

  hid_t scalar = ::H5Screate(H5S_SCALAR);
  if(scalar<0) return false;

  hid_t aid = toolx_H5Acreate(aDS,a_name.c_str(),to_T_file_type(TYPE()),scalar,H5P_DEFAULT);
  if(aid<0) {
    ::H5Sclose(scalar);
    return false;
  }

  if(H5Awrite(aid,to_T_mem_type(TYPE()),&aData)<0) {
    ::H5Sclose(scalar);
    ::H5Aclose(aid);
    return false;
  }

  ::H5Sclose(scalar);
  ::H5Aclose(aid);

  return true;
}

}}

#endif
