#include "ncGroup.h"
#include "ncVar.h"
#include "ncDim.h"
#include "ncVlenType.h"
#include "ncCompoundType.h"
#include "ncOpaqueType.h"
#include "ncGroupAtt.h"
#include "ncByte.h"
#include "ncUbyte.h"
#include "ncChar.h"
#include "ncShort.h"
#include "ncUshort.h"
#include "ncInt.h"
#include "ncUint.h"
#include "ncInt64.h"
#include "ncUint64.h"
#include "ncFloat.h"
#include "ncDouble.h"
#include "ncString.h"
#include <ncException.h>
#include "ncCheck.h"
using namespace std;
using namespace netCDF::exceptions;

namespace netCDF {
  //  Global comparator operator ==============
  // comparator operator
  bool operator<(const NcGroup& lhs,const NcGroup& rhs)
  {
    return false;
  }

  // comparator operator
  bool operator>(const NcGroup& lhs,const NcGroup& rhs)
  {
    return true;
  }
}

using namespace netCDF;

/////////////////////////////////////////////

NcGroup::~NcGroup()
{
}

// Constructor generates a null object.
NcGroup::NcGroup() :
  nullObject(true),
  myId(-1)
{}


// constructor
NcGroup::NcGroup(const int groupId) :
  nullObject(false),
  myId(groupId)
{ }

// assignment operator
NcGroup& NcGroup::operator=(const NcGroup & rhs)
{
  nullObject = rhs.nullObject;
  myId = rhs.myId;
  return *this;
}

// The copy constructor.
NcGroup::NcGroup(const NcGroup& rhs):
  nullObject(rhs.nullObject),
  myId(rhs.myId)
{}


// equivalence operator
bool NcGroup::operator==(const NcGroup & rhs) const
{
  if(nullObject)
    return nullObject == rhs.nullObject;
  else
    return myId == rhs.myId;
}

//  !=  operator
bool NcGroup::operator!=(const NcGroup & rhs) const
{
  return !(*this == rhs);
}


// /////////////
// NcGroup-related methods
// /////////////

// Get the group name.
string NcGroup::getName(bool fullName) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getName on a Null group",__FILE__,__LINE__);
  string groupName;
  if(fullName){
    // return full name of group with foward "/" separarating sub-groups.
    size_t lenp;
    ncCheck(nc_inq_grpname_len(myId,&lenp),__FILE__,__LINE__);
    char* charName= new char[lenp+1];
    ncCheck(nc_inq_grpname_full(myId,&lenp,charName),__FILE__,__LINE__);
    groupName = charName;
    delete charName;
  }
  else {
    // return the (local) name of this group.
    char charName[NC_MAX_NAME+1];
    ncCheck(nc_inq_grpname(myId,charName),__FILE__,__LINE__);
    groupName = charName;
  }
  return groupName;
}

// returns true if this is the root group.
bool NcGroup::isRootGroup()  const{
  bool result = getName() == "/";
  return result;
}

// Get the parent group.
NcGroup NcGroup::getParentGroup() const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getParentGroup on a Null group",__FILE__,__LINE__);
  try {
    int parentId;
    ncCheck(nc_inq_grp_parent(myId,&parentId),__FILE__,__LINE__);
    NcGroup ncGroupParent(parentId);
    return ncGroupParent;
  }
  catch (NcEnoGrp& e) {
    // no group found, so return null group
    return NcGroup();
  }
}


// Get the group id.
int  NcGroup::getId() const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getId on a Null group",__FILE__,__LINE__);
  return myId;
}

// Get the number of NcGroup objects.
int NcGroup::getGroupCount(NcGroup::GroupLocation location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getGroupCount on a Null group",__FILE__,__LINE__);
  // initialize group counter
  int ngroups=0;

  // record this group
  if(location == ParentsAndCurrentGrps || location == AllGrps) {
    ngroups ++;
  }

  // number of children in current group
  if(location == ChildrenGrps || location == AllChildrenGrps || location == AllGrps ) {
    int numgrps;
    int* ncids=NULL;
    ncCheck(nc_inq_grps(getId(), &numgrps,ncids),__FILE__,__LINE__);
    ngroups += numgrps;
  }

  // search in parent groups
  if(location == ParentsGrps || location == ParentsAndCurrentGrps || location == AllGrps ) {
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    ngroups += groups.size();
  }


  // get the number of all children that are childreof children
  if(location == ChildrenOfChildrenGrps || location == AllChildrenGrps || location == AllGrps ) {
    multimap<string,NcGroup> groups(getGroups(ChildrenOfChildrenGrps));
    ngroups += groups.size();
  }

  return ngroups;
}


// Get the set of child NcGroup objects.
multimap<std::string,NcGroup> NcGroup::getGroups(NcGroup::GroupLocation location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getGroups on a Null group",__FILE__,__LINE__);
  // create a container to hold the NcGroup's.
  multimap<string,NcGroup> ncGroups;

  // record this group
  if(location == ParentsAndCurrentGrps || location == AllGrps) {
    ncGroups.insert(pair<const string,NcGroup>(getName(),*this));
  }

  // the child groups of the current group
  if(location == ChildrenGrps || location == AllChildrenGrps || location == AllGrps ) {
    // get the number of groups
    int groupCount = getGroupCount();
    if (groupCount){
      vector<int> ncids(groupCount);
      int* numgrps=NULL;
      // now get the id of each NcGroup and populate the ncGroups container.
      ncCheck(nc_inq_grps(myId, numgrps,&ncids[0]),__FILE__,__LINE__);
      for(int i=0; i<groupCount;i++){
        NcGroup tmpGroup(ncids[i]);
        ncGroups.insert(pair<const string,NcGroup>(tmpGroup.getName(),tmpGroup));
      }
    }
  }

  // search in parent groups.
  if(location == ParentsGrps || location == ParentsAndCurrentGrps || location == AllGrps ) {
    NcGroup tmpGroup(*this);
    if(!tmpGroup.isRootGroup()) {
      while(1) {
	const NcGroup parentGroup(tmpGroup.getParentGroup());
	if(parentGroup.isNull()) break;
	ncGroups.insert(pair<const string,NcGroup>(parentGroup.getName(),parentGroup));
	tmpGroup=parentGroup;
      }
    }
  }

  // search in child groups of the children
  if(location == ChildrenOfChildrenGrps || location == AllChildrenGrps || location == AllGrps ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcGroup> childGroups(it->second.getGroups(AllChildrenGrps));
      ncGroups.insert(childGroups.begin(),childGroups.end());
    }
  }

  return ncGroups;
}

// Get the named child NcGroup object.
NcGroup NcGroup::getGroup(const string& name,NcGroup::GroupLocation location) const{
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getGroup on a Null group",__FILE__,__LINE__);
  multimap<string,NcGroup> ncGroups(getGroups(location));
  pair<multimap<string,NcGroup>::iterator,multimap<string,NcGroup>::iterator> ret;
  ret = ncGroups.equal_range(name);
  if(ret.first == ret.second)
    return NcGroup();  // null group is returned
  else
    return ret.first->second;
}



// Get all NcGroup objects with a given name.
set<NcGroup> NcGroup::getGroups(const std::string& name,NcGroup::GroupLocation location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getGroups on a Null group",__FILE__,__LINE__);
  // get the set of ncGroups in this group and above.
  multimap<std::string,NcGroup> ncGroups(getGroups(location));
  pair<multimap<string,NcGroup>::iterator,multimap<string,NcGroup>::iterator> ret;
  multimap<string,NcGroup>::iterator it;
  ret = ncGroups.equal_range(name);
  set<NcGroup> tmpGroup;
  for (it=ret.first; it!=ret.second; ++it) {
    tmpGroup.insert(it->second);
  }
  return tmpGroup;
}

// Add a new child NcGroup object.
NcGroup NcGroup::addGroup(const string& name) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::addGroup on a Null group",__FILE__,__LINE__);
  int new_ncid;
  ncCheck(nc_def_grp(myId,const_cast<char*> (name.c_str()),&new_ncid),__FILE__,__LINE__);
  return NcGroup(new_ncid);
}



// /////////////
// NcVar-related accessors
// /////////////

// Get the number of NcVar objects in this group.
int NcGroup::getVarCount(NcGroup::Location location) const {

  // search in current group.
  NcGroup tmpGroup(*this);
  int nvars=0;
  // search in current group
  if((location == ParentsAndCurrent || location == ChildrenAndCurrent || location == Current || location ==All) && !tmpGroup.isNull()) {
    ncCheck(nc_inq_nvars(tmpGroup.getId(), &nvars),__FILE__,__LINE__);
  }

  // search recursively in all parent groups.
  if(location == Parents || location == ParentsAndCurrent || location ==All) {
    tmpGroup=getParentGroup();
    while(!tmpGroup.isNull()) {
      int nvarsp;
      ncCheck(nc_inq_nvars(tmpGroup.getId(), &nvarsp),__FILE__,__LINE__);
      nvars += nvarsp;
      // continue loop with the parent.
      tmpGroup=tmpGroup.getParentGroup();
    }
  }

  // search recursively in all child groups
  if(location == ChildrenAndCurrent || location == Children || location == All) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      nvars += it->second.getVarCount(ChildrenAndCurrent);
    }
  }
  return nvars;
}

// Get the collection of NcVar objects.
multimap<std::string,NcVar> NcGroup::getVars(NcGroup::Location location) const {

  // create a container to hold the NcVar's.
  multimap<string,NcVar> ncVars;

  // search in current group.
  NcGroup tmpGroup(*this);
  if((location == ParentsAndCurrent || location == ChildrenAndCurrent || location == Current || location ==All) && !tmpGroup.isNull()) {
    // get the number of variables.
    int varCount = getVarCount();
    if (varCount){
      // now get the name of each NcVar object and populate the ncVars container.
      int* nvars=NULL;
      vector<int> varids(varCount);
      ncCheck(nc_inq_varids(myId, nvars,&varids[0]),__FILE__,__LINE__);
      for(int i=0; i<varCount;i++){
        NcVar tmpVar(*this,varids[i]);
        ncVars.insert(pair<const string,NcVar>(tmpVar.getName(),tmpVar));
      }
    }
  }


  // search recursively in all parent groups.
  if(location == Parents || location == ParentsAndCurrent || location ==All) {
    tmpGroup=getParentGroup();
    while(!tmpGroup.isNull()) {
      // get the number of variables
      int varCount = tmpGroup.getVarCount();
      if (varCount){
        // now get the name of each NcVar object and populate the ncVars container.
        int* nvars=NULL;
        vector<int> varids(varCount);
        ncCheck(nc_inq_varids(tmpGroup.getId(), nvars,&varids[0]),__FILE__,__LINE__);
        for(int i=0; i<varCount;i++){
          NcVar tmpVar(tmpGroup,varids[i]);
          ncVars.insert(pair<const string,NcVar>(tmpVar.getName(),tmpVar));
        }
      }
      // continue loop with the parent.
      tmpGroup=tmpGroup.getParentGroup();
    }
  }

  // search recusively in all child groups.
  if(location == ChildrenAndCurrent || location == Children  || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcVar> vars=it->second.getVars(ChildrenAndCurrent);
      ncVars.insert(vars.begin(),vars.end());
    }
  }

  return ncVars;
}


// Get all NcVar objects with a given name.
set<NcVar> NcGroup::getVars(const string& name,NcGroup::Location location) const {
  // get the set of ncVars in this group and above.
  multimap<std::string,NcVar> ncVars(getVars(location));
  pair<multimap<string,NcVar>::iterator,multimap<string,NcVar>::iterator> ret;
  multimap<string,NcVar>::iterator it;
  ret = ncVars.equal_range(name);
  set<NcVar> tmpVar;
  for (it=ret.first; it!=ret.second; ++it) {
    tmpVar.insert(it->second);
  }
  return tmpVar;
}



// Get the named NcVar object.
NcVar NcGroup::getVar(const string& name,NcGroup::Location location) const {
  multimap<std::string,NcVar> ncVars(getVars(location));
  pair<multimap<string,NcVar>::iterator,multimap<string,NcVar>::iterator> ret;
  ret = ncVars.equal_range(name);
  if(ret.first == ret.second)
    // no matching netCDF variable found so return null object.
    return NcVar();
  else
    return ret.first->second;
}

// Adds a new netCDF scalar variable.
NcVar NcGroup::addVar(const std::string& name, const NcType& ncType) const {
  return NcGroup::addVar(name, ncType, std::vector<NcDim>());
}

// Add a new netCDF variable.
NcVar NcGroup::addVar(const string& name, const string& typeName, const string& dimName) const {
  ncCheckDefineMode(myId);

  // get an NcType object with the given type name.
  NcType tmpType(getType(typeName,NcGroup::ParentsAndCurrent));
  if(tmpType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar failed: typeName must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // get a NcDim object with the given dimension name
  NcDim tmpDim(getDim(dimName,NcGroup::ParentsAndCurrent));
  if(tmpDim.isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar failed: dimName must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // finally define a new netCDF  variable
  int varId;
  int dimId(tmpDim.getId());
  ncCheck(nc_def_var(myId,name.c_str(),tmpType.getId(),1,&dimId,&varId),__FILE__,__LINE__);
  // return an NcVar object for this new variable
  return NcVar(*this,varId);
}


// Add a new netCDF variable.
NcVar NcGroup::addVar(const string& name, const NcType& ncType, const NcDim& ncDim) const {
  ncCheckDefineMode(myId);

  // check NcType object is valid
  if(ncType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar with a Null NcType object",__FILE__,__LINE__);
  NcType tmpType(getType(ncType.getName(),NcGroup::ParentsAndCurrent));
  if(tmpType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar failed: NcType must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // check NcDim object is valid
  if(ncDim.isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar with a Null NcDim object",__FILE__,__LINE__);
  NcDim tmpDim(getDim(ncDim.getName(),NcGroup::ParentsAndCurrent));
  if(tmpDim.isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar failed: NcDim must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // finally define a new netCDF variable
  int varId;
  int dimId(tmpDim.getId());
  ncCheck(nc_def_var(myId,name.c_str(),tmpType.getId(),1,&dimId,&varId),__FILE__,__LINE__);
  // return an NcVar object for this new variable
  return NcVar(*this,varId);
}


// Add a new netCDF multi-dimensional variable.
NcVar NcGroup::addVar(const string& name, const string& typeName, const vector<string>& dimNames) const {
  ncCheckDefineMode(myId);

  // get an NcType object with the given name.
  NcType tmpType(getType(typeName,NcGroup::ParentsAndCurrent));
  if(tmpType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar failed: typeName must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // get a set of NcDim objects corresponding to the given dimension names.
  vector<int> dimIds;
  dimIds.reserve(dimNames.size());
  for (size_t i=0; i<dimNames.size();i++){
    NcDim tmpDim(getDim(dimNames[i],NcGroup::ParentsAndCurrent));
    if(tmpDim.isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar failed: dimNames must be defined in either the current group or a parent group",__FILE__,__LINE__);
    dimIds.push_back(tmpDim.getId());
  }

  // finally define a new netCDF variable
  int varId;
  int *dimIdsPtr = dimIds.empty() ? 0 : &dimIds[0];
  ncCheck(nc_def_var(myId,name.c_str(),tmpType.getId(),dimIds.size(), dimIdsPtr,&varId),__FILE__,__LINE__);
  // return an NcVar object for this new variable
  return NcVar(*this,varId);
}

// Add a new netCDF multi-dimensional variable.
NcVar NcGroup::addVar(const string& name, const NcType& ncType, const vector<NcDim>& ncDimVector) const {
  ncCheckDefineMode(myId);

  // check NcType object is valid
  if(ncType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar with a Null NcType object",__FILE__,__LINE__);
  NcType tmpType(getType(ncType.getName(),NcGroup::ParentsAndCurrent));
  if(tmpType.isNull()) throw NcNullType("Attempt to invoke NcGroup::addVar failed: NcType must be defined in either the current group or a parent group",__FILE__,__LINE__);

  // check NcDim objects are valid
  vector<NcDim>::const_iterator iter;
  vector<int> dimIds;
  dimIds.reserve(ncDimVector.size());
  for (iter=ncDimVector.begin();iter < ncDimVector.end(); iter++) {
    if(iter->isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar with a Null NcDim object",__FILE__,__LINE__);
    NcDim tmpDim(getDim(iter->getName(),NcGroup::ParentsAndCurrent));
    if(tmpDim.isNull()) throw NcNullDim("Attempt to invoke NcGroup::addVar failed: NcDim must be defined in either the current group or a parent group",__FILE__,__LINE__);
    dimIds.push_back(tmpDim.getId());
  }

  // finally define a new netCDF variable
  int varId;
  int *dimIdsPtr = dimIds.empty() ? 0 : &dimIds[0];
  ncCheck(nc_def_var(myId,name.c_str(),tmpType.getId(),dimIds.size(), dimIdsPtr,&varId),__FILE__,__LINE__);
  // return an NcVar object for this new variable
  return NcVar(*this,varId);
}


// /////////////
// NcAtt-related methods
// /////////////

// Get the number of group attributes.
int NcGroup::getAttCount(NcGroup::Location location) const {

  // search in current group.
  NcGroup tmpGroup(*this);
  int ngatts=0;
  // search in current group
  if((location == ParentsAndCurrent || location == ChildrenAndCurrent || location == Current || location ==All) && !tmpGroup.isNull()) {
    ncCheck(nc_inq_natts(tmpGroup.getId(), &ngatts),__FILE__,__LINE__);
  }

  // search recursively in all parent groups.
  if(location == Parents || location == ParentsAndCurrent || location ==All) {
    tmpGroup=getParentGroup();
    while(!tmpGroup.isNull()) {
      int ngattsp;
      ncCheck(nc_inq_natts(tmpGroup.getId(), &ngattsp),__FILE__,__LINE__);
      ngatts += ngattsp;
      // continue loop with the parent.
      tmpGroup=tmpGroup.getParentGroup();
    }
  }

  // search recursively in all child groups
  if(location == ChildrenAndCurrent || location == Children || location == All) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      ngatts += it->second.getAttCount(ChildrenAndCurrent);
    }
  }

  return ngatts;
}

// Get the collection of NcGroupAtt objects.
multimap<std::string,NcGroupAtt> NcGroup::getAtts(NcGroup::Location location) const {

  // create a container to hold the NcAtt's.
  multimap<string,NcGroupAtt> ncAtts;

  // search in current group.
  NcGroup tmpGroup(*this);
  if((location == ParentsAndCurrent || location == ChildrenAndCurrent || location == Current || location ==All) && !tmpGroup.isNull()) {
    // get the number of attributes
    int attCount = tmpGroup.getAttCount();
    // now get the name of each NcAtt and populate the ncAtts container.
    for(int i=0; i<attCount;i++){
      char charName[NC_MAX_NAME+1];
      ncCheck(nc_inq_attname(tmpGroup.getId(),NC_GLOBAL,i,charName),__FILE__,__LINE__);
      NcGroupAtt tmpAtt(tmpGroup.getId(),i);
      ncAtts.insert(pair<const string,NcGroupAtt>(string(charName),tmpAtt));
    }
  }

  // search recursively in all parent groups.
  if(location == Parents || location == ParentsAndCurrent || location ==All) {
    tmpGroup=getParentGroup();
    while(!tmpGroup.isNull()) {
      // get the number of attributes
      int attCount = tmpGroup.getAttCount();
      // now get the name of each NcAtt and populate the ncAtts container.
      for(int i=0; i<attCount;i++){
        char charName[NC_MAX_NAME+1];
        ncCheck(nc_inq_attname(tmpGroup.getId(),NC_GLOBAL,i,charName),__FILE__,__LINE__);
        NcGroupAtt tmpAtt(tmpGroup.getId(),i);
        ncAtts.insert(pair<const string,NcGroupAtt>(string(charName),tmpAtt));
      }
      // continue loop with the parent.
      tmpGroup=tmpGroup.getParentGroup();
    }
  }

  // search recusively in all child groups.
  if(location == ChildrenAndCurrent || location == Children  || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcGroupAtt> atts=it->second.getAtts(ChildrenAndCurrent);
      ncAtts.insert(atts.begin(),atts.end());
    }
  }

  return ncAtts;
}

// Get the named NcGroupAtt object.
NcGroupAtt NcGroup::getAtt(const std::string& name,NcGroup::Location location) const {
  multimap<std::string,NcGroupAtt> ncAtts(getAtts(location));
  pair<multimap<string,NcGroupAtt>::iterator,multimap<string,NcGroupAtt>::iterator> ret;
  ret = ncAtts.equal_range(name);
  if(ret.first == ret.second)
    // no matching groupAttribute so return null object.
    return NcGroupAtt();
  else
    return ret.first->second;
}

// Get all NcGroupAtt objects with a given name.
set<NcGroupAtt> NcGroup::getAtts(const string& name,NcGroup::Location location) const {
  // get the set of ncGroupAtts in this group and above.
  multimap<std::string,NcGroupAtt> ncAtts(getAtts(location));
  pair<multimap<string,NcGroupAtt>::iterator,multimap<string,NcGroupAtt>::iterator> ret;
  multimap<string,NcGroupAtt>::iterator it;
  ret = ncAtts.equal_range(name);
  set<NcGroupAtt> tmpAtt;
  for (it=ret.first; it!=ret.second; ++it) {
    tmpAtt.insert(it->second);
  }
  return tmpAtt;
}




//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const string& dataValues) const {
  ncCheckDefineMode(myId);
  ncCheck(nc_put_att_text(myId,NC_GLOBAL,name.c_str(),dataValues.size(),dataValues.c_str()),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const unsigned char* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_uchar(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const signed char* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_schar(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, short datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_short(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, int datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_int(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, long datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_long(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, float datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_float(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, double datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_double(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, unsigned short datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_ushort(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, unsigned int datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_uint(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, long long datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_longlong(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, unsigned long long datumValue) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_ulonglong(myId,NC_GLOBAL,name.c_str(),type.getId(),1,&datumValue),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const short* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_short(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const int* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_int(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const long* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_long(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const float* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_float(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const double* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_double(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const unsigned short* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_ushort(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const unsigned int* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_uint(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const long long* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_longlong(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const unsigned long long* dataValues) const {
  ncCheckDefineMode(myId);
  NcType::ncType typeClass(type.getTypeClass());
  if(typeClass == NcType::nc_VLEN || typeClass == NcType::nc_OPAQUE || typeClass == NcType::nc_ENUM || typeClass == NcType::nc_COMPOUND)
    ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  else
    ncCheck(nc_put_att_ulonglong(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}


//  Creates a new NetCDF group attribute or if already exisiting replaces it.
NcGroupAtt NcGroup::putAtt(const string& name, size_t len, const char** dataValues) const {
  ncCheckDefineMode(myId);
  ncCheck(nc_put_att_string(myId,NC_GLOBAL,name.c_str(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}

//  Creates a new NetCDF group attribute or if already exisiting replaces it.
 NcGroupAtt NcGroup::putAtt(const string& name, const NcType& type, size_t len, const void* dataValues) const {
  ncCheckDefineMode(myId);
  ncCheck(nc_put_att(myId,NC_GLOBAL,name.c_str(),type.getId(),len,dataValues),__FILE__,__LINE__);
  // finally instantiate this attribute and return
  return getAtt(name);
}



// /////////////
// NcDim-related methods
// /////////////

// Get the number of NcDim objects.
int NcGroup::getDimCount(NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getDimCount on a Null group",__FILE__,__LINE__);

  // intialize counter
  int ndims=0;

  // search in current group
  if(location == Current || location == ParentsAndCurrent || location == ChildrenAndCurrent || location == All ) {
    int ndimsp;
    ncCheck(nc_inq_ndims(getId(), &ndimsp),__FILE__,__LINE__);
    ndims += ndimsp;
  }

  // search in parent groups.
  if(location == Parents || location == ParentsAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ndims += it->second.getDimCount();
    }
  }

  // search in child groups.
  if(location == Children || location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(AllChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ndims += it->second.getDimCount();
    }
  }
  return ndims;
}


// Get the set of NcDim objects.
multimap<string,NcDim> NcGroup::getDims(NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getDims on a Null group",__FILE__,__LINE__);
  // create a container to hold the NcDim's.
  multimap<string,NcDim> ncDims;

  // search in current group
  if(location == Current || location == ParentsAndCurrent || location == ChildrenAndCurrent || location == All ) {
    int dimCount = getDimCount();
    if (dimCount){
      vector<int> dimids(dimCount);
      ncCheck(nc_inq_dimids(getId(), &dimCount, &dimids[0], 0),__FILE__,__LINE__);
      // now get the name of each NcDim and populate the nDims container.
      for(int i=0; i<dimCount;i++){
        NcDim tmpDim(*this,dimids[i]);
        ncDims.insert(pair<const string,NcDim>(tmpDim.getName(),tmpDim));
      }
    }
  }

  // search in parent groups.
  if(location == Parents || location == ParentsAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcDim> dimTmp(it->second.getDims());
      ncDims.insert(dimTmp.begin(),dimTmp.end());
    }
  }

  // search in child groups (makes recursive calls).
  if(location == Children || location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(AllChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcDim> dimTmp(it->second.getDims());
      ncDims.insert(dimTmp.begin(),dimTmp.end());
    }
  }

  return ncDims;
}



// Get the named NcDim object.
NcDim NcGroup::getDim(const string& name,NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getDim on a Null group",__FILE__,__LINE__);
  multimap<string,NcDim> ncDims(getDims(location));
  pair<multimap<string,NcDim>::iterator,multimap<string,NcDim>::iterator> ret;
  ret = ncDims.equal_range(name);
  if(ret.first == ret.second)
    return NcDim(); // null group is returned
  else
    return ret.first->second;
}


// Get all NcDim objects with a given name.
set<NcDim> NcGroup::getDims(const string& name,NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getDims on a Null group",__FILE__,__LINE__);
  // get the set of ncDims in this group and above.
  multimap<string,NcDim> ncDims(getDims(location));
  pair<multimap<string,NcDim>::iterator,multimap<string,NcDim>::iterator> ret;
  multimap<string,NcDim>::iterator it;
  ret = ncDims.equal_range(name);
  set<NcDim> tmpDim;
  for (it=ret.first; it!=ret.second; ++it) {
    tmpDim.insert(it->second);
  }
  return tmpDim;
}

// Add a new NcDim object.
NcDim NcGroup::addDim(const string& name, size_t dimSize) const {
  ncCheckDefineMode(myId);
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::addDim on a Null group",__FILE__,__LINE__);
  int dimId;
  ncCheck(nc_def_dim(myId,name.c_str(),dimSize,&dimId),__FILE__,__LINE__);
  // finally return NcDim object for this new variable
  return NcDim(*this,dimId);
}

// Add a new NcDim object with unlimited size..
NcDim NcGroup::addDim(const string& name) const {
  ncCheckDefineMode(myId);
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::addDim on a Null group",__FILE__,__LINE__);
  int dimId;
  ncCheck(nc_def_dim(myId,name.c_str(),NC_UNLIMITED,&dimId),__FILE__,__LINE__);
  // finally return NcDim object for this new variable
  return NcDim(*this,dimId);
}





// /////////////
// type-object related methods
// /////////////

// Gets the number of type objects.
int NcGroup::getTypeCount(NcGroup::Location location) const {

  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypeCount on a Null group",__FILE__,__LINE__);

  // intialize counter
  int ntypes=0;

  // search in current group
  if(location == Current || location == ParentsAndCurrent || location == ChildrenAndCurrent || location == All ) {
    int ntypesp;
    int* typeidsp=NULL;
    ncCheck(nc_inq_typeids(getId(), &ntypesp,typeidsp),__FILE__,__LINE__);
    ntypes+= ntypesp;
  }

  // search in parent groups.
  if(location == Parents || location == ParentsAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ntypes += it->second.getTypeCount();
    }
  }

  // search in child groups.
  if(location == Children || location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(AllChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ntypes += it->second.getTypeCount();
    }
  }
  return ntypes;
}



// Gets the number of type objects with a given enumeration type.
int NcGroup::getTypeCount(NcType::ncType enumType, NcGroup::Location location) const {

  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypeCount on a Null group",__FILE__,__LINE__);

  // intialize counter
  int ntypes=0;

  // search in current group
  if(location == Current || location == ParentsAndCurrent || location == ChildrenAndCurrent || location == All ) {
    int ntypesp;
    int* typeidsp=NULL;
    ncCheck(nc_inq_typeids(getId(), &ntypesp,typeidsp),__FILE__,__LINE__);
    if (ntypesp){
      vector<int> typeids(ntypesp);
      ncCheck(nc_inq_typeids(getId(), &ntypesp,&typeids[0]),__FILE__,__LINE__);
      for (int i=0; i<ntypesp;i++){
        NcType tmpType(*this,typeids[i]);
        if(tmpType.getTypeClass() == enumType) ntypes++;
      }
    }
  }

  // search in parent groups.
  if(location == Parents || location == ParentsAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ntypes += it->second.getTypeCount(enumType);
    }
  }

  // search in child groups.
  if(location == Children || location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(AllChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      ntypes += it->second.getTypeCount(enumType);
    }
  }
  return ntypes;
}


// Gets the collection of NcType objects.
multimap<string,NcType> NcGroup::getTypes(NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypes on a Null group",__FILE__,__LINE__);
  // create a container to hold the NcType's.
  multimap<string,NcType> ncTypes;

  // search in current group
  if(location == Current || location == ParentsAndCurrent || location == ChildrenAndCurrent || location == All ) {
    int typeCount = getTypeCount();
    if (typeCount){
      vector<int> typeids(typeCount);
      ncCheck(nc_inq_typeids(getId(), &typeCount,&typeids[0]),__FILE__,__LINE__);
      // now get the name of each NcType and populate the nTypes container.
      for(int i=0; i<typeCount;i++){
        NcType tmpType(*this,typeids[i]);
        ncTypes.insert(pair<const string,NcType>(tmpType.getName(),tmpType));
      }
    }
  }

  // search in parent groups.
  if(location == Parents || location == ParentsAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(ParentsGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcType> typeTmp(it->second.getTypes());
      ncTypes.insert(typeTmp.begin(),typeTmp.end());
    }
  }

  // search in child groups (makes recursive calls).
  if(location == Children || location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups(AllChildrenGrps));
    for (it=groups.begin();it!=groups.end();it++) {
      multimap<string,NcType> typeTmp(it->second.getTypes());
      ncTypes.insert(typeTmp.begin(),typeTmp.end());
    }
  }

  return ncTypes;
}


// Gets the collection of NcType objects with a given name.
set<NcType> NcGroup::getTypes(const string& name, NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypes on a Null group",__FILE__,__LINE__);
  // iterator for the multimap container.
  multimap<string,NcType>::iterator it;
  // return argument of equal_range: iterators to lower and upper bounds of the range.
  pair<multimap<string,NcType>::iterator,multimap<string,NcType>::iterator> ret;
  // get the entire collection of types.
  multimap<string,NcType> types(getTypes(location));
  // define STL set object to hold the result
  set<NcType> tmpType;
  // get the set of NcType objects with a given name
  ret=types.equal_range(name);
  for (it=ret.first;it!=ret.second;it++) {
    tmpType.insert(it->second);
  }
  return tmpType;
}


// Gets the collection of NcType objects with a given data type.
set<NcType> NcGroup::getTypes(NcType::ncType enumType, NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypes on a Null group",__FILE__,__LINE__);
  // iterator for the multimap container.
  multimap<string,NcType>::iterator it;
  // get the entire collection of types.
  multimap<string,NcType> types(getTypes(location));
  // define STL set object to hold the result
  set<NcType> tmpType;
  // get the set of NcType objects with a given data type
  for (it=types.begin();it!=types.end();it++) {
    if(it->second.getTypeClass() == enumType) {
      tmpType.insert(it->second);
    }
  }
  return(tmpType);
}


// Gets the collection of NcType objects with a given name and data type.
set<NcType> NcGroup::getTypes(const string& name, NcType::ncType enumType, NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getTypes on a Null group",__FILE__,__LINE__);
  // iterator for the multimap container.
  multimap<string,NcType>::iterator it;
  // return argument of equal_range: iterators to lower and upper bounds of the range.
  pair<multimap<string,NcType>::iterator,multimap<string,NcType>::iterator> ret;
  // get the entire collection of types.
  multimap<string,NcType> types(getTypes(location));
  // define STL set object to hold the result
  set<NcType> tmpType;
  // get the set of NcType objects with a given name
  ret=types.equal_range(name);
  for (it=ret.first;it!=ret.second;it++) {
    if((*it).second.getTypeClass() == enumType) {
      tmpType.insert(it->second);
    }
  }
  return(tmpType);
}


// Gets the NcType object with a given name.
NcType NcGroup::getType(const string& name, NcGroup::Location location) const {
  if(isNull()) throw NcNullGrp("Attempt to invoke NcGroup::getType on a Null group",__FILE__,__LINE__);
  if(name ==  "byte"    ) return ncByte;
  if(name ==  "ubyte"   ) return ncUbyte;
  if(name ==  "char"    ) return ncChar;
  if(name ==  "short"   ) return ncShort;
  if(name ==  "ushort"  ) return ncUshort;
  if(name ==  "int"     ) return ncInt;
  if(name ==  "uint"    ) return ncUint;
  if(name ==  "int64"   ) return ncInt64;
  if(name ==  "uint64"  ) return ncUint64;
  if(name ==  "float"   ) return ncFloat;
  if(name ==  "double"  ) return ncDouble;
  if(name ==  "string"  ) return ncString;

  // this is a user defined type
  // iterator for the multimap container.
  multimap<string,NcType>::iterator it;
  // return argument of equal_range: iterators to lower and upper bounds of the range.
  pair<multimap<string,NcType>::iterator,multimap<string,NcType>::iterator> ret;
  // get the entire collection of types.
  multimap<string,NcType> types(getTypes(location));
  // define STL set object to hold the result
  set<NcType> tmpType;
    // get the set of NcType objects with a given name
  ret=types.equal_range(name);
  if(ret.first == ret.second)
    return NcType();
  else
    return ret.first->second;
}


// Adds a new netCDF Enum type.
NcEnumType NcGroup::addEnumType(const string& name,NcEnumType::ncEnumType baseType) const {
  ncCheckDefineMode(myId);
  nc_type typeId;
  ncCheck(nc_def_enum(myId, baseType, name.c_str(), &typeId),__FILE__,__LINE__);
  NcEnumType ncTypeTmp(*this,name);
  return ncTypeTmp;
}


// Adds a new netCDF Vlen type.
NcVlenType NcGroup::addVlenType(const string& name,NcType& baseType) const {
  ncCheckDefineMode(myId);
  nc_type typeId;
  ncCheck(nc_def_vlen(myId,  const_cast<char*>(name.c_str()),baseType.getId(),&typeId),__FILE__,__LINE__);
  NcVlenType ncTypeTmp(*this,name);
  return ncTypeTmp;
}


// Adds a new netCDF Opaque type.
NcOpaqueType NcGroup::addOpaqueType(const string& name, size_t size) const {
  ncCheckDefineMode(myId);
  nc_type typeId;
  ncCheck(nc_def_opaque(myId, size,const_cast<char*>(name.c_str()), &typeId),__FILE__,__LINE__);
  NcOpaqueType ncTypeTmp(*this,name);
  return ncTypeTmp;
}

// Adds a new netCDF UserDefined type.
NcCompoundType NcGroup::addCompoundType(const string& name, size_t size) const {
  ncCheckDefineMode(myId);
  nc_type typeId;
  ncCheck(nc_def_compound(myId, size,const_cast<char*>(name.c_str()),&typeId),__FILE__,__LINE__);
  NcCompoundType ncTypeTmp(*this,name);
  return ncTypeTmp;
}


// Get the collection of coordinate variables.
map<string,NcGroup> NcGroup::getCoordVars(NcGroup::Location location) const {
  map<string,NcGroup> coordVars;

  // search in current group and parent groups.
  NcGroup tmpGroup(*this);
  multimap<string,NcDim>::iterator itD;
  multimap<string,NcVar>::iterator itV;
  while(1) {
    // get the collection of NcDim objects defined in this group.
    multimap<string,NcDim> dimTmp(tmpGroup.getDims());
    multimap<string,NcVar> varTmp(tmpGroup.getVars());
    for (itD=dimTmp.begin();itD!=dimTmp.end();itD++) {
      string coordName(itD->first);
      itV = varTmp.find(coordName);
      if(itV != varTmp.end()) {
	coordVars.insert(pair<const string,NcGroup>(string(coordName),tmpGroup));
      }
    }
    if(location != ParentsAndCurrent || location != All || tmpGroup.isRootGroup()) {
      break;
    }
    // continue loop with the parent.
    tmpGroup=tmpGroup.getParentGroup();
  }

  // search in child groups (makes recursive calls).
  if(location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      map<string,NcGroup> coordVarsTmp=getCoordVars(ChildrenAndCurrent);
      coordVars.insert(coordVarsTmp.begin(),coordVarsTmp.end());
    }
  }

  return coordVars;
}

// Get the NcDim and NcVar object pair for a named coordinate variables.
void NcGroup::getCoordVar(string& coordVarName, NcDim& ncDim, NcVar& ncVar, NcGroup::Location location) const {

  // search in current group and parent groups.
  multimap<string,NcDim>::iterator itD;
  NcGroup tmpGroup(*this);
  multimap<string,NcVar>::iterator itV;
  while(1) {
    // get the collection of NcDim objects defined in this group.
    multimap<string,NcDim> dimTmp(tmpGroup.getDims());
    multimap<string,NcVar> varTmp(tmpGroup.getVars());
    itD=dimTmp.find(coordVarName);
    itV=varTmp.find(coordVarName);
    if(itD != dimTmp.end() && itV != varTmp.end()) {
      ncDim=itD->second;
      ncVar=itV->second;
      return;
    }
    if(location != ParentsAndCurrent || location != All || tmpGroup.isRootGroup()) {
      break;
    }
    // continue loop with the parent.
    tmpGroup=tmpGroup.getParentGroup();
  }

  // search in child groups (makes recursive calls).
  if(location == ChildrenAndCurrent || location == All ) {
    multimap<string,NcGroup>::iterator it;
    multimap<string,NcGroup> groups(getGroups());
    for (it=groups.begin();it!=groups.end();it++) {
      getCoordVar(coordVarName,ncDim,ncVar,ChildrenAndCurrent);
      if(!ncDim.isNull()) break;
    }
  }

  if(ncDim.isNull()) {
    // return null objects as no coordinates variables were obtained.
    NcDim dimTmp;
    NcVar varTmp;
    ncDim=dimTmp;
    ncVar=varTmp;
    return;
  }

}
