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


#ifndef HEADERBASE
#define HEADERBASE

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

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

#include "Map.hh"
#include "SplitString.hh"
#include "ReadWriteTextFile.hh"
#include "CppToPython.hh"
#include "StringTools.hh"

//!Data container for various data type with name-tag
/*!
  This class is improved in its functionarities from "HeaderBase".
  "std::vector<Int4>", "std::vector<Double>" and std::vector<std::string> can be stored
  into this container, and "Int4", "Double" and "std::string" can be also
  kept in it with its names.

  To keep the convenience for storing various types of objects,
  "HeaderBase" is not more efficient than "ElementContainer" and
  "NeutronVectorS", and this class should not be used as
  containers for experimental numerical data sets.
  This class is applied to keep header information
  in "ElementContainer". Please see the data structure of it.

  To keep compatibility with the old-version of "HeaderBase",
  some methods are added in this class.
  This class object has six class objects as protected member,
  "Map<Int4>", "Map<Double>", "Map<std::string>",
  "Map<std::vector<Int4> >", "Map<std::vector<Double> >" and
  "Map<std::vector<std::string> >".
  If you want to append the other functionarities to this class,
  the protected methods are manipulated from your sub-class
  and see the manuals of "Map".

  You cannot register the data object with the same key-name
  as the already used key-name over the six categories.
  To avoid the duplication, the addition process is checked
  in the methods of "Add(...)"
  If you want to know the key-names registered in this storage,
  use "Search(std::string)" or "Dump()".

  Delimiter in this class is "," -- don't forget this rule.
  Comma(s) should not be contained in std::strings
  appending to this storage.

  "DumpToString()" and "InputString()" is used
  exchanging the all information between servers.
  All information can be converted into a string object
  by "DumpToString()",
  and re-builded with the string object
  to "HeaderBase" by "InputString(std::string)".

  All information including vector-objects
  stored in this class will be deleted in the destructor.
  If data-objects extracted from "HeaderBase" are installed
  into other containers, the objects should be copied
  in your application before executing this destructor.

  Because both "Erase(Key)" and Add(Key,Value) is
  executed in the method of "OverWrite(Key,Value)",
  the type of Value can be changed by "OverWrite(Key,Value)".
 */

class HeaderBase
{
private:
  std::map<std::string, int> _keymap;
  /*!< @brief std::map of locations of keys.

  value 1..6 corresponds to each of
    Int4Map to StringVectorMap */

  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;    // to aboid re-declaration
  /** called from WriteNEXusFile::WriteData()
   *
   */
  template <class Write>
  void NXwrite(Write &W) const ;

  friend class ReadNeXusFile;
  /** called from ReadNeXusFile::ReadData2()
   *
   */

  friend class ManyoDataIO;

  template <class Read>
  void NXread(Read &R) ;

protected:
  Map< Int4 >              *Int4Map;
  Map< Double >            *DoubleMap;
  Map< std::string >            *StringMap;
  Map< std::vector< Int4 >  >  *Int4VectorMap;
  Map< std::vector< Double > > *DoubleVectorMap;
  Map< std::vector< std::string > > *StringVectorMap;
  void MakeMaps();
  std::vector< std::string > _keyTypeName;
//  CppToPython *PyConverter;

public:
  HeaderBase();
  /*!< @brief Constructor */

  //HeaderBase( std::string DumpedString );
  /* Constructor, the argument is a string
   which should be generated by "DumpToString()".
   The new object constructed by itself has the same information
   as the contents of the argument.*/

 ~HeaderBase();
  /*!< @brief Destructor.

  All information stored in this class
    will be deleted in this method. */
  HeaderBase( const HeaderBase &ob );
  /*!< @brief Copy constructor. */
  //HeaderBase &operator=( HeaderBase &ob );
  HeaderBase operator=( const HeaderBase &ob );

  void Add( std::string Key, Int4   value );
  /*!< @brief Add a new value with its key. */
  void Add( std::string Key, Double value );
  /*!< @brief Add a new value with its key. */
  void Add( std::string Key, std::string value );
  /*!< @brief Add a new value with its key. */
  void Add( std::string Key, std::vector<Int4>   value );
  /*!< @brief Add a new value with its key. */
  void Add( std::string Key, std::vector<Double> value );
  /*!< @brief Add a new value with its key. */
  void Add( std::string Key, std::vector<std::string> value );
  /*!< @brief Add a new value with its key. */

  void AddHeader( std::string Key, Int4   value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void AddHeader( std::string Key, Double value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void AddHeader( std::string Key, std::string value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void AddHeader( std::string Key, std::vector<Int4>   value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void AddHeader( std::string Key, std::vector<Double> value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void AddHeader( std::string Key, std::vector<std::string> value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */

  void add( std::string Key, Int4   value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void add( std::string Key, Double value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void add( std::string Key, std::string value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void add( std::string Key, std::vector<Int4>   value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void add( std::string Key, std::vector<Double> value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */
  void add( std::string Key, std::vector<std::string> value ){ Add( Key, value ); }
  /*!< @brief Alias of Add(Key,value) */

  void AddInt4List( std::string Key, PyObject *List );
  void AddDoubleList( std::string Key, PyObject *List );
  void AddStringList( std::string Key, PyObject *List );

  bool Empty(void) {return _keymap.empty(); }

  void Erase( std::string Key );
  /*!< @brief
    Remove a value and its name-tag from the storage.

    This method searches the six-Maps for "Key".
    If the "Key" cannot be found in the storage,
    warning messages will be output to the STD-out.
  */
  void erase( std::string Key ){ Erase( Key ); }
  /*!< @brief Alias of "Erase(Key)". */

  void clear();
  /*!< @brief
    Clear completely entire information stored in this class.
   */
  void Clear(){ clear(); }
  /*!< @brief Alias of "clear()". */

  void OverWrite( std::string Key, Int4   value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */
  void OverWrite( std::string Key, Double value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */
  void OverWrite( std::string Key, std::string value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */
  void OverWrite( std::string Key, std::vector<Int4>   value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */
  void OverWrite( std::string Key, std::vector<Double> value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */
  void OverWrite( std::string Key, std::vector<std::string> value ){Erase( Key ); Add( Key, value );}
  /*!< @brief Changes the value assigned to "Key". */

  void Dump();
  /*!< @brief Dump all information in user-frstd::endly format to the STD-out. */
  PyObject* PyDump();
  /*!< @brief This method returns a Python-list object whose content equal
    std::strings shown by "void Dump()".
   */
  void dump(){ Dump(); }
  /*!< @brief Alias of "Dump()". */

  void DumpHeader(){ Dump(); }

  void Search( std::string Key );
  /*!< @brief Searchs the six Maps for "Key".

   The name of std::map contained the "Key" returns to the STD-out.*/
  UInt4 CheckKey( std::string Key ) {return (_keymap[Key] > 0) ? 1 : 0;}
  /*!< @brief
    Returns the number of "Key".

    If "Key" has been entered, this method will return 1.
    Under normal conditions, this method returns 1 or 0.
  */

  Int4 PutInt4( std::string Key );
  /*!< @brief Returns an integer type value assigned to "Key". */
  Double PutDouble( std::string Key );
  /*!< @brief Returns an double type value assigned to "Key". */
  std::string PutString( std::string Key );
  /*!< @brief Returns an string type value assigned to "Key". */
  std::vector<Int4>   PutInt4Vector( std::string Key );
  /*!< @brief Returns an pointer of std::vector<Int4> assigned to "Key". */
  std::vector<Double> PutDoubleVector( std::string Key );
  /*!< @brief Returns an pointer of std::vector<Double> assigned to "Key". */
  std::vector<std::string> PutStringVector( std::string Key );
    /*!< @brief Returns an pointer of std::vector<std::string> assigned to "Key". */

  Int4   PutInt4(   UInt4 Index ){ return Int4Map->Put(Index); }
  Double PutDouble( UInt4 Index ){ return DoubleMap->Put(Index); }
  std::string PutString( UInt4 Index ){ return StringMap->Put(Index); }
  std::vector<Int4>   PutInt4Vector(   UInt4 Index ){ return Int4VectorMap->Put(Index); }
  std::vector<Double> PutDoubleVector( UInt4 Index ){ return DoubleVectorMap->Put(Index); }
  std::vector<std::string> PutStringVector( UInt4 Index ){ return StringVectorMap->Put(Index); }

  std::string DumpToString();
  /*!< @brief
    Convert all information stored in this class to a string.

    All information can be rebuild from the std::string by inputting
    it into "InputString(std::string)".
  */
  void InputString( std::string s );
  /*!< @brief Receives a string generated by "DumpToString()"
   and re-build the information.*/

  void InputFile( std::string FileName );

  Int4   i( std::string Key ){ return PutInt4( Key ); }
  /*!< @brief This method is leaved keeping compatibility with "HeaderBase". */
  Double d( std::string Key ){ return PutDouble( Key ); }
  /*!< @brief This method is leaved keeping compatibility with "HeaderBase". */
  Float  f( std::string Key){ return (Float)( d( Key ) ); }
  /*!< @brief This method is leaved keeping compatibility with "HeaderBase". */
  std::string s( std::string Key ){ return PutString( Key ); }
  /*!< @brief This method is leaved keeping compatibility with "HeaderBase". */
  std::vector<Int4>   iv( std::string Key ){ return PutInt4Vector( Key ); }
  /*!< @brief Alias of PuInt4Vector(std::string). */
  std::vector<Double> dv( std::string Key ){ return PutDoubleVector( Key ); }
  /*!< @brief Alias of PutDoubleVector(std::string). */
  std::vector<std::string> sv( std::string Key ){ return PutStringVector( Key ); }
  /*!< @brief Alias of PutStringVector(std::string). */

  friend class WriteNeXusFile;
  /*!< @brief makes enable to direct access to Int4Map etc. from
    WriteNeXusFile::AppendAttribute(), instead of using the obsolete
    PutInt4Map() series functions
  */
  friend class NeutronExchanger;
  /*!< @brief makes enable to direct access to Int4Map etc. from
    NeutronExchanger::TransmitHeaderBase() and ReceiveHeaderBase()
  */

  UInt4 PutKeyLocation( std::string Key ){ return _keymap[Key]; };
  /*!< returns locations of "Key". value 1..6 corresponds to each of
    Int4Map to StringVectorMap */

};

////////////////////////////////////
template <class Archive>
void HeaderBase::
serialize(Archive &ar, const unsigned int ver) {
  ar & boost::serialization::make_nvp("Keymap", _keymap);

  ar & boost::serialization::make_nvp("Int4Map",      *Int4Map);
  ar & boost::serialization::make_nvp("DoubleMap",    *DoubleMap);
  ar & boost::serialization::make_nvp("StringMap",    *StringMap);
  ar & boost::serialization::make_nvp("Int4Vector",   *Int4VectorMap);
  ar & boost::serialization::make_nvp("DoubleVector", *DoubleVectorMap);
  ar & boost::serialization::make_nvp("StringVector", *StringVectorMap);

}

////////////////////////////////////////////////
template <class Write>
void HeaderBase::
NXwrite(Write &W) const {
  W.WriteData("_Keymap", _keymap);

  for (std::map<std::string, int>::const_iterator p=_keymap.begin(),
           end=_keymap.end();p!=end;++p) {
    switch (p->second) {
    case 1:
      W.WriteData(p->first, (*Int4Map)[p->first]);
      break;
    case 2:
      W.WriteData(p->first, (*DoubleMap)[p->first]);
      break;
    case 3:
      W.WriteData(p->first, (*StringMap)[p->first]);
      break;
    case 4:
      W.WriteData(p->first, (*Int4VectorMap)[p->first]);
      break;
    case 5:
      W.WriteData(p->first, (*DoubleVectorMap)[p->first]);
      break;
    case 6:
      W.WriteData(p->first, (*StringVectorMap)[p->first]);
      break;
    }
  }

}
////////////////////////////////////////////////
template <class Read>
void HeaderBase::
NXread(Read &R) {
  R.ReadData("_Keymap", _keymap);

  for (std::map<std::string, int>::const_iterator p=_keymap.begin(),
           end=_keymap.end();p!=end;++p) {
    switch (p->second) {
    case 1:
      R.ReadData(p->first, (*Int4Map)[p->first]);
      break;
    case 2:
      R.ReadData(p->first, (*DoubleMap)[p->first]);
      break;
    case 3:
      R.ReadData(p->first, (*StringMap)[p->first]);
      break;
    case 4:
      R.ReadData(p->first, (*Int4VectorMap)[p->first]);
      break;
    case 5:
      R.ReadData(p->first, (*DoubleVectorMap)[p->first]);
      break;
    case 6:
      R.ReadData(p->first, (*StringVectorMap)[p->first]);
      break;
    }
  }
}
#endif
