/***************************************************************************

  PNeXus.cpp

  Author: Andreas Suter
  e-mail: andreas.suter@psi.ch

  $Id$

***************************************************************************/

/***************************************************************************
 *   Copyright (C) 2007-2011 by Andreas Suter                              *
 *   andreas.suter@psi.ch                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <cstdio>
#include <cstring>

#include "PNeXus.h"

#ifndef SIZE_FLOAT32
#    define SIZE_FLOAT32    4
#    define SIZE_FLOAT64    8
#    define SIZE_INT8       1
#    define SIZE_UINT8      1
#    define SIZE_INT16      2
#    define SIZE_UINT16     2
#    define SIZE_INT32      4
#    define SIZE_UINT32     4
#    define SIZE_INT64      8
#    define SIZE_UINT64     8
#    define SIZE_CHAR8      1
#    define SIZE_CHAR       1
#    define SIZE_UCHAR8     1
#    define SIZE_UCHAR      1
#    define SIZE_CHAR16     2
#    define SIZE_UCHAR16    2
#endif /* SIZE_FLOAT32 */

//-----------------------------------------------------------------------------------------------------
// PNeXus constructor
//-----------------------------------------------------------------------------------------------------
/**
 *
 */
PNeXus::PNeXus()
{
  Init();
}

//-----------------------------------------------------------------------------------------------------
// PNeXus constructor
//-----------------------------------------------------------------------------------------------------
/**
 *
 *
 * \param fileName
 */
PNeXus::PNeXus(const char* fileName)
{
  Init();
  fFileName = fileName;
  if (ReadFile(fileName) != NX_OK) {
    cerr << endl << fErrorMsg << " (error code=" << fErrorCode << ")" << endl << endl;
  } else {
    fIsValid = true;
  }
}

//-----------------------------------------------------------------------------------------------------
// PNeXus destructor
//-----------------------------------------------------------------------------------------------------
/**
 *
 */
PNeXus::~PNeXus()
{
  CleanUp();
}

//-----------------------------------------------------------------------------------------------------
// ReadFile (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on successfull reading
 * - NX_ERROR on error. The error code/message will give the details.
 *
 * \param fileName
 */
int PNeXus::ReadFile(const char *fileName)
{
  int status, ival;
  float fval;
  string str("");

  // open file
  status = NXopen(fileName, NXACC_READ, &fFileHandle);
  if (status != NX_OK) {
    fErrorCode = PNEXUS_FILE_OPEN_ERROR;
    fErrorMsg  = "PNeXus::ReadFile() **ERROR** Couldn't open file "+string(fileName)+"!";
    return NX_ERROR;
  }

  // open group
  status = NXopengroup(fFileHandle, "run", "NXentry");
  if (status != NX_OK) {
    fErrorCode = PNEXUS_GROUP_OPEN_ERROR;
    fErrorMsg  = "PNeXus::ReadFile() **ERROR** Couldn't open NeXus group run!";
    return NX_ERROR;
  }

  // IDF
  if (NXopendata(fFileHandle, "idf_version") != NX_OK) {
    if (NXopendata(fFileHandle, "IDF_version") != NX_OK) {
      fErrorCode = PNEXUS_OPEN_DATA_ERROR;
      fErrorMsg  = "PNeXus::ReadFile() **ERROR** couldn't open 'IDF_version' nor 'idf_version' data!!";
      return NX_ERROR;
    }
  }
  if (!ErrorHandler(NXgetdata(fFileHandle, &fIDFVersion), PNEXUS_GET_DATA_ERROR, "couldn't read 'IDF_version' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'IDF_version' data")) return NX_ERROR;

  // program_name
  if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'program_name' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fProgramName), PNEXUS_GET_DATA_ERROR, "couldn't read 'program_name' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringAttr("version", fProgramVersion), PNEXUS_GET_ATTR_ERROR, "couldn't read program_name attribute!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'program_name' data")) return NX_ERROR;

  // run number
  if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &fRunNumber), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data")) return NX_ERROR;

  // title
  if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'title' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fRunTitle), PNEXUS_GET_DATA_ERROR, "couldn't read 'title' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'title' data")) return NX_ERROR;

  // notes
  if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'notes' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fNotes), PNEXUS_GET_DATA_ERROR, "couldn't read 'notes' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'notes' data")) return NX_ERROR;

  // analysis tag
  if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'analysis' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fAnalysisTag), PNEXUS_GET_DATA_ERROR, "couldn't read 'analysis' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'analysis' data")) return NX_ERROR;

  // lab
  if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'lab' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fLab), PNEXUS_GET_DATA_ERROR, "couldn't read 'lab' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'lab' data")) return NX_ERROR;

  // beamline
  if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'beamline' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(fBeamLine), PNEXUS_GET_DATA_ERROR, "couldn't read 'beamline' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'beamline' data")) return NX_ERROR;

  // start time
  if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data")) return NX_ERROR;
  size_t pos, pos1, pos2;
  pos1 = str.find("T");
  pos2 = str.find(" ");
  if ((pos1 == string::npos) && (pos2 == string::npos)) {
    fErrorCode = PNEXUS_GET_DATA_ERROR;
    fErrorMsg  = "PNeXus::ReadFile() **ERROR** Wrong start date-time found (" + str + ")!";
    return NX_ERROR;
  }
  if (pos1 != string::npos)
    pos = pos1;
  else
    pos = pos2;
  fStartDate = str.substr(0, pos);
  fStartTime = str.substr(pos+1, str.length());

  // stop time
  if (!ErrorHandler(NXopendata(fFileHandle, "stop_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data")) return NX_ERROR;
  pos1 = str.find("T");
  pos2 = str.find(" ");
  if ((pos1 == string::npos) && (pos2 == string::npos)) {
    fErrorCode = PNEXUS_GET_DATA_ERROR;
    fErrorMsg  = "PNeXus::ReadFile() **ERROR** Wrong stop date-time found (" + str + ")!";
    return NX_ERROR;
  }
  if (pos1 != string::npos)
    pos = pos1;
  else
    pos = pos2;
  fStopDate = str.substr(0, pos);
  fStopTime = str.substr(pos+1, str.length());

  // switching state (red/green mode)
  if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'switching_states' data!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &fSwitchingState), PNEXUS_GET_DATA_ERROR, "couldn't read 'switching_states' data!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'switching_states' data")) return NX_ERROR;

  // open subgroup NXuser
  if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup user!")) return NX_ERROR;

  // change to subgroup
  if (!ErrorHandler(NXinitgroupdir(fFileHandle), PNEXUS_INIT_GROUPDIR_ERROR, "couldn't init group directory")) return NX_ERROR;
  int numItems = 0;
  NXname groupName, className;
  if (!ErrorHandler(NXgetgroupinfo(fFileHandle, &numItems, groupName, className), PNEXUS_GET_GROUP_INFO_ERROR, "couldn't get user group info")) return NX_ERROR;

  // get all the user info
  // initialize the user data set
  fUser.fUserName = "";
  fUser.fExperimentNumber = "";
  // go through the user list and filter out the required items
  NXname nx_label;
  int    nx_dataType;
  for (int i=0; i<numItems; i++) {
    if (!ErrorHandler(NXgetnextentry(fFileHandle, nx_label, className, &nx_dataType), PNEXUS_GET_NEXT_ENTRY_ERROR, "couldn't get next entry")) return NX_ERROR;
    if (!ErrorHandler(NXopendata(fFileHandle, nx_label), PNEXUS_OPEN_DATA_ERROR, "couldn't open data")) return NX_ERROR;
    if (!strcmp(nx_label, "name")) {
      if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't get user name")) return NX_ERROR;
      fUser.fUserName = str;
    }
    if (!strcmp(nx_label, "experiment_number")) {
      if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't get user name")) return NX_ERROR;
      fUser.fExperimentNumber = str;
    }
    if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close data")) return NX_ERROR;
  }

  // close subgroup NXuser
  NXclosegroup(fFileHandle);

  // open subgroup NXsample
  if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXSample"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup sample!")) return NX_ERROR;

  // read sample name
  if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in sample group")) return NX_ERROR;
  fSample.fName = str;

  // read sample temperature
  if (!ErrorHandler(NXopendata(fFileHandle, "temperature"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'temperature' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'temperature' data in sample group!")) return NX_ERROR;
  fSample.fTemperature = (double)fval;
  if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read temperature units!")) return NX_ERROR;
  fSample.fTemperatureUnit = str;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'temperature' data in sample group")) return NX_ERROR;

  // read sample magnetic field
  if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field' data in sample group!")) return NX_ERROR;
  fSample.fMagneticField = (double)fval;
  if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read magnetic field units!")) return NX_ERROR;
  fSample.fMagneticFieldUnit = str;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'temperature' data in sample group")) return NX_ERROR;

  // read sample shape, e.g. powder, single crystal, etc.
  if (!ErrorHandler(NXopendata(fFileHandle, "shape"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'shape' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'shape' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'shape' data in sample group")) return NX_ERROR;
  fSample.fShape = str;

  // read magnetic field state, e.g. TF, LF, ZF
  if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_state' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field_state' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_state' data in sample group")) return NX_ERROR;
  fSample.fMagneticFieldState = str;

  // read 'magnetic_field_vector' and 'coordinate_system' attribute
  if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_vector"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_vector' data in sample group!")) return NX_ERROR;
  int attLen = SIZE_INT32, attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("available"), &fSample.fMagneticFieldVectorAvailable, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_vector available' data in sample group!")) return NX_ERROR;
  if (fSample.fMagneticFieldVectorAvailable) {
    if (!ErrorHandler(GetDoubleVectorData(fSample.fMagneticFieldVector), PNEXUS_GET_DATA_ERROR, "couldn't get 'magnetic_field_vector' data!")) return NX_ERROR;
  }
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_vector' data in sample group")) return NX_ERROR;

  // read sample environment, e.g. CCR, LowTemp-2, etc.
  if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'environment' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'environment' data in sample group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'environment' data in sample group")) return NX_ERROR;
  fSample.fEnvironment = str;

  // close subgroup NXsample
  NXclosegroup(fFileHandle);

  // get required instrument information
  // open subgroup NXinstrument with subgroups detector, collimator, beam
  if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup instrument!")) return NX_ERROR;

  // get instrument name
  if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in instrument group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in instrument group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in instrument group")) return NX_ERROR;
  fInstrument.name = str;

  // open subgroup NXdetector
  if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup detector!")) return NX_ERROR;

  // get number of detectors
  if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in detector group!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data in detector group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data in detector group")) return NX_ERROR;
  fInstrument.detector.number = ival;

  // close subgroup NXdetector
  NXclosegroup(fFileHandle);

  // open subgroup NXcollimator
  if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup collimator!")) return NX_ERROR;

  // get collimator type
  if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'type' data in collimator group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'type' data in collimator group!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'type' data in collimator group")) return NX_ERROR;
  fInstrument.collimator.type = str;

  // close subgroup NXcollimator
  NXclosegroup(fFileHandle);

  // open subgroup NXbeam
  if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup beam!")) return NX_ERROR;

  // get the total counts
  if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'total_counts' data in beam group!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'total_counts' data in beam group!")) return NX_ERROR;
  fInstrument.beam.total_counts = (double)fval;
  if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read total_counts units!")) return NX_ERROR;
  fInstrument.beam.total_counts_units = str;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'total_counts' data in beam group")) return NX_ERROR;

  // close subgroup NXbeam
  NXclosegroup(fFileHandle);

  // close subgroup NXinstrument
  NXclosegroup(fFileHandle);

  // open subgroup NXdata (this is for Version 1, only and is subject to change in the near future!)
  if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup histogram_data_1!")) return NX_ERROR;

  // get time resolution
  if (!ErrorHandler(NXopendata(fFileHandle, "resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'resolution' data in histogram_data_1 group!")) return NX_ERROR;
  if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'resolution' data in histogram_data_1 group!")) return NX_ERROR;
  if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'resolution'' units!")) return NX_ERROR;
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'resolution' data in histogram_data_1 group")) return NX_ERROR;
  if (!strcmp(str.data(), "picoseconds")) {
    fData.fTimeResolution = (double)ival/1000.0; // time resolution in (ns)
  }

  // get data, t0, first good bin, last good bin, data
  if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'counts' data in histogram_data_1 group!")) return NX_ERROR;
  int histoLength=0;

  // get number of histos
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("number"), &fData.fNumberOfHistos, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR;

  // get histo length
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("length"), &histoLength, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR;

  // get t0
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("t0_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 't0_bin' data in histogram_data_1 group!")) return NX_ERROR;
  fData.fT0.push_back(ival);

  // get first good bin
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("first_good_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'first_good_bin' data in histogram_data_1 group!")) return NX_ERROR;
  fData.fFirstGoodBin.push_back(ival);

  // get last good bin
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("last_good_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'last_good_bin' data in histogram_data_1 group!")) return NX_ERROR;
  fData.fLastGoodBin.push_back(ival);

  // get data

  // get information of the current nexus entity
  int rank, type, dims[32], size, noOfElements;
  if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get data info!")) return NX_ERROR;

  // calculate the needed size
  size = dims[0];
  for (int i=1; i<rank; i++)
    size *= dims[i];
  noOfElements = size;
  size *= GetDataSize(type);

  // allocate locale memory to get the data
  char *data_ptr = new char[size];
  if (data_ptr == 0) {
    return NX_ERROR;
  }

  // get the data
  int *i_data_ptr = (int*) data_ptr;
  status = NXgetdata(fFileHandle, i_data_ptr);
  if (status != NX_OK) {
    return NX_ERROR;
  }

  // check that the amount of data is consistent with the attribute information
  if (noOfElements != (int)fData.fNumberOfHistos * histoLength) {
    fErrorCode = PNEXUS_HISTO_ERROR;
    fErrorMsg = "inconsistent histogram info!";
    return NX_ERROR;
  }


  // copy the data into the vector
  fData.fHisto.clear();
  vector<unsigned int> data;
  for (int i=0; i<noOfElements; i++) {
    if ((i % histoLength == 0) && (i>0)) {
      fData.fHisto.push_back(data);
      data.clear();
      data.push_back(*(i_data_ptr+i));
    } else {
      data.push_back(*(i_data_ptr+i));
    }
  }
  fData.fHisto.push_back(data);
  data.clear();

  // clean up
  if (data_ptr) {
    delete [] data_ptr;
  }

  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'counts' data in histogram_data_1 group")) return NX_ERROR;

  // get grouping
  if (!ErrorHandler(NXopendata(fFileHandle, "grouping"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping' data in histogram_data_1 group!")) return NX_ERROR;
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("available"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping available' data in histogram_data_1 group!")) return NX_ERROR;
  if (ival) {
    vector<int> grouping;
    if (!ErrorHandler(GetIntVectorData(grouping), PNEXUS_GET_DATA_ERROR, "couldn't read 'grouping' data in histogram_data_1 group!")) return NX_ERROR;
    for (unsigned int i=0; i<grouping.size(); i++)
      fData.fGrouping.push_back(grouping[i]);
    grouping.clear();
  }
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'grouping' data in histogram_data_1 group")) return NX_ERROR;

  // get alpha
  if (!ErrorHandler(NXopendata(fFileHandle, "alpha"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'alpha' data in histogram_data_1 group!")) return NX_ERROR;
  attLen = SIZE_INT32;
  attType = NX_INT32;
  if (!ErrorHandler(NXgetattr(fFileHandle, const_cast<char*>("available"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'alpha available' data in histogram_data_1 group!")) return NX_ERROR;
  if (ival) {
    if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get alpha info!")) return NX_ERROR;
    // calculate the needed size
    size = dims[0];
    for (int i=1; i<rank; i++)
      size *= dims[i];
    noOfElements = size;
    size *= GetDataSize(type);

    // check that noOfElements is a multiple of 3: grp_1, grp_2, alpha_12, etc.
    if ((3*(noOfElements/3)-noOfElements) != 0) {
      fErrorCode = PNEXUS_GET_META_INFO_ERROR;
      fErrorMsg  = "PNeXus::ReadFile(): **ERROR** alpha NeXus structure (grp_1, grp_2, alpha_12, etc.) violation.";
      return NX_ERROR;
    }

    // allocate locale memory to get the data
    char *data_ptr = new char[size];
    if (data_ptr == 0) {
      return NX_ERROR;
    }

    // get the data
    float *f_data_ptr = (float*) data_ptr;
    status = NXgetdata(fFileHandle, f_data_ptr);
    if (status != NX_OK) {
      return NX_ERROR;
    }

    // copy the data into the vector
    fData.fAlpha.clear();
    PNeXusAlpha alpha;
    for (int i=0; i<noOfElements; i+=3) {
      fval = *(f_data_ptr+i);
      alpha.fGroupFirst  = (unsigned int) fval;
      fval = *(f_data_ptr+i+1);
      alpha.fGroupSecond = (unsigned int) fval;
      alpha.fAlphaVal    = *(f_data_ptr+i+2);
      fData.fAlpha.push_back(alpha);
    }

    // clean up
    if (data_ptr) {
      delete [] data_ptr;
    }
  }
  if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'alpha' data in histogram_data_1 group")) return NX_ERROR;

  // close subgroup NXdata
  NXclosegroup(fFileHandle);

  // close group run
  NXclosegroup(fFileHandle);

  // close file
  NXclose(&fFileHandle);

  GroupHistoData();

  fIsValid = true;

  return true;
}

//-----------------------------------------------------------------------------------------------------
// WriteFile (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on successfull writing
 * - NX_ERROR on error. The error code/message will give the details.
 *
 * \param fileName
 * \param fileType, allowed types are: hdf4, hdf5, xml
 */
int PNeXus::WriteFile(const char *fileName, const char *fileType)
{
  char  str[256];
  int   size, idata;
  float fdata;
  NXaccess access=NXACC_CREATE4;

  if (!strcmp(fileType, "hdf4"))
    access=NXACC_CREATE4;
  else if (!strcmp(fileType, "hdf5"))
    access=NXACC_CREATE5;
  else if (!strcmp(fileType, "xml"))
    access=NXACC_CREATEXML;
  else
    access=NXACC_CREATE4;

  sprintf(str, "couldn't open file '%s' for writing", fileName);
  if (!ErrorHandler(NXopen(fileName, access, &fFileHandle), PNEXUS_FILE_OPEN_ERROR, str)) return NX_ERROR;

  // write NXfile attributes (NeXus_version, user)
  strncpy(str, fUser.fUserName.data(), sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "user", str, strlen(str), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXfile attributes")) return NX_ERROR;

  // make group 'run'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "run", "NXentry"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'run'.")) return NX_ERROR;
  // open group 'run'
  if (!ErrorHandler(NXopengroup(fFileHandle, "run", "NXentry"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'run' for writting.")) return NX_ERROR;

  // write IDF_version
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "IDF_version", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'IDF_version'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "IDF_version"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'IDF_version' for writting.")) return NX_ERROR;
  idata = fIDFVersion;
  if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'IDF_version'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write program_name, and attribute version
  size = fProgramName.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "program_name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'program_name'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'program_name' for writting.")) return NX_ERROR;
  strncpy(str, fProgramName.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'program_name'.")) return NX_ERROR;
  strncpy(str, fProgramVersion.data(), sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "version", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'version' for 'program_name'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'number'
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR;
  idata = fRunNumber;
  if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'title'
  size = fRunTitle.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "title", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'title'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'title' for writting.")) return NX_ERROR;
  strncpy(str, fRunTitle.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'title'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'notes'
  size = fNotes.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "notes", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'notes'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'notes' for writting.")) return NX_ERROR;
  strncpy(str, fNotes.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'notes'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'analysis'
  size = fAnalysisTag.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "analysis", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'analysis'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'analysis' for writting.")) return NX_ERROR;
  strncpy(str, fAnalysisTag.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'analysis'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'lab'
  size = fLab.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "lab", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'lab'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'lab' for writting.")) return NX_ERROR;
  strncpy(str, fLab.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'lab'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'beamline'
  size = fBeamLine.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "beamline", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'beamline'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'beamline' for writting.")) return NX_ERROR;
  strncpy(str, fBeamLine.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'beamline'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'start_time'
  snprintf(str, sizeof(str), "%s %s", fStartDate.data(), fStartTime.data());
  size = strlen(str);
  if (!ErrorHandler(NXmakedata(fFileHandle, "start_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'start_time'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'start_time' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'start_time'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'end_time'
  snprintf(str, sizeof(str), "%s %s", fStopDate.data(), fStopTime.data());
  size = strlen(str);
  if (!ErrorHandler(NXmakedata(fFileHandle, "end_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'end_time'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "end_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'end_time' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'end_time'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write run 'switching_states'
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "switching_states", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'switching_states'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'switching_states' for writting.")) return NX_ERROR;
  idata = fSwitchingState;
  if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'switching_states'.")) return NX_ERROR;
  NXclosedata(fFileHandle);


  // make group 'user'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "user", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'user'.")) return NX_ERROR;
  // open group 'user'
  if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'user' for writting.")) return NX_ERROR;

  // write user 'name'
  size = fUser.fUserName.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR;
  strncpy(str, fUser.fUserName.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write user 'experiment_number'
  size = fUser.fExperimentNumber.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "experiment_number", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'experiment_number'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "experiment_number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'experiment_number' for writting.")) return NX_ERROR;
  strncpy(str, fUser.fExperimentNumber.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'experiment_number'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // close group 'user'
  NXclosegroup(fFileHandle);

  // make group 'sample'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "sample", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'sample'.")) return NX_ERROR;
  // open group 'sample'
  if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'sample' for writting.")) return NX_ERROR;

  // write sample 'name'
  size = fSample.fName.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR;
  strncpy(str, fSample.fName.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write sample 'temperature'
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "temperature", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'temperature'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "temperature"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'temperature' for writting.")) return NX_ERROR;
  fdata = fSample.fTemperature;
  if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'temperature'.")) return NX_ERROR;
  strncpy(str, fSample.fTemperatureUnit.data(), sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'temperature'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write sample 'magnetic_field'
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field' for writting.")) return NX_ERROR;
  fdata = fSample.fMagneticField;
  if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field'.")) return NX_ERROR;
  strncpy(str, fSample.fMagneticFieldUnit.data(), sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'magnetic_field'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write sample 'shape'
  size = fSample.fShape.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "shape", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'shape'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "shape"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'shape' for writting.")) return NX_ERROR;
  strncpy(str, fSample.fShape.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'shape'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write sample 'magnetic_field_state'
  size = fSample.fMagneticFieldState.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_state", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_state'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_state' for writting.")) return NX_ERROR;
  strncpy(str, fSample.fMagneticFieldState.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_state'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write sample 'magnetic_field_vector'
  // !! TO BE DONE !!

  // write sample 'environment'
  size = fSample.fEnvironment.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "environment", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'environment'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'environment' for writting.")) return NX_ERROR;
  strncpy(str, fSample.fEnvironment.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'environment'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // close group 'sample'
  NXclosegroup(fFileHandle);

  // make group 'instrument'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'instrument'.")) return NX_ERROR;
  // open group 'instrument'
  if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'instrument' for writting.")) return NX_ERROR;

  // write instrument 'name'
  size = fInstrument.name.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR;
  strncpy(str, fInstrument.name.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // make group 'detector'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "detector", "NXdetector"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'detector'.")) return NX_ERROR;
  // open group 'detector'
  if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'detector' for writting.")) return NX_ERROR;

  // write detector 'number'
  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR;
  idata = fInstrument.detector.number;
  if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // close group 'detector'
  NXclosegroup(fFileHandle);

  // make group 'collimator'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'collimator'.")) return NX_ERROR;
  // open group 'collimator'
  if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'collimator' for writting.")) return NX_ERROR;

  // write collimator 'type'
  size = fInstrument.collimator.type.length();
  if (!ErrorHandler(NXmakedata(fFileHandle, "type", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'type'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'type' for writting.")) return NX_ERROR;
  strncpy(str, fInstrument.collimator.type.data(), sizeof(str));
  if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'type'.")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // close group 'collimator'
  NXclosegroup(fFileHandle);

  // make group 'beam'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "beam", "NXbeam"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'beam'.")) return NX_ERROR;
  // open group 'beam'
  if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'beam' for writting.")) return NX_ERROR;

  size = 1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "total_counts", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'total_counts'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'total_counts' for writting.")) return NX_ERROR;
  fdata = fInstrument.beam.total_counts;
  if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'total_counts'.")) return NX_ERROR;
  strncpy(str, fInstrument.beam.total_counts_units.data(), sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'total_counts'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // close group 'beam'
  NXclosegroup(fFileHandle);

  // close group 'instrument'
  NXclosegroup(fFileHandle);

  // make group 'histogram_data_1'
  if (!ErrorHandler(NXmakegroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'histogram_data_1'.")) return NX_ERROR;
  // open group 'histogram_data_1'
  if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'histogram_data_1' for writting.")) return NX_ERROR;

  // write data 'counts'
  int *histo_data=0;
  int histo_size[2];
  if (fData.fHisto.size() == 0) {
    histo_data = new int[1];
    histo_data[0] = -1;
    histo_size[0] = 1;
    histo_size[1] = 1;
  } else {
    histo_data = new int[fData.fHisto.size()*fData.fHisto[0].size()];
    for (unsigned int i=0; i<fData.fHisto.size(); i++)
      for (unsigned int j=0; j<fData.fHisto[0].size(); j++)
        histo_data[i*fData.fHisto[0].size()+j] = fData.fHisto[i][j];
    histo_size[0] = fData.fHisto.size();
    histo_size[1] = fData.fHisto[0].size();
  }

  if (!ErrorHandler(NXmakedata(fFileHandle, "counts", NX_INT32, 2, histo_size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'counts'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'counts' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, (void*)histo_data), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'counts'.")) return NX_ERROR;
  strncpy(str, "counts", sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'counts'")) return NX_ERROR;
  idata = 1;
  if (!ErrorHandler(NXputattr(fFileHandle, "signal", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'signal' for 'counts'")) return NX_ERROR;
  idata = fData.fHisto.size();
  if (!ErrorHandler(NXputattr(fFileHandle, "number", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'number' for 'counts'")) return NX_ERROR;
  idata = fData.fT0[0];
  if (!ErrorHandler(NXputattr(fFileHandle, "T0_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'T0_bin' for 'counts'")) return NX_ERROR;
  idata = fData.fFirstGoodBin[0];
  if (!ErrorHandler(NXputattr(fFileHandle, "first_good_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'first_good_bin' for 'counts'")) return NX_ERROR;
  idata = fData.fLastGoodBin[0];
  if (!ErrorHandler(NXputattr(fFileHandle, "last_good_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'last_good_bin' for 'counts'")) return NX_ERROR;
  fdata = 1.0e3*fData.fTimeResolution/2.0;
  if (!ErrorHandler(NXputattr(fFileHandle, "offset", &fdata, 1, NX_FLOAT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'offset' for 'counts'")) return NX_ERROR;

  if (histo_data) {
    delete [] histo_data;
  }
  NXclosedata(fFileHandle);

  // write data 'histogram_resolution'
  if (!ErrorHandler(NXmakedata(fFileHandle, "histogram_resolution", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'histogram_resolution'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "histogram_resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'histogram_resolution' for writting.")) return NX_ERROR;
  fdata = fData.fTimeResolution * 1.0e3; // ns -> ps
  if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'histogram_resolution'.")) return NX_ERROR;
  strncpy(str, "picoseconds", sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'histogram_resolution'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write data 'time_zero' always set to 0 since the T0_bin attribute of counts is relevant only
  if (!ErrorHandler(NXmakedata(fFileHandle, "time_zero", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'time_zero'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "time_zero"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'time_zero' for writting.")) return NX_ERROR;
  fdata = 0.0;
  if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'time_zero'.")) return NX_ERROR;
  strncpy(str, "microseconds", sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'time_zero'")) return NX_ERROR;
  idata = 0;
  if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'time_zero'")) return NX_ERROR;
  NXclosedata(fFileHandle);

  // write data 'raw_time'
  if (fData.fHisto.size() == 0) {
    fErrorCode = PNEXUS_HISTO_ERROR;
    fErrorMsg  = "no data for writing present.";
    return NX_ERROR;
  }
  float *raw_time = new float[fData.fHisto[0].size()-1];
  for (unsigned int i=0; i<fData.fHisto[0].size()-1; i++) {
    raw_time[i] = fData.fTimeResolution * (float)i / 1.0e3; // raw time in (us)
  }
  size = fData.fHisto[0].size()-1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "raw_time", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'raw_time'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "raw_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'raw_time' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, raw_time), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'raw_time'.")) return NX_ERROR;
  idata = 1;
  if (!ErrorHandler(NXputattr(fFileHandle, "axis", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'axis' for 'raw_time'")) return NX_ERROR;
  idata = 1;
  if (!ErrorHandler(NXputattr(fFileHandle, "primary", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'primary' for 'raw_time'")) return NX_ERROR;
  strncpy(str, "microseconds", sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'raw_time'")) return NX_ERROR;
  NXclosedata(fFileHandle);
  if (raw_time) {
    delete [] raw_time;
    raw_time = 0;
  }

  // write data 'corrected_time'
  float *corrected_time = new float[fData.fHisto[0].size()-1];
  for (unsigned int i=0; i<fData.fHisto[0].size()-1; i++) {
    corrected_time[i] = fData.fTimeResolution * (float)((int)i-(int)fData.fT0[0]+1) / 1.0e3; // raw time in (us)
  }
  size = fData.fHisto[0].size()-1;
  if (!ErrorHandler(NXmakedata(fFileHandle, "corrected_time", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'corrected_time'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "corrected_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'corrected_time' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, corrected_time), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'corrected_time'.")) return NX_ERROR;
  idata = 1;
  if (!ErrorHandler(NXputattr(fFileHandle, "axis", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'axis' for 'corrected_time'")) return NX_ERROR;
  strncpy(str, "microseconds", sizeof(str));
  if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'corrected_time'")) return NX_ERROR;
  NXclosedata(fFileHandle);
  if (corrected_time) {
    delete [] corrected_time;
    corrected_time = 0;
  }

  // write data 'grouping'
  int *grouping = new int[fData.fHisto.size()];
  vector<int> groupNo; // keep the number of different groupings
  if (fData.fHisto.size() == fData.fGrouping.size()) { // grouping vector seems to be properly defined
    bool found;
    groupNo.push_back(fData.fGrouping[0]);
    for (unsigned int i=0; i<fData.fHisto.size(); i++) {
      grouping[i] = fData.fGrouping[i];
      found = false;
      for (unsigned int j=0; j<groupNo.size(); j++) {
        if ((int)fData.fGrouping[i] == groupNo[j]) {
          found = true;
          break;
        }
      }
      if (!found) {
        groupNo.push_back(fData.fGrouping[i]);
      }
    }
  } else { // grouping vector not available
    for (unsigned int i=0; i<fData.fHisto.size(); i++) {
      grouping[i] = 0;
    }
  }
  size = fData.fHisto.size();
  if (!ErrorHandler(NXmakedata(fFileHandle, "grouping", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'grouping'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "grouping"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'grouping' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, grouping), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'grouping'.")) return NX_ERROR;
  idata = groupNo.size();
  if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'grouping'")) return NX_ERROR;
  NXclosedata(fFileHandle);
  if (grouping) {
    delete [] grouping;
    grouping = 0;
  }
  groupNo.clear();

  // write data 'alpha'
  float *alpha;
  int array_size[2];
  if (fData.fAlpha.size() == 0) {
    alpha = new float[3];
    alpha[0] = -1.0;
    alpha[1] = -1.0;
    alpha[2] = -1.0;
    array_size[0] = 1;
    array_size[1] = 3;
  } else {
    alpha = new float[fData.fAlpha.size()*3];
    for (unsigned int i=0; i<fData.fAlpha.size(); i++) {
      alpha[i] = (float)fData.fAlpha[i].fGroupFirst;
      alpha[i+1] = (float)fData.fAlpha[i].fGroupSecond;
      alpha[i+2] = (float)fData.fAlpha[i].fAlphaVal;
    }
    array_size[0] = fData.fAlpha.size();
    array_size[1] = 3;
  }
  if (!ErrorHandler(NXmakedata(fFileHandle, "alpha", NX_FLOAT32, 2, array_size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'alpha'.")) return NX_ERROR;
  if (!ErrorHandler(NXopendata(fFileHandle, "alpha"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'alpha' for writting.")) return NX_ERROR;
  if (!ErrorHandler(NXputdata(fFileHandle, (void*)alpha), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'alpha'.")) return NX_ERROR;
  idata = fData.fAlpha.size();
  if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'alpha'")) return NX_ERROR;
  if (alpha) {
    delete [] alpha;
  }

  // close group 'histogram_data_1'
  NXclosegroup(fFileHandle);

  // close group 'run'
  NXclosegroup(fFileHandle);

  NXclose(&fFileHandle);
  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// WriteFile (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Dump read data to the standard output.
 */
void PNeXus::Dump()
{
  cout << endl << "-----------------------------";
  cout << endl << " NeXus Dump";
  cout << endl << "-----------------------------";
  cout << endl;
  cout << endl << "file name       : " << fFileName;
  cout << endl << "IDF version     : " << fIDFVersion;
  cout << endl << "program name    : " << fProgramName << ", version : " << fProgramVersion;
  cout << endl << "run number      : " << fRunNumber;
  cout << endl << "run title       : " << fRunTitle;
  cout << endl << "run notes       : " << fNotes;
  cout << endl << "analysis        : " << fAnalysisTag;
  cout << endl << "lab             : " << fLab;
  cout << endl << "beamline        : " << fBeamLine;
  cout << endl << "start date/time : " << fStartDate << "/" << fStartTime;
  cout << endl << "stop  date/time : " << fStopDate << "/" << fStopTime;
  cout << endl << "switching state : " << fSwitchingState << " (red/green mode)";
  cout << endl << "-----------------------------";
  cout << endl << "user info";
  cout << endl << "user            : " << fUser.fUserName;
  cout << endl << "exp. number     : " << fUser.fExperimentNumber;
  cout << endl << "-----------------------------";
  cout << endl << "sample info";
  cout << endl << "sample name     : " << fSample.fName;
  cout << endl << "temperature     : " << fSample.fTemperature << " (" << fSample.fTemperatureUnit << ")";
  cout << endl << "magnetic state  : " << fSample.fMagneticFieldState;
  cout << endl << "magnetic field  : " << fSample.fMagneticField << " (" << fSample.fMagneticFieldUnit << ")";
  cout << endl << "mag. vector     : ";
  if (fSample.fMagneticFieldVectorAvailable) {
    for (unsigned int i=0; i<fSample.fMagneticFieldVector.size(); i++) {
      cout << fSample.fMagneticFieldVector[i] << ", ";
    }
    cout << "(" << fSample.fMagneticFieldVectorUnits << "), coord.system: " << fSample.fMagneticFieldVectorCoordinateSystem;
  } else {
    cout << "not available.";
  }
  cout << endl << "shape           : " << fSample.fShape;
  cout << endl << "environment     : " << fSample.fEnvironment;
  cout << endl << "-----------------------------";
  cout << endl << "instrument info";
  cout << endl << "instrument name : " << fInstrument.name;
  cout << endl << "   detector info";
  cout << endl << "   detector number : " << fInstrument.detector.number;
  cout << endl << "   -----------------------------";
  cout << endl << "   collimator info";
  cout << endl << "   collimator type : " << fInstrument.collimator.type;
  cout << endl << "   -----------------------------";
  cout << endl << "   beam info";
  cout << endl << "   beam total number of counts : " << fInstrument.beam.total_counts << " " << fInstrument.beam.total_counts_units;
  cout << endl << "-----------------------------";
  cout << endl << "run data";
  cout << endl << "time resolution : " << fData.fTimeResolution << " (ns)";
  cout << endl << "number of histos: " << fData.fNumberOfHistos;
  if (fData.fAlpha.size() != 0) {
    cout << endl << "alpha           : ";
    for (unsigned int i=0; i<fData.fAlpha.size(); i++) {
      cout << "(" << fData.fAlpha[i].fGroupFirst << "/" << fData.fAlpha[i].fGroupSecond << "/" << fData.fAlpha[i].fAlphaVal << "), ";
    }
  } else {
    cout << endl << "alpha           : not available";
  }
  if (fData.fGrouping.size() != 0) {
    cout << endl << "grouping        : ";
    for (unsigned int i=0; i<fData.fGrouping.size(); i++) {
      cout << "(" << i << "/" << fData.fGrouping[i] << "), ";
    }
  } else {
    cout << endl << "grouping        : not available";
  }
  cout << endl << "t0              : " << fData.fT0[0];
  cout << endl << "first good bin  : " << fData.fFirstGoodBin[0];
  cout << endl << "last good bin   : " << fData.fLastGoodBin[0];
  cout << endl << "histograms      : +++++++++++";
  for (unsigned int i=0; i<fData.fHisto.size(); i++) {
    cout << endl << "histo " << i+1 << ": ";
    for (unsigned int j=0; j<15; j++) {
      cout << fData.fHisto[i][j] << ", ";
    }
    cout << "...";
  }
  cout << endl << "-----------------------------";
  cout << endl << "grouped histograms : ++++++++";
  for (unsigned int i=0; i<fGroupedData.size(); i++) {
    cout << endl << "grouped histo " << i+1 << ": ";
    for (unsigned int j=0; j<15; j++) {
      cout << fGroupedData[i][j] << ", ";
    }
    cout << "...";
  }
  cout << endl << "-----------------------------";
  cout << endl << "done.";
  cout << endl << endl;

}

//-----------------------------------------------------------------------------------------------------
// SetStartDate (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>The date should look like yyyy-mm-dd.
 *
 * <b>return:</b>
 * - true on date string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param date
 */
bool PNeXus::SetStartDate(const char *date)
{
  string strDate = date;
  return SetStartDate(strDate);
}

//-----------------------------------------------------------------------------------------------------
// SetStartDate (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>The date should look like yyyy-mm-dd.
 *
 * <b>return:</b>
 * - true on date string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param date
 */
bool PNeXus::SetStartDate(string date)
{
  bool ok=false;
  string str = TransformDate(date, ok);

  if (!ok)
    return false;

  fStartDate = str;

  return true;
}

//-----------------------------------------------------------------------------------------------------
// SetStartTime (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>The time should look like hh:mm:ss.
 *
 * <b>return:</b>
 * - true on time string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param time
 */
bool PNeXus::SetStartTime(const char *time)
{
  string strTime = time;
  return SetStartTime(strTime);
}

//-----------------------------------------------------------------------------------------------------
// SetStartTime (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>The time should look like hh:mm:ss.
 *
 * <b>return:</b>
 * - true on time string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param time
 */
bool PNeXus::SetStartTime(string time)
{
  if (time.length() != 8) {
    fErrorCode = PNEXUS_WRONG_TIME_FORMAT;
    fErrorMsg = "PNeXus::SetStartTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!";
    return false;
  }

  if ((time[2] != ':') || (time[5] != ':')) {
    fErrorCode = PNEXUS_WRONG_TIME_FORMAT;
    fErrorMsg = "PNeXus::SetStartTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!";
    return false;
  }

  fStartTime = time;

  return true;
}

//-----------------------------------------------------------------------------------------------------
// SetStopDate (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - true on date string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param date
 */
bool PNeXus::SetStopDate(const char *date)
{
  string strDate = date;
  return SetStopDate(strDate);
}

//-----------------------------------------------------------------------------------------------------
// SetStopDate (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - true on date string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param date
 */
bool PNeXus::SetStopDate(string date)
{
  bool ok=false;
  string str = TransformDate(date, ok);

  if (!ok)
    return false;

  fStopDate = str;

  return true;
}

//-----------------------------------------------------------------------------------------------------
// SetStopTime (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - true on time string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param time
 */
bool PNeXus::SetStopTime(const char *time)
{
  string strTime = time;
  return SetStopTime(strTime);
}

//-----------------------------------------------------------------------------------------------------
// SetStopTime (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - true on time string has proper format
 * - false on error. The error code/message will give the details.
 *
 * \param time
 */
bool PNeXus::SetStopTime(string time)
{
  if (time.length() != 8) {
    fErrorCode = PNEXUS_WRONG_TIME_FORMAT;
    fErrorMsg = "PNeXus::SetStopTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!";
    return false;
  }

  if ((time[2] != ':') || (time[5] != ':')) {
    fErrorCode = PNEXUS_WRONG_TIME_FORMAT;
    fErrorMsg = "PNeXus::SetStopTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!";
    return false;
  }

  fStopTime = time;

  return true;
}

//-----------------------------------------------------------------------------------------------------
// CleanUp (private)
//-----------------------------------------------------------------------------------------------------
/**
 *
 */
void PNeXus::CleanUp()
{
  fData.fTimeResolution = 0.0;
  fData.fNumberOfHistos = 0;
  fData.fAlpha.clear();
  fData.fGrouping.clear();
  fData.fFirstGoodBin.clear();
  fData.fLastGoodBin.clear();
  fData.fT0.clear();
  fData.fHistoName.clear();
  for (unsigned int i=0; i<fData.fHisto.size(); i++)
    fData.fHisto[i].clear();
  fData.fHisto.clear();
}

//-----------------------------------------------------------------------------------------------------
// Init (private)
//-----------------------------------------------------------------------------------------------------
/**
 *
 */
void PNeXus::Init()
{
  fIsValid = false;

  fFileName    = "n/a";
  fFileHandle  = 0;

  fErrorMsg    = "No Data available!";
  fErrorCode   = -1;

  fIDFVersion  = -1;
  fProgramName = "n/a";
  fProgramVersion = "n/a";

  fRunNumber   = -1;
  fRunTitle    = "n/a";
  fNotes       = "n/a";
  fAnalysisTag = "n/a";
  fLab         = "n/a";
  fBeamLine    = "n/a";
  fStartDate   = "n/a";
  fStartTime   = "n/a";
  fStopDate    = "n/a";
  fStopTime    = "n/a";

  fSwitchingState = -1;

  fSample.fEnvironment = "n/a";
  fSample.fMagneticField = 9.9e17;
  fSample.fMagneticFieldUnit = "n/a";
  fSample.fMagneticFieldState = "n/a";
  fSample.fName = "n/a";
  fSample.fShape = "n/a";
  fSample.fTemperature = 9.9e17;
  fSample.fTemperatureUnit = "n/a";

  fInstrument.name = "n/a";
  fInstrument.detector.number = 0;
  fInstrument.collimator.type = "n/a";
  fInstrument.beam.total_counts = 0;
  fInstrument.beam.total_counts_units = "n/a";

  fData.fNumberOfHistos = 0;
  fData.fTimeResolution = 0.0;
  fData.fAlpha.clear();
  fData.fFirstGoodBin.clear();
  fData.fLastGoodBin.clear();
  fData.fHistoName.clear();
  fData.fGrouping.clear();
  fData.fT0.clear();
}

//-----------------------------------------------------------------------------------------------------
// ErrorHandler (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - true of no error occurred
 * - false on error
 *
 * \param status of the calling routine
 * \param errCode will set the fErrorCode of the class
 * \param errMsg will set the fErrorMsg of the class
 */
bool PNeXus::ErrorHandler(NXstatus status, int errCode, const string &errMsg)
{
  if (status != NX_OK) {
    fErrorCode = errCode;
    fErrorMsg = errMsg;
    if (fFileHandle != 0) {
      CleanUp();
      NXclose(&fFileHandle);
    }
    return false;
  }
  return true;
}

//-----------------------------------------------------------------------------------------------------
// GetStringData (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on success
 * - NX_ERROR otherwiese
 *
 * \param str string to be fed
 */
NXstatus PNeXus::GetStringData(string &str)
{
  int i, status, rank, type, dims[32];
  char cstr[VGNAMELENMAX];
  NXname data_value;

  status = NXgetinfo(fFileHandle, &rank, dims, &type);
  if (status != NX_OK) {
    fErrorCode = PNEXUS_GET_META_INFO_ERROR;
    fErrorMsg = "PNeXus::GetStringData() **ERROR** couldn't get meta info!";
    return NX_ERROR;
  }
  if ((type != NX_CHAR) || (rank > 1) || (dims[0] >= VGNAMELENMAX)) {
    fErrorCode = PNEXUS_WRONG_META_INFO;
    fErrorMsg = "PNeXus::GetStringData() **ERROR** found wrong meta info!";
    return NX_ERROR;
  }

  status = NXgetdata(fFileHandle, data_value);
  if (status != NX_OK) {
    fErrorCode = PNEXUS_GET_DATA_ERROR;
    fErrorMsg = "PNeXus::GetStringData() **ERROR** couldn't get data!";
    return NX_ERROR;
  }

  for (i = 0; i < dims[0]; i++)
    cstr[i] = *(data_value + i);
  cstr[i] = '\0';

  str = cstr;

  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// GetStringAttr (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on success
 * - NX_ERROR otherwiese
 *
 * \param attr attribute tag
 * \param str string to be fed
 */
NXstatus PNeXus::GetStringAttr(string attr, string &str)
{
  int i, status, attlen, atttype;
  char cstr[VGNAMELENMAX];
  NXname data_value;

  attlen = VGNAMELENMAX - 1;
  atttype = NX_CHAR;
  status = NXgetattr(fFileHandle, (char *)attr.c_str(), data_value, &attlen, &atttype);
  if (status != NX_OK) {
    cerr << endl << "**ERROR** in routine NXMgetstringattr: couldn't get attribute!" << endl << endl;
    fErrorCode = PNEXUS_GET_ATTR_ERROR;
    fErrorMsg = "PNeXus::GetStringAttr() **ERROR** couldn't get string attribute data!";
    return NX_ERROR;
  }

  for (i = 0; i < attlen; i++)
    cstr[i] = *(data_value + i);
  cstr[i] = '\0';

  str = cstr;

  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// GetDataSize (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - size in bytes of the given type
 * - 0 if the type is not recognized
 *
 * \param type
 */
int PNeXus::GetDataSize(int type)
{
  int size;

  switch (type) {
    case NX_CHAR:
      size = SIZE_CHAR8;
      break;
    case NX_FLOAT32:
      size = SIZE_FLOAT32;
      break;
    case NX_FLOAT64:
      size = SIZE_FLOAT64;
      break;
    case NX_INT8:
      size = SIZE_INT8;
      break;
    case NX_UINT8:
      size = SIZE_UINT8;
      break;
    case NX_INT16:
      size = SIZE_INT16;
      break;
    case NX_UINT16:
      size = SIZE_UINT16;
      break;
    case NX_INT32:
      size = SIZE_INT32;
      break;
    case NX_UINT32:
      size = SIZE_UINT32;
      break;
    default:
      size = 0;
      break;
  }

  return size;
}

//-----------------------------------------------------------------------------------------------------
// GetDoubleVectorData (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on success
 * - NX_ERROR otherwiese
 *
 * \param data
 */
NXstatus PNeXus::GetDoubleVectorData(vector<double> &data)
{
  // get information of the current nexus entity
  int rank, type, dims[32], size, noOfElements;
  if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get data info!"))
    return NX_ERROR;

  // calculate the needed size
  size = dims[0];
  for (int i=1; i<rank; i++)
    size *= dims[i];
  noOfElements = size;
  size *= GetDataSize(type);

  // allocate locale memory to get the data
  char *data_ptr = new char[size];
  if (data_ptr == 0) {
    return NX_ERROR;
  }

  // get the data
  float *f_data_ptr = (float*) data_ptr;
  int status = NXgetdata(fFileHandle, f_data_ptr);
  if (status != NX_OK) {
    return NX_ERROR;
  }

  // copy the data into the vector
  data.clear();
  for (int i=0; i<noOfElements; i++) {
    data.push_back(*(f_data_ptr+i));
  }

  // clean up
  if (data_ptr) {
    delete [] data_ptr;
  }

  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// GetIntVectorData (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * <b>return:</b>
 * - NX_OK on success
 * - NX_ERROR otherwiese
 *
 * \param data
 */
NXstatus PNeXus::GetIntVectorData(vector<int> &data)
{
  // get information of the current nexus entity
  int rank, type, dims[32], size, noOfElements;
  if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get data info!"))
    return NX_ERROR;

  // calculate the needed size
  size = dims[0];
  for (int i=1; i<rank; i++)
    size *= dims[i];
  noOfElements = size;
  size *= GetDataSize(type);

  // allocate locale memory to get the data
  char *data_ptr = new char[size];
  if (data_ptr == 0) {
    return NX_ERROR;
  }

  // get the data
  int *i_data_ptr = (int*) data_ptr;
  int status = NXgetdata(fFileHandle, i_data_ptr);
  if (status != NX_OK) {
    return NX_ERROR;
  }

  // copy the data into the vector
  data.clear();
  for (int i=0; i<noOfElements; i++) {
    data.push_back(*(i_data_ptr+i));
  }

  // clean up
  if (data_ptr) {
    delete [] data_ptr;
  }

  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// SetT0 (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Sets t0. The current muSR NeXus implementation has only a single t0 for all detectors since it was
 * tailored for pulsed muon sources (ISIS). This eventually needs to be changed.
 *
 * \param t0 to be set
 * \param idx index of t0
 */
void PNeXus::SetT0(unsigned int t0, unsigned idx)
{
  if (idx >= fData.fT0.size())
    fData.fT0.resize(idx+1);

  fData.fT0[idx] = t0;
}

//-----------------------------------------------------------------------------------------------------
// SetFirstGoodBin (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Sets first good bin. The current muSR NeXus implementation has only a single first good bin for all detectors since it was
 * tailored for pulsed muon sources (ISIS). This eventually needs to be changed.
 *
 * \param fgb first good bin to be set
 * \param idx index of t0
 */
void PNeXus::SetFirstGoodBin(unsigned int fgb, unsigned int idx)
{
  if (idx >= fData.fFirstGoodBin.size())
    fData.fFirstGoodBin.resize(idx+1);

  fData.fFirstGoodBin[idx] = fgb;
}

//-----------------------------------------------------------------------------------------------------
// SetLastGoodBin (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Sets last good bin. The current muSR NeXus implementation has only a single last good bin for all detectors since it was
 * tailored for pulsed muon sources (ISIS). This eventually needs to be changed.
 *
 * \param lgb to be set
 * \param idx index of t0
 */
void PNeXus::SetLastGoodBin(unsigned int lgb, unsigned int idx)
{
  if (idx >= fData.fLastGoodBin.size())
    fData.fLastGoodBin.resize(idx+1);

  fData.fLastGoodBin[idx] = lgb;
}

//-----------------------------------------------------------------------------------------------------
// SetHisto (public)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>
 *
 * \param histoNo histogram number (start counting from 0)
 * \param data histogram data vector to be set
 */
void PNeXus::SetHisto(unsigned int histoNo, vector<unsigned int> &data)
{
  if (histoNo >= fData.fHisto.size())
    fData.fHisto.resize(histoNo+1);

  fData.fHisto[histoNo] = data;
}

//-----------------------------------------------------------------------------------------------------
// GroupHistoData (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Feed the grouped histo data, based on the grouping vector and the raw histo data.
 */
NXstatus PNeXus::GroupHistoData()
{
  // check if NO grouping is whished, in which case fData.fHisto == fGroupedData
  if (fData.fGrouping.size() == 0) {
    for (unsigned int i=0; i<fData.fHisto.size(); i++)
      fGroupedData.push_back(fData.fHisto[i]);
    return NX_OK;
  }

  // check that the grouping size is equal to the number of histograms
  if (fData.fGrouping.size() != fData.fHisto.size()) {
    fErrorCode = PNEXUS_HISTO_ERROR;
    fErrorMsg = "PNeXus::GroupHistoData() **ERROR** grouping vector size is unequal to the number of histos present!";
    return NX_ERROR;
  }

  // make a vector of all grouping present
  vector<unsigned int> groupingValue;
  bool newGroup = true;
  for (unsigned int i=0; i<fData.fGrouping.size(); i++) {
    newGroup = true;
    for (unsigned int j=0; j<groupingValue.size(); j++) {
      if (groupingValue[j] == fData.fGrouping[i]) {
        newGroup = false;
        break;
      }
    }
    if (newGroup)
      groupingValue.push_back(fData.fGrouping[i]);
  }

  // check that none of the grouping values is outside of the valid range
  for (unsigned int i=0; i<groupingValue.size(); i++) {
    if (groupingValue[i]-1 > fData.fHisto.size()) {
      fErrorCode = PNEXUS_HISTO_ERROR;
      fErrorMsg = "PNeXus::GroupHistoData() **ERROR** grouping values out of range";
      return NX_ERROR;
    }
  }

  // set fGroupedData to the proper size
  fGroupedData.clear();
  fGroupedData.resize(groupingValue.size());
  for (unsigned int i=0; i<fGroupedData.size(); i++) {
    fGroupedData[i].resize(fData.fHisto[0].size());
  }

  for (unsigned int i=0; i<fData.fHisto.size(); i++) {
    for (unsigned int j=0; j<fData.fHisto[i].size(); j++) {
      fGroupedData[fData.fGrouping[i]-1][j] += fData.fHisto[i][j];
    }
  }

  // cleanup
  groupingValue.clear();

  return NX_OK;
}

//-----------------------------------------------------------------------------------------------------
// TransformDate (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>Transformed a given date to the form yyyy-mm-dd if possible. Allowed input dates are yyyy-mm-dd,
 * yy-mm-dd, yyyy-MMM-dd, or yy-MMM-dd, where mm is the month as number and MMM the month as string, e.g. APR
 *
 * <p><b>return:</b> transformed date
 *
 * \param date
 * \param ok
 */
string PNeXus::TransformDate(string date, bool &ok)
{
  string result = date;
  string str;
  char cstr[128];
  int status, yy, mm, dd;

  ok = true;

  switch(date.length()) {
  case 8: // yy-mm-dd
    if ((date[2] != '-') || (date[5] != '-')) {
      ok = false;
    } else {
      status = sscanf(date.c_str(), "%d-%d-%d", &yy, &mm, &dd);
      if (status != 3) {
        ok = false;
      } else {
        if (yy >= 70) // i.e. 1970
          result = "19"+date;
        else // i.e. 20YY
          result = "20"+date;
      }
    }
    break;
  case 9: // yy-MMM-dd
    if ((date[2] != '-') || (date[6] != '-')) {
      ok = false;
    } else {
      str = date.substr(0,2); // yy
      status = sscanf(str.c_str(), "%d", &yy);
      if (status != 1)
        ok = false;
      str = date.substr(3,3); // MMM
      mm = GetMonth(str);
      if (mm == 0)
        ok = false;
      str = date.substr(7,2); // dd
      status = sscanf(str.c_str(), "%d", &dd);
      if (status != 1)
        ok = false;
      if (ok) {
        if (yy >= 70) // i.e. 1970
          yy += 1900;
        else
          yy += 2000;
        snprintf(cstr, sizeof(cstr), "%04d-%02d-%02d", yy, mm, dd);
        result = cstr;
      }
    }
    break;
  case 10: // yyyy-mm-dd
    if ((date[4] != '-') || (date[7] != '-')) {
      ok = false;
    } else {
      status = sscanf(date.c_str(), "%04d-%02d-%02d", &yy, &mm, &dd);
      if (status != 3) {
        ok = false;
      } else {
        ok = true;
      }
    }
    break;
  case 11: // yyyy-MMM-dd
    if ((date[4] != '-') || (date[8] != '-')) {
      ok = false;
    } else {
      str = date.substr(0,4); // yyyy
      status = sscanf(str.c_str(), "%d", &yy);
      if (status != 1)
        ok = false;
      str = date.substr(5,3); // MMM
      mm = GetMonth(str);
      if (mm == 0)
        ok = false;
      str = date.substr(9,2); // dd
      status = sscanf(str.c_str(), "%d", &dd);
      if (status != 1)
        ok = false;
      if (ok) {
        snprintf(cstr, sizeof(cstr), "%04d-%02d-%02d", yy, mm, dd);
        result = cstr;
      }
    }
    break;
  default:
    ok = false;
    break;
  }

  if (!ok) {
    fErrorCode = PNEXUS_WRONG_DATE_FORMAT;
    fErrorMsg = "PNeXus::TransformDate **ERROR** given date="+date+", is not of any of the the required forms!";
  }

  return result;
}

//-----------------------------------------------------------------------------------------------------
// GetMonth (private)
//-----------------------------------------------------------------------------------------------------
/**
 * <p>For a month string MMM (e.g. APR) return the month number
 *
 * <p><b>return:</b> month number
 *
 * \param month string of the form MMM, e.g. APR
 */
int PNeXus::GetMonth(const string month)
{
  int result = 0;

  if (!month.compare("jan") || !month.compare("Jan") || !month.compare("JAN"))
    result = 1;
  else if (!month.compare("feb") || !month.compare("Feb") || !month.compare("FEB"))
    result = 2;
  else if (!month.compare("mar") || !month.compare("Mar") || !month.compare("MAR"))
    result = 3;
  else if (!month.compare("apr") || !month.compare("Apr") || !month.compare("APR"))
    result = 4;
  else if (!month.compare("may") || !month.compare("May") || !month.compare("MAY"))
    result = 5;
  else if (!month.compare("jun") || !month.compare("Jun") || !month.compare("JUN"))
    result = 6;
  else if (!month.compare("jul") || !month.compare("Jul") || !month.compare("JUL"))
    result = 7;
  else if (!month.compare("aug") || !month.compare("Aug") || !month.compare("AUG"))
    result = 8;
  else if (!month.compare("sep") || !month.compare("Sep") || !month.compare("SEP"))
    result = 9;
  else if (!month.compare("oct") || !month.compare("Oct") || !month.compare("OCT"))
    result = 10;
  else if (!month.compare("nov") || !month.compare("Nov") || !month.compare("NOV"))
    result = 11;
  else if (!month.compare("dec") || !month.compare("Dec") || !month.compare("DEC"))
    result = 12;
  else
    result = 0;

  return result;
}

//-----------------------------------------------------------------------------------------------------
// end
//-----------------------------------------------------------------------------------------------------
