#include "datatable.h"
#include "sopnamsp.h"
#include "strutil.h"
#include "pexceptions.h"
#include "fiosegdb.h"


/*!
   \class SOPHYA::DataTable
   \ingroup HiStats
   This class can be used to organize data in table (row-column) form.
   Each column holds homogeneous data (same data type), while different
   columns can be used for different data types 
   (integer, float, string ...).
   The whole data set is kept in memory.
   \sa SOPHYA::MuTyV
   \sa SOPHYA::DataTableRow
   \sa SOPHYA::BaseDataTable
   \sa SOPHYA::SegDataBlock

   \code
   #include "datatable.h"
   // ...
   DataTable dt(64);
   dt.AddFloatColumn("X0_f");
   dt.AddFloatColumn("X1_f");
   dt.AddDoubleColumn("X0X0pX1X1_d");
   double x[5];
   for(int i=0; i<63; i++) {
     x[0] = (i%9)-4.;  x[1] = (i/9)-3.;  x[2] = x[0]*x[0]+x[1]*x[1];
     dt.AddRow(x);
   }
   // Printing table info
   cout << dt ;
   // Saving object into a PPF file
   POutPersist po("dtable.ppf");
   po << dt ;
   \endcode

  DataTableRow objects can also be used to fill the table, or access the data, as
  shown in the example below:
  \code
  // We use the same table as in the example above:
  DataTableRow row = dt.EmptyRow();
  row[0] = 83.3; row[1] = 20.;   row[2] = 83.3*83.3+20.*20.;
  dt.AddRow(row);
  row["X0_f"] = 7.5; row["X1_f"] = 6.;  row["X0X0pX1X1_d"] = 7.5*7.5+6.*6.;
  dt.AddRow(row);  
  \endcode
*/


//! Default constructor - optional specification of block (or segment) size
DataTable::DataTable(sa_size_t segsz)
  : BaseDataTable(segsz)
{
}

//! copy constructor 
/*!
  The copy constructur shares the data if \b share=true. 
  Otherwise, the Clone() method is called to make a complete copy.
*/
DataTable::DataTable(DataTable const & a, bool share)
  : BaseDataTable(a.SegmentSize())
{
  if (share) Share(a);
  else Clone(a);
  mNEnt = a.mNEnt;
  mNSeg = a.mNSeg;
  if (a.mInfo)  mInfo = new DVList(*(a.mInfo));
}
//! Copy the table structure from \b a and shares the data (columns content)
void DataTable::Share(DataTable const & a)
{
  // On copie la structure de table 
  CopyStructure(a);
  // Et on partage les donnees des colonnes 
  for (size_t kk=0; kk<mNames.size(); kk++) {
    sa_size_t sk = mNames[kk].ser;
    sa_size_t ska = a.mNames[kk].ser;
    switch (mNames[kk].type) {
    case IntegerField :
      mICols[sk].Share(a.mICols[ska]); 
    break;
    case LongField :
      mLCols[sk].Share(a.mLCols[ska]); 
      break;
    case FloatField :
      mFCols[sk].Share(a.mFCols[ska]); 
      break;
    case DoubleField :
    case DateTimeField :
      mDCols[sk].Share(a.mDCols[ska]); 
      break;
    case ComplexField :
      mYCols[sk].Share(a.mYCols[ska]); 
      break;
    case DoubleComplexField :
      mZCols[sk].Share(a.mZCols[ska]); 
      break;
    case StringField :
      mSCols[sk].Share(a.mSCols[ska]); 
      break;
    default:
      throw ForbiddenError("DataTable::Share() : unknown column type ");
    break;
    }
  }  
}

//! Copy the table structure from \b a and duplicate (copy) the data (columns content)
void DataTable::Clone(DataTable const & a)
{
  // On copie la structure de table 
  CopyStructure(a);
  // Et on partage les donnees des colonnes 
  for (size_t kk=0; kk<mNames.size(); kk++) {
    sa_size_t sk = mNames[kk].ser;
    sa_size_t ska = a.mNames[kk].ser;
    switch (mNames[kk].type) {
    case IntegerField :
      mICols[sk].Clone(a.mICols[ska], true); 
    break;
    case LongField :
      mLCols[sk].Clone(a.mLCols[ska], true); 
      break;
    case FloatField :
      mFCols[sk].Clone(a.mFCols[ska], true); 
      break;
    case DoubleField :
    case DateTimeField :
      mDCols[sk].Clone(a.mDCols[ska], true); 
      break;
    case ComplexField :
      mYCols[sk].Clone(a.mYCols[ska], true); 
      break;
    case DoubleComplexField :
      mZCols[sk].Clone(a.mZCols[ska], true); 
      break;
    case StringField :
      mSCols[sk].Clone(a.mSCols[ska], true); 
      break;
    default:
      throw ForbiddenError("DataTable::Clone() : unknown column type ");
    break;
    }
  }  
}
//! Reset (/clear) the table content and structure
void DataTable::Clear()
{
  if ( (NVar() == 0) && (NEntry() == 0)) return;
  mNEnt = 0;
  mNSeg = 0;
  if (mVarD) delete[] mVarD;
  mVarD = NULL;
  if (mVarMTV) delete[] mVarMTV;
  mVarMTV = NULL;
  mNames.clear();
  if (mInfo) delete mInfo;
  mInfo = NULL;
  mMin.clear();
  mMax.clear();
  mMinMaxNEnt.clear();
  mIColsP.clear();
  mLColsP.clear();
  mFColsP.clear();
  mDColsP.clear();
  mYColsP.clear();
  mZColsP.clear();
  mSColsP.clear();

  mIColIdx.clear();
  mLColIdx.clear();
  mFColIdx.clear();
  mDColIdx.clear();
  mYColIdx.clear();
  mZColIdx.clear();
  mSColIdx.clear();

  mICols.clear();
  mLCols.clear();
  mFCols.clear();
  mDCols.clear();
  mYCols.clear();
  mZCols.clear();
  mSCols.clear();
}


/*! 
  Implements the action defined in the BaseDataTable interface.
  In the current implementation, throws an exception (ParmError) 
  if the table contains some data already.
*/
sa_size_t DataTable::AddColumn(FieldType ft, string const & cnom)
{
  if (NEntry() > 0) 
    throw ParmError("DataTable::AddColumn() Table contains already data ");
  CheckColName(cnom);
  sa_size_t ser; 
  sa_size_t idx = NVar(); 
  switch (ft) {
  case IntegerField :
    ser = mICols.size();
    mICols.push_back(SegDataBlock<int_4>(mSegSz));
    mIColIdx.push_back(idx);
    mIColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mICols.size(); kk++)
      mIColsP[kk] = &(mICols[kk]);
    break;
  case LongField :
    ser = mLCols.size();
    mLCols.push_back(SegDataBlock<int_8>(mSegSz));
    mLColIdx.push_back(idx);
    mLColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mLCols.size(); kk++)
      mLColsP[kk] = &(mLCols[kk]);
    break;
  case FloatField :
    ser = mFCols.size();
    mFCols.push_back(SegDataBlock<r_4>(mSegSz));
    mFColIdx.push_back(idx);
    mFColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mFCols.size(); kk++)
      mFColsP[kk] = &(mFCols[kk]);
    break;
  case DoubleField :
  case DateTimeField :
    ser = mDCols.size();
    mDCols.push_back(SegDataBlock<r_8>(mSegSz));
    mDColIdx.push_back(idx);
    mDColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mDCols.size(); kk++)
      mDColsP[kk] = &(mDCols[kk]);
    break;
  case ComplexField :
    ser = mYCols.size();
    mYCols.push_back(SegDataBlock< complex<r_4> >(mSegSz));
    mYColIdx.push_back(idx);
    mYColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mYCols.size(); kk++)
      mYColsP[kk] = &(mYCols[kk]);
    break;
  case DoubleComplexField :
    ser = mZCols.size();
    mZCols.push_back(SegDataBlock< complex<r_8> >(mSegSz));
    mZColIdx.push_back(idx);
    mZColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mZCols.size(); kk++)
      mZColsP[kk] = &(mZCols[kk]);
    break;
  case StringField :
    ser = mSCols.size();
    mSCols.push_back(SegDataBlock<string>(mSegSz));
    mSColIdx.push_back(idx);
    mSColsP.push_back(NULL);
    for(sa_size_t kk=0; kk<mSCols.size(); kk++)
      mSColsP[kk] = &(mSCols[kk]);
    break;
  default:
    throw ParmError("DataTable::AddColumn() unknown field type ");
    break;
  }
  colst col;
  col.nom = cnom;
  col.type = ft;
  col.ser = ser;
  mNames.push_back(col);

  // Tableaux de calcul min-max
  mMin.push_back(0.);
  mMax.push_back(0.);
  mMinMaxNEnt.push_back(0);

  return NVar();
}

