// This may look like C code, but it is really -*- C++ -*-
// Class BaseDataTable 
//   R. Ansari -  Avril 2005
//   (C)  LAL-IN2P3/CNRS    CEA-DAPNIA  

#ifndef BASEDTABLE_H_SEEN
#define BASEDTABLE_H_SEEN

#include "machdefs.h"

#include <iostream>
#include <string>
#include <vector>
#include <map>

#include <complex>

#include "ntupintf.h"
#include "dvlist.h"
#include "segdatablock.h"
#include "tvector.h"

namespace SOPHYA {

//  Forward class declaration for Fits handler
template <class T>  class FitsHandler;

//! Class for representing and manipulating DataTable rows.  
class DataTableRow {
public:
//! Constructor - For use by BaseDataTable class
  DataTableRow( vector<string> const & colnames );
//! Copy constructor
  DataTableRow( DataTableRow const & a );
//! Returns the value for column \b k. No bound checking performed 
  inline MuTyV      operator[]  (sa_size_t k)  const { return mtv_[k]; }
//! Returns a reference to the value of column \b k. No bound checking performed 
  inline MuTyV&     operator[]  (sa_size_t k)  { return mtv_[k]; }
//! Returns the value for column \b colname. 
  inline MuTyV      operator[]  (string const colname)  const { return get(colname); }
//! Returns a reference to the value of column \b k. 
  inline MuTyV&     operator[]  (string const colname)   { return get(colname); }
//! Returns the value for column \b colname. 
  MuTyV      get(string const& colname) const;
//! Returns a reference to the value of column \b k. 
  MuTyV&     get(string const& colname);
//! Acces to the MuTyV pointer
  inline sa_size_t  Size() const { return size_; }
//! Acces to the MuTyV pointer
  inline MuTyV const *     MTVPtr() const { return mtv_; }
//! Acces to the MuTyV pointer
  inline MuTyV*     MTVPtr() { return mtv_; }
//! prints the object content on the output stream 
  ostream&   Print(ostream& os) const;
protected:
  MuTyV * mtv_;
  sa_size_t size_;
  map<string , sa_size_t> nm2idx_;
};

/*! Prints the DataTableRow object content on the output stream ( */
inline ostream& operator << (ostream& s, DataTableRow const & row)
  {  row.Print(s);  return(s);  }

//! Interface definition for classes handling data in a table.
class BaseDataTable : public AnyDataObj , public NTupleInterface {
public:
  // ===> DO NOT CHANGE EXISTING enum type VALUES !!! 
  enum FieldType {IntegerField=1, LongField=2, 
		  FloatField=3, DoubleField=4,
		  ComplexField=5, DoubleComplexField=6,
		  StringField=7, DateTimeField=8};
  // <=====================================================> 

  //! Return the column type as a string (long or short depending on fgl=true/false)
  static string     ColTypeToString(FieldType ft, bool fgl=false);
  //! Return the column type corresponding to \b sctyp
  static FieldType  StringToColType(string const & sctyp);

  // constructeur , destructeur 
  BaseDataTable(sa_size_t segsz=512);
  virtual           ~BaseDataTable();
  
  //! Adds a column holding integer, named \b cnom
  inline sa_size_t  AddIntegerColumn(const char * cnom) 
  { return AddColumn(IntegerField, cnom); }
  //! Adds a column holding integer, named \b cnom
  inline sa_size_t  AddIntegerColumn(string const & cnom) 
  { return AddColumn(IntegerField, cnom); }
  //! Adds a column holding long integer, named \b cnom
  inline sa_size_t  AddLongColumn(const char * cnom) 
  { return AddColumn(LongField, cnom); }
  //! Adds a column holding long integer, named \b cnom
  inline sa_size_t  AddLongColumn(string const & cnom) 
  { return AddColumn(LongField, cnom); }
  //! Adds a column holding floating values (r_4), named \b cnom
  inline sa_size_t  AddFloatColumn(const char * cnom) 
  { return AddColumn(FloatField, cnom); }
  //! Adds a column holding floating values (r_4), named \b cnom
  inline sa_size_t  AddFloatColumn(string const & cnom) 
  { return AddColumn(FloatField, cnom); }
  //! Adds a column holding double values (r_8), named \b cnom
  inline sa_size_t  AddDoubleColumn(const char * cnom) 
  { return AddColumn(DoubleField, cnom); }
  //! Adds a column holding double values (r_8), named \b cnom
  inline sa_size_t  AddDoubleColumn(string const & cnom) 
  { return AddColumn(DoubleField, cnom); }
  //! Adds a column holding single precision complex values (complex<r_4>), named \b cnom
  inline sa_size_t  AddComplexColumn(const char * cnom) 
  { return AddColumn(ComplexField, cnom); }
  //! Adds a column holding single precision complex values (complex<r_4>), named \b cnom
  inline sa_size_t  AddComplexColumn(string const & cnom) 
  { return AddColumn(ComplexField, cnom); }
  //! Adds a column holding double precision complex values (complex<r_8>), named \b cnom
  inline sa_size_t  AddDoubleComplexColumn(const char * cnom) 
  { return AddColumn(DoubleComplexField, cnom); }
  //! Adds a column holding double precision complex values (complex<r_8>), named \b cnom
  inline sa_size_t  AddDoubleComplexColumn(string const & cnom) 
  { return AddColumn(DoubleComplexField, cnom); }
  //! Adds a column holding character strings, named \b cnom
  inline sa_size_t  AddStringColumn(const char * cnom) 
  { return AddColumn(StringField, cnom); }
  //! Adds a column holding character strings, named \b cnom
  inline sa_size_t  AddStringColumn(string const & cnom) 
  { return AddColumn(StringField, cnom); }
  //! Adds a column holding Date/Time value, named \b cnom 
  inline sa_size_t  AddDateTimeColumn(const char * cnom) 
  { return AddColumn(DateTimeField, cnom); }
  //! Adds a column holding Date/Time value, named \b cnom 
  inline sa_size_t  AddDateTimeColumn(string const & cnom) 
  { return AddColumn(DateTimeField, cnom); }
  
  inline sa_size_t  AddColumn(FieldType ft, const char * cnom)
  { string nom = cnom;  return AddColumn(ft, nom); }
  
  //! Pure virtual method for adding a new column to the Data Table
  virtual sa_size_t AddColumn(FieldType ft, string const & cnom) = 0;
  //! Duplicate the data table structure from the source Table
  virtual sa_size_t CopyStructure(BaseDataTable const & a);
  //! Checks if the two table have the same column structure
  virtual bool      CompareStructure(BaseDataTable const & a);

  // Verifie la validite du nom de colonne:envoie une exception
  // si nom en double ou non valide
  virtual bool      CheckColName(string const & cnom);
  // Verifie et corrige si besoin un nom de colonne
  virtual bool      CheckCorrectColName(string & cnom);

  // Acces to various counts and parameters
  //! Return the number of lines (rows) in the table)
  inline sa_size_t  NRows() const { return mNEnt ; }
  //! Return the number of lines (rows) in the table)
  inline sa_size_t  NEntry() const { return mNEnt ; }
  //! Return the number of columns in the tables (number of cells in a row)  
  inline sa_size_t  NCols() const   { return mNames.size() ; } 
  //! Return the number of columns in the tables (number of cells in a row)  
  inline sa_size_t  NVar() const   { return mNames.size() ; } 
  //! Return the number of columns in the tables (number of cells in a row)  
  inline sa_size_t  NVars() const   { return mNames.size() ; } 
  //! Return the segment size (SegDBInterface objects corresponding to columns)  
  inline sa_size_t  SegmentSize() const   { return mSegSz ; } 
  //! Return the number of segments (SegDBInterface objects corresponding to columns)  
  inline sa_size_t  NbSegments() const   { return mNSeg ; } 

  //! Return a compatible DataTableRow object (with table column names)
  virtual DataTableRow  EmptyRow();

  // Filling data structures (adding lines/rows)
  virtual sa_size_t AddRow(const r_8* data);
  // Filling data structures (adding lines/rows)
  virtual sa_size_t AddRow(const MuTyV * data);
  // Filling data structures (adding lines/rows)
  virtual sa_size_t AddRow(DataTableRow const & data);

  //! Alias for AddRow()
  inline sa_size_t  AddLine(const r_8* data)
  { return AddRow(data); }
  //! Alias for AddRow()
  inline sa_size_t  AddLine(const MuTyV * data)
  { return AddRow(data); }
  //! Alias for AddLine()
  inline sa_size_t  AddLine(DataTableRow const & data)
  { return AddRow(data); }

  //! Alias for AddRow() - Kept for backward compatibility with NTuple class interface.
  inline sa_size_t  Fill(const r_8* data)
  { return AddRow(data); }

  // Pour etendre la table -      
  virtual sa_size_t Extend();

  //! Return the information stored in row \b n of the table in \b row object
  virtual DataTableRow&   GetRow(sa_size_t n, DataTableRow& row) const ;
  //! Return the information stored in line (row) \b n of the table 
  virtual MuTyV *   GetRow(sa_size_t n) const ;
  //! Return the information stored in line \b n of the table. Alias of GetLine 
  inline  MuTyV *   GetLine(sa_size_t n) const 
  { return GetRow(n); }

  //! Return the information stored in column \b k of the table, converted to double 
  virtual TVector<r_8>  GetColumnD(sa_size_t k) const ;

  //! Return the information stored in column named \b nom of the table, converted to double 
  inline TVector<r_8>  GetColumnD(char const * nom) const 
  { return  GetColumnD(IndexNom(nom)) ; }
  //! Return the information stored in column named \b nom of the table, converted to double 
  inline TVector<r_8>  GetColumnD(string const & nom) const 
  { return  GetColumnD(IndexNom(nom)) ; }

  //! Return the index for column name \b nom  
  sa_size_t         IndexNom(char const* nom) const ; 
  //! Return the index for column name \b nom  
  inline sa_size_t  IndexNom(string const & nom) const 
  { return IndexNom(nom.c_str()); }
  //! Return the column name for column index \b k
  string            NomIndex(sa_size_t k) const ; 

  //! Return the column type for column \b k (no check on index range)
  inline FieldType  GetColumType(sa_size_t k) const
  { return mNames[k].type; }
  //! Return the column name  for column \b k (no check on index range)
  inline const string & GetColumName(sa_size_t k)  const
  { return mNames[k].nom; }
  
  
  //! Copy or merges  the data from \b a into the data table (cp=true -> copy)
  virtual void      CopyMerge(BaseDataTable const& a, bool cp=false) ; 

  //! Clear/reset the table content and structure.
  virtual void Clear() = 0;

  //! Prints  the table content (ascii dump)
  virtual ostream&  Print(ostream& os, sa_size_t lstart, sa_size_t lend, 
			  sa_size_t lstep=1) const ;
  //! Prints  the table content (ascii dump) on stream \b os
  inline ostream&   Print(ostream& os) const
  { return Print(os, 0, NEntry(), 1); }
  //! Prints  the table content (ascii dump) on stream \b os
  inline ostream&   WriteASCII(ostream& os) const 
  { return Print(os, 0, NEntry(), 1); }

  
  //! Prints table definition and number of entries 
  void              Show(ostream& os) const ; 
  //! Prints table definition and number of entries on the standard output stream \b cout
  inline void       Show() const { Show(cout) ; } 
  
  //! Return the associated DVList (info) object. 
  DVList&           Info() const ; 
  
  // Remplissage depuis fichier ASCII 
  sa_size_t	    FillFromASCIIFile(istream& is, char clm='#', const char* sep=" \t");
  
  //-------------------------------------------------------------
  //-----------  Declaration de l interface NTuple --------------

  virtual sa_size_t NbLines() const ;
  virtual sa_size_t NbColumns() const ;
  virtual r_8 *     GetLineD(sa_size_t n) const ;
  virtual r_8	    GetCell(sa_size_t n, sa_size_t k) const ;
  virtual r_8	    GetCell(sa_size_t n, string const & nom) const ;
  virtual string    GetCelltoString(sa_size_t n, sa_size_t k) const ;
  virtual void      GetMinMax(sa_size_t k, double& min, double& max)   const ; 
  virtual void      GetMinMax(string const & nom, double& min, double& max)   const ; 
  virtual sa_size_t ColumnIndex(string const & nom)  const ;
  virtual string    ColumnName(sa_size_t k) const;
  virtual string    VarList_C(const char* nomx=NULL) const ;
  virtual string    LineHeaderToString() const;
  virtual string    LineToString(sa_size_t n) const;  

  //! Return a table row (line), formatted and converted to a string
  virtual string    TableRowToString(sa_size_t n, bool qstr, const char* sep=" ",
				     int fw=12) const; 

  //  Pour la gestion de persistance PPF
  friend class ObjFileIO<BaseDataTable> ;
  // pour fichiers FITS
  friend class FitsHandler<BaseDataTable>;
  
protected:
  uint_8   mNEnt ;   // nb total d'entrees 
  uint_8   mSegSz ;  // taille bloc/segment 
  uint_8   mNSeg;    // Nb total de segments
  mutable r_8 *  mVarD;    // Pour retourner une ligne de la table
  mutable MuTyV *  mVarMTV;    // Pour retourner une ligne de la table en MuTyV

  //! \cond   Pour pas inclure ds la documentation doxygen
  typedef struct { 
    string nom; 
    FieldType type;
    sa_size_t ser;
  } colst;
  //! \endcond  
  std::vector<colst> mNames;

  mutable DVList* mInfo;        // Infos (variables) attachees au NTuple

  // Pour calculer et garder le min/max   
  mutable std::vector<r_8> mMin;
  mutable std::vector<r_8> mMax;
  mutable std::vector<uint_8> mMinMaxNEnt;
  
  // Les pointeurs des SegDataBlock ...  
  std::vector< SegDBInterface<int_4> * > mIColsP; 
  std::vector< sa_size_t > mIColIdx;
  std::vector< SegDBInterface<int_8> * > mLColsP; 
  std::vector< sa_size_t > mLColIdx;
  std::vector< SegDBInterface<r_4> * > mFColsP;
  std::vector< sa_size_t > mFColIdx; 
  std::vector< SegDBInterface<r_8> * > mDColsP; 
  std::vector< sa_size_t > mDColIdx;
  std::vector< SegDBInterface< complex<r_4> > * > mYColsP;
  std::vector< sa_size_t > mYColIdx; 
  std::vector< SegDBInterface< complex<r_8> > * > mZColsP; 
  std::vector< sa_size_t > mZColIdx;
  std::vector< SegDBInterface<string> * > mSColsP; 
  std::vector< sa_size_t > mSColIdx;
  
}; 
/*! Prints table information (Column name and type, min/max) on stream \b s (bd.Show(s)) */
inline ostream& operator << (ostream& s, BaseDataTable const & bd)
  {  bd.Show(s);  return(s);  }
  
} // namespace SOPHYA

#endif
