/*
$Id: UInt4Container.hh 2247 2011-04-27 05:20:41Z jisuzuki $
*/


#ifndef UINT4CONTAINER
#define UINT4CONTAINER

/* Header.hh should be included on the top to avoid environments
 * specific issues (see Header.hh for the detail).
 */
#include "Header.hh"

#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <nexus/napi.h>

#include "HeaderBase.hh"
#include "CppToPython.hh"
#include "SplitString.hh"
#include "ElementContainer.hh"

//! std::vector<UInt4> container with their name tag.
/*! This class is data container class like "ElementContainer".
  Functionalities of class methods in this class is similar to
  the methods of "ElementContainer". See the manual of "ElementContainer".
 */
class UInt4Container
{
private:
  std::vector< std::vector< UInt4 >* > v;
  std::vector< std::string > s;
  std::vector< std::vector< Double >* > vd;
  std::vector< std::string > sd;

  HeaderBase *header;
  CppToPython *PythonConverter;
  void SetupPythonConverter();

  friend class boost::serialization::access;
  /** serialize method used from boost::serialization::access
   *
   */
  template <class Archive>
  void serialize(Archive &ar, const unsigned int ver);

  friend class WriteNeXusFile;
  /** called from WriteNEXusFile::WriteData()
   *
   */
  template <class Write>
  void NXwrite(Write &W) const ;

  friend class ReadNeXusFile;
  /** called from ReadNeXusFile::ReadData()
   *
   */
  template <class Read>
  void NXread(Read &R) ;

protected:

public:
  UInt4Container();
  UInt4Container( HeaderBase *Header );
  //UInt4Container( HeaderBase Header );

  UInt4Container( const UInt4Container &ob );
  UInt4Container operator=( const UInt4Container &ob );

 ~UInt4Container();

  UInt4 PutTableSize(){ return (UInt4)v.size(); }
  UInt4 PutSize(){ return PutTableSize(); }

  UInt4 PutTableSizeDouble(){ return (UInt4)vd.size(); }
  UInt4 PutSizeDouble(){ return PutTableSizeDouble(); }

  UInt4 PutSize( UInt4 IndexNumber ){ return (UInt4)(v[IndexNumber] -> size()); }
  UInt4 PutSize( std::string Key ){ return (UInt4)(Put(Key) . size()); }

  UInt4 PutSizeDouble( UInt4 IndexNumber ){ return (UInt4)(vd[IndexNumber] -> size()); }
  UInt4 PutSizeDouble( std::string Key ){ return (UInt4)(PutDouble(Key) . size()); }

  UInt4 PutIndexNumber( std::string Key );
  UInt4 PutIndexNumberDouble( std::string Key );

  std::string PutName( UInt4 IndexNumber ){ return s[IndexNumber]; }
  std::string PutNameDouble( UInt4 IndexNumber ){ return sd[IndexNumber]; }

  HeaderBase PutHeader();
  HeaderBase *PutHeaderPointer(){ return header; }
  void InputHeader( HeaderBase Header );

  //! reverse all std::vector
  void Reverse(void) {
    std::cout << "UInt4Container::Reverse() not implemented yet" << std::endl;
  }

  void DumpKey();
  void Dump();
  void DumpValue();
  void DumpFromVectorContainer();
  void DumpFromVectorContainer( UInt4 size );
  void Dump( UInt4 size );
  void DumpValue( UInt4 size );

  void Add( std::string Key, std::vector< UInt4 > value );
  void Add( std::string Key, PyObject *value );
  void Add( std::string Key, UInt4 *&Array, UInt4 ArraySize );

  void AddDouble( std::string Key, std::vector< Double > value );
  void AddDouble( std::string Key, PyObject *value );
  void AddDouble( std::string Key, Double *&Array, UInt4 ArraySize );

  std::vector<UInt4> Put( UInt4 IndexNumber );
  std::vector<Double> PutDouble( UInt4 IndexNumber );
  std::vector<UInt4> Put( std::string Key );
  std::vector<Double> PutDouble( std::string Key );
  UInt4  Put( std::string Key, UInt4 Index );
  Double PutDouble( std::string Key, UInt4 Index );

  PyObject *PutList( std::string Key );
  PyObject *PutListDouble( std::string Key );
  UInt4 CheckKey( std::string Key );
  UInt4 CheckKeyDouble( std::string Key );

  // These methods will be utilized for making NeXus file.
  std::string PutMergedKey();
  std::string PutMergedKeyDouble();
  std::vector<UInt4>  PutSizeVector();
  std::vector<UInt4>  PutSizeVectorDouble();
  std::vector<UInt4>  PutMergedDataVector();
  std::vector<Double>  PutMergedDataVectorDouble();

  // A data container will be built with the arguments of this method,
  // and this method will be called from "ReadNeXusFile" and "WriteNeXusFile".
  void MakeContainer( std::string MergedKey,
              std::vector<UInt4> SizeVector,
              std::vector<UInt4> MergedDataVector,
              HeaderBase h );
  /*!<
    The object of HeaderBase, the fourth argument, will be deleted
    in this function. If the object extracted with "PutHeader()" is
    installed with this function, you should copy it before use this function.
   */
  void MakeContainer( std::string MergedKey,
              std::vector<UInt4> SizeVector,
              std::vector<UInt4> MergedDataVector,
              std::string MergedKeyDouble,
              std::vector<UInt4> SizeVectorDouble,
              std::vector<Double> MergedDataVectorDouble,
              HeaderBase h );
  /*!< This method will be used from the outside of this class.*/

  std::vector<UInt4>* operator[]( std::string Key );
  /*!< This operator returns the pointer of std::vector<UInt4>
    whose name is "Key".
   */
  std::vector<UInt4>* operator[]( UInt4 index );
  /*!< This operator returns the pointer of std::vector<UInt4>
    whose index-number is "index".
   */
  std::vector<UInt4>* operator()( std::string Key );
  /*!< This operator returns the pointer of std::vector<UInt4>
    whose name is "Key".
   */
  std::vector<UInt4>* operator()( UInt4 index );
  /*!< This operator returns the pointer of std::vector<UInt4>
    whose index-number is "index".
   */

  ElementContainer ConvertIntoElementContainer();
  /*!< The contents stored in this class is comverted into the class object of
    ElementContainer. */



  //! dummy code to avoid NeutronVector::operator+=()
  UInt4Container &operator+=(const UInt4Container &r) {
    std::cout << "operator += for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator-=()
  UInt4Container &operator-=(const UInt4Container &r) {
    std::cout << "operator -= for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator*=()
  UInt4Container &operator*=(const UInt4Container &r) {
    std::cout << "operator *= for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator/=()
  UInt4Container &operator/=(const UInt4Container &r) {
    std::cout << "operator /= for UInt4Container was not implemented" << std::endl;
    return *this;
  }

  //! dummy code to avoid NeutronVector::operator+()
  UInt4Container operator+(UInt4Container &r) {
    std::cout << "operator + for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator-()
  UInt4Container operator-(UInt4Container &r) {
    std::cout << "operator - for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator*()
  UInt4Container operator*(UInt4Container &r) {
    std::cout << "operator * for UInt4Container was not implemented" << std::endl;
    return *this;
  }
  //! dummy code to avoid NeutronVector::operator/()
  UInt4Container operator/(UInt4Container &r) {
    std::cout << "operator / for UInt4Container was not implemented" << std::endl;
    return *this;
  }
};

/////////////////////////////////////////////////////////
template <class Archive>
void UInt4Container::
serialize(Archive &ar, const unsigned int ver) {
  ar & boost::serialization::make_nvp("UInt4Keys", s);
  ar & boost::serialization::make_nvp("UInt4Values", v);

  ar & boost::serialization::make_nvp("DoubleKeys", sd);
  ar & boost::serialization::make_nvp("DoubleValues", vd);

  ar & boost::serialization::make_nvp("Header", *header);
}

////////////////////////////////////////////////
template <class Write>
void UInt4Container::
NXwrite(Write &W) const {
  W.WriteData("UInt4Keys", s);
  W.WriteData("UInt4Values", v);

  W.WriteData("DoubleKeys", sd);
  W.WriteData("DoubleValues", vd);

  if (!header->Empty())
  W.WriteData("Header", *header);
}

////////////////////////////////////////////////
template <class Read>
void UInt4Container::
NXread(Read &R) {
  R.ReadData("UInt4Keys", s);
  R.ReadData("UInt4Values", v);

  R.ReadData("DoubleKeys", sd);
  R.ReadData("DoubleValues", vd);

  NXname _name, _class;
  int _id, _status;
  while ((_status=R.GetNextEntry2(_name, _class, &_id))==1) {
//    std::cerr << "NEXTENTRY: " << _name << "," << _class << ", "
//              << _id << "," << _status << std::endl;
    if (strcmp(_name, "Header")==0)
      R.ReadData("Header", *header);
  }
}
#endif

