/* Lecteur Fits de bolometre */
#include "machdefs.h"
#include <stdlib.h>
#include <stdio.h>
#include "pexceptions.h"
#include "fboloread.h"

#define _NCOL_BOLO_MIN_ 5

/*!
  \class SOPHYA::FitsBoloRead
  \ingroup FitsIOServer
  Class for defining a bolometer by connecting columns out FITS files
  \verbatim
  -- Exemple:
  FitsBoloRead fbr(blen,bsens);
  fbr.SetDebug(2);

  fbr.SetBolo("bolomuv","bolo.fits",0);
  fbr.SetFlag("flag");
  fbr.SetSNum("samplenum");
  fbr.SetAlpha("alpha","alpha.fits",0);
  fbr.SetDelta("delta");
  int col1 = fbr.AddCol("altitude","other_file.fits");
  fbr.Print(2);
  cout<<"NRows= "<<fbr.GetNbLine()<<endl;

  cout<<"Reading element by elements"<<endl;
  for(long i=0; i<fbr.GetNbLine(); i++) {
    double a = fbr.GetAlpha(i);
    double d = fbr.GetDelta(i);
    double b = fbr.GetBolo(i);
    double s = fbr.GetSNum(i);
    double f = fbr.GetFlag(i);
    double c1 = fbr.GetCol(col1,i);
    cout<<i<<" s="<<(long long)s<<" b="<<b<<" f="<<f
        <<" a="<<a<<" d="<<d<<" c1="c1<<endl;
  }

  cout<<"Reading with vectors"<<endl;
  TVector<double> Va,Vd,Vb,Vf;
  TVector<float> Vc1;
  TVector<int_4> Vs;
  for(long i=0, istep=1000; i<fbr.GetNbLine(); i+=istep) {
    long i2=i+istep-1; if(i2>=fbr.GetNbLine()) i2=fbr.GetNbLine()-1;
    fbr.GetSNum(i,i2,Vs);
    fbr.GetBolo(i,i2,Vb);
    fbr.GetFlag(i,i2,Vf);
    fbr.GetAlpha(i,i2,Va);
    fbr.GetDelta(i,i2,Vd);
    fbr.GetCol(col1,i,i2,Vc1);
  }
  \endverbatim
*/

//////////////////////////////////////////////////////////////
/*!
  Constructor. Define the structure of the bolometer columns connector.
  \param blen : read buffer length
  \param bsens : buffer reading direction
  \verbatim
  - bsens>0    read forward
    bsens<0    read backward
    bsens==0   read centered
  \endverbatim
*/
FitsBoloRead::FitsBoloRead(long blen,long bsens)
  : NBline(0), DbgLevel(0), BuffLen(blen), BuffSens(bsens)
{
  for(int i=0;i<_NCOL_BOLO_MIN_;i++) {
    mFName.push_back("");
    mLabel.push_back("");
    mHDU.push_back(0);
    mFABT.push_back(NULL);
  }
}

/*! Destructor */
FitsBoloRead::~FitsBoloRead(void)
{
 for(int i=0; i<(int)mFABT.size(); i++)
   if(mFABT[i]!=NULL) {delete mFABT[i]; mFABT[i]=NULL;}
 // NBline=0; mFABT.resize(_NCOL_BOLO_MIN_);
}

//////////////////////////////////////////////////////////////
/*!
 Add the column number "col" to the bolometer connector.
 Protected method. Do not use directly but read the doc.
 \param col : number of the column to be added
 \param label : label of the column
 \param fname : FITS file name containing the column
 \param ihdu : FITS HDU containing the column
 \verbatim
 - col<0     : add a new column
   col<size  : fill a new column
   col>=size : throw exception
 - The method also set the number of rows of the Bolometer connector
   as the smallest number of rows of the connected columns.
   That could be changed by using the SetNbLine() method.
 -----------------------------------------------------------------
 It is very boring to define all the fits file names and hdu for
   the predefined TOI SNum,Bolo,Flag,Alpha,Delta as some of them
   are in the same FITS file (and HDU).
 The connector pre-defined a logic as follow:
 ... if fname_Bolo=="" try fname_SNum
 ... if fname_SNum=="" try fname_Bolo
 ... if fname_Flag=="" try fname_Bolo first
 ...                       fname_SNum second.
 ... if fname_Alpha=="" try fname_Delta first
 ...                        fname_Bolo second
 ...                        fname_SNum third
 ... if fname_Delta=="" try fname_Alpha first
 ...                        fname_Bolo second
 ...                        fname_SNum third
 Of course, the decision differs depending on the order
   of calling the addcol() method.
 -----------------------------------------------------------------
 \endverbatim
 \warning ihdu[1,nhdu], col=[0,nrow-1]
 \return The number of the column added in the FitsBoloRead connector.
*/
int FitsBoloRead::addcol(int col,const char *label,const char* fname,int ihdu)
{
 if(col>=(int)mFABT.size()) // Not an existing column
   throw ParmError("FitsBoloRead::addcol: column number not existing\n");

 if(col>=0) {  // Already filled column
   if(mFABT[col]!=NULL) delete mFABT[col];
 } else {      // Prepare for new column
   col=mFABT.size();
   mFName.push_back("");
   mLabel.push_back("");
   mHDU.push_back(0);
   mFABT.push_back(NULL);
 }

 if(fname) mFName[col] = fname; else mFName[col] = "";
 mLabel[col] = label;
 mHDU[col]   = ihdu;
 // Try to guess the fname and ihdu if not defined
 Gess_If_Not_Define(col);

 mFABT[col] =
   new FitsABTColRead(mFName[col].c_str(),mLabel[col].c_str()
                     ,mHDU[col],BuffLen,BuffSens,(int)DbgLevel);

 // Set the number of rows as the smallest of for connected columns
 long nrows = mFABT[col]->GetNbLine();
 if(NBline==0) NBline = nrows;
 else if(nrows<NBline) NBline = nrows;

 return col;
}

/*!
  Guess what are the File name and HDU if they are not explicitly defined.
  (See addcol() doc)
*/
void FitsBoloRead::Gess_If_Not_Define(int col)
{
 if(col<0 || col>=_NCOL_BOLO_MIN_) return; // Pas une colonne predefinie
 if(mFName[col].size()>0) return; // definition explicite

 if(col==ColSNum) {            // Cas du Sample Number
   if(mFName[ColBolo].size()>0)
     {mFName[col]=mFName[ColBolo]; mHDU[col]=mHDU[ColBolo]; return;}

 } else if(col==ColBolo) {     // Cas du Bolo
   if(mFName[ColSNum].size()>0)
     {mFName[col]=mFName[ColSNum]; mHDU[col]=mHDU[ColSNum]; return;}

 } else if(col==ColFlag) {     // Cas du Flag
   if(mFName[ColBolo].size()>0)
     {mFName[col]=mFName[ColBolo]; mHDU[col]=mHDU[ColBolo]; return;}
   if(mFName[ColSNum].size()>0)
     {mFName[col]=mFName[ColSNum]; mHDU[col]=mHDU[ColSNum]; return;}

 } else if(col==ColAlpha) {    // Cas du Alpha
   if(mFName[ColDelta].size()>0)
     {mFName[col]=mFName[ColDelta]; mHDU[col]=mHDU[ColDelta]; return;}
   if(mFName[ColBolo].size()>0)
     {mFName[col]=mFName[ColBolo]; mHDU[col]=mHDU[ColBolo]; return;}
   if(mFName[ColSNum].size()>0)
     {mFName[col]=mFName[ColSNum]; mHDU[col]=mHDU[ColSNum]; return;}

 } else if(col==ColDelta) {    // Cas du Delta
   if(mFName[ColAlpha].size()>0)
     {mFName[col]=mFName[ColAlpha]; mHDU[col]=mHDU[ColAlpha]; return;}
   if(mFName[ColBolo].size()>0)
     {mFName[col]=mFName[ColBolo]; mHDU[col]=mHDU[ColBolo]; return;}
   if(mFName[ColSNum].size()>0)
     {mFName[col]=mFName[ColSNum]; mHDU[col]=mHDU[ColSNum]; return;}
 }
}

//////////////////////////////////////////////////////////////
/*! Set the number of rows for that class, choosing column "col"
  \param col : number of the column giving the number of rows
  \verbatim
  col : <0         set nrows to the smallest of all connected columns
  col : [0,ncol-1] set nrows to the number of rows of column "col"
  col : >=ncol     trow exception
  \endverbatim
  \warning By default, the number of rows is set to the smallest of all connected columns
*/
void FitsBoloRead::SetNbLine(int col)
{
  if(col>=(int)mFABT.size())  // Bad column number
   throw ParmError("FitsBoloRead::SetNbLine: bad column number\n");

 if(col>=0) {  // existing column, return its NbLine
   if(mFABT[col]==NULL)
     throw NullPtrError("FitsBoloRead::SetNbLine: column not connected\n");
   NBline = mFABT[col]->GetNbLine();
 }

 // take the smallest NbLine for all columns
 NBline = 0;
 for(col=0; col<(int)mFABT.size(); col++) {
   if(mFABT[col]==NULL) continue;
   long nbl = mFABT[col]->GetNbLine();
   if(nbl==0) continue;
   if(NBline==0 || nbl<NBline) NBline = nbl;
 }
}

//////////////////////////////////////////////////////////////
/*! Print */
void FitsBoloRead::Print(ostream& os,int lp) const
{
 os<<"FitsBoloRead::Print: NBline="<<NBline<<" NCol="<<GetNbCol()<<endl;
 for(int i=0; i<(int)mFABT.size(); i++) {
   if(mFABT[i]==NULL) {
      cout<<"...Col "<<i<<" not connected"<<endl;
      continue;
   } else {
     cout<<"--> Col "<<i<<" label="<<mLabel[i]<<" hdu="<<mHDU[i]
         <<"        FName="<<mFName[i]<<endl;
     if(lp>0) mFABT[i]->Print(os,lp);
   }
 }
}

#undef _NCOL_BOLO_MIN_
