/* Interface Fits BINARY/ASCII Table     cmv 21/05/2001 */
#include "machdefs.h"
#include <stdlib.h>
#include <stdio.h>
#include "pexceptions.h"
#include "fbtntintf.h"

//////////////////////////////////////////////////////////////
FitsBTNtuIntf::FitsBTNtuIntf(string fname,int ihdu
               ,int_4 blen,int_4 bsens,uint_2 lp)
{
 DbgLevel = lp;
 SetNulVal();
 SetBuffer(blen,bsens);
 FitsFN = fname;
 IHdu = ihdu;
 Init();
}

FitsBTNtuIntf::FitsBTNtuIntf(char * cfname,int ihdu
               ,int_4 blen,int_4 bsens,uint_2 lp)
{
 DbgLevel = lp;
 SetNulVal();
 SetBuffer(blen,bsens);
 FitsFN = cfname;
 IHdu = ihdu;
 Init();
}

FitsBTNtuIntf::FitsBTNtuIntf(FitsBTNtuIntf& fbtnt)
{
 DbgLevel = fbtnt.DbgLevel;
 SetNulVal(fbtnt.NulVal);
 SetBuffer(fbtnt.BuffLen,fbtnt.BuffSens);
 FitsFN = fbtnt.FitsFN;
 IHdu = fbtnt.IHdu;
 Init();
}

FitsBTNtuIntf::FitsBTNtuIntf()
{
 DbgLevel = 0;
 SetNulVal();
 SetBuffer();
 FitsFN = "";
 IHdu = -1;
 Init();
}

FitsBTNtuIntf::~FitsBTNtuIntf()
{
 Delete();
}


//////////////////////////////////////////////////////////////
void FitsBTNtuIntf::ChangeBuffer(int_4 blen,int_4 bsens)
{
SetBuffer(blen,bsens);
DeAllocBuff();
AllocBuff();
}

void FitsBTNtuIntf::AllocBuff(void)
{
if(Buffer==NULL || NBcol<=0) return;
int_4 n = BuffLen; if(BuffSens==0) n++;
for(int icol=0;icol<NBcol;icol++)
  if(ColReadable[icol]) Buffer[icol] = new double[n];
    else Buffer[icol] = NULL;
}

void FitsBTNtuIntf::DeAllocBuff(void)
{
if(Buffer==NULL || NBcol<=0) return;
for(int icol=0;icol<NBcol;icol++)
  if(Buffer[icol]!=NULL) 
    {delete [] Buffer[icol]; Buffer[icol] = NULL;}
LineDeb = LineFin = -1;
}

void FitsBTNtuIntf::Delete()
{
 DeAllocBuff();
 delete [] Buffer; Buffer=NULL;
 if(mRet!=NULL) delete [] mRet;
 if(ColReadable!=NULL) delete [] ColReadable;

 int sta = 0;
 if(fits_close_file(FitsPtr,&sta)) printerror(sta);
 FitsPtr = NULL;
}

void FitsBTNtuIntf::Init()
{
 NBcol = 0; NBline = 0;
 LineDeb = LineFin = -1;
 ColName.resize(0);
 ColReadable = NULL;
 Buffer = NULL;
 mRet = NULL;
 NFitsRead = 0;

 if(FitsFN.size() <= 0 ) {
   FitsPtr = NULL;
   IHdu = -1;
   return;
 }

 //////////////////////////
 // Ouverture du fichier //
 //////////////////////////

 int sta=0,nhdu,hdutype;
 const char * cfitsfn = FitsFN.c_str();

 // Open fits
 if(fits_open_file(&FitsPtr,cfitsfn,READONLY,&sta)) printerror(sta);

 // Get number of hdu
 if(fits_get_num_hdus(FitsPtr,&nhdu,&sta)) printerror(sta);
 if(DbgLevel) cout<<"FitsBTNtuIntf::Init  nhdu="<<nhdu<<endl;
 if(nhdu<=0) {Delete(); return;}

 // Get HDU for bin table
 // si IHdu <=0 on cherche la 1ere bin table
 // sinon on se positionne sur IHdu
 if(IHdu<=0 || IHdu>nhdu)
   for(int ihdu=1;ihdu<=nhdu;ihdu++) {
     if(fits_movabs_hdu(FitsPtr,ihdu,&hdutype,&sta)) printerror(sta);
     if(DbgLevel) cout<<"...Init ihdu="
                      <<ihdu<<" hdutype="<<hdutype<<endl;
     if(hdutype==BINARY_TBL || hdutype==ASCII_TBL) {IHdu = ihdu; break;}
   }
 if(IHdu<=0 || IHdu>nhdu) {
   cout<<"NO BINARY or ASCII hdu found"<<endl;
   Delete();
   return;
 }
 if(fits_movabs_hdu(FitsPtr,IHdu,&hdutype,&sta)) printerror(sta);
 if(hdutype!=BINARY_TBL && hdutype!=ASCII_TBL) {Delete(); return;}

 // Get number of columns
 if(fits_get_num_cols(FitsPtr,&NBcol,&sta)) printerror(sta);
 if(DbgLevel) cout<<"...Init  NBcol="<<NBcol<<endl;
 if(NBcol<1) {Delete(); return;}
 mRet = new double[NBcol];
 ColReadable = new bool[NBcol];
 Buffer = new double*[NBcol];
 int icol;
 for(icol=0;icol<NBcol;icol++)
   {Buffer[icol]=NULL; ColReadable[icol]=false;}

 // Get number of rows
 if(fits_get_num_rows(FitsPtr,&NBline,&sta)) printerror(sta);
 if(DbgLevel) cout<<"...Init  NBline="<<NBline<<endl;
 if(NBline<1) {Delete(); return;}

 // Get column infos
 for(icol=0;icol<NBcol;icol++) {
   int colnum,typecode;
   char templt[16], colname[256];
   sprintf(templt,"%d",icol+1);
   if(fits_get_colname(FitsPtr,CASESEN,templt,colname,&colnum,&sta))
     printerror(sta);
   ColName.push_back(colname);
   if(fits_get_coltype(FitsPtr,icol+1,&typecode,NULL,NULL,&sta))
     printerror(sta);
   if(typecode!=TSTRING && typecode!=TCOMPLEX &&  typecode!=TDBLCOMPLEX)
        ColReadable[icol] = true;
   if(DbgLevel)
     cout<<"...Init col="<<icol+1
	 <<" name="<<ColName[icol]
         <<" typecode="<<typecode<<" (readable="
         <<ColReadable[icol]<<")"<<endl;
 }
 // Prepare buffer
 AllocBuff();

}

/////////////////////////////////////////////////
uint_4 FitsBTNtuIntf::NbLines() const
{
 return NBline;
}

uint_4 FitsBTNtuIntf::NbColumns() const
{
 return NBcol;
}

r_8 * FitsBTNtuIntf::GetLineD(int n) const
// Attention: n [0,NBline[, cfistio veut [1,NBline]
{
 int sta=0,anynul;
 if(n<0 || n>=NBline) return NULL;

 // Pas de bufferisation, on lit betement
 if(BuffLen==1) {
   NFitsRead++;
   for(int icol=0;icol<NBcol;icol++) {
     mRet[icol] = 0.;
     if(!ColReadable[icol]) continue;
     fits_read_col_dbl(FitsPtr,icol+1,n+1,1,1,NulVal,&mRet[icol],&anynul,&sta);
     if(sta) printerror(sta);
   }
   return mRet;
 }

 // Gestion avec bufferisation
 if(!Buffer) {
   cout<<"FitsBTNtuIntf::GetLineD Buffer not allocated"<<endl;
   return NULL;
 }
 if(n<LineDeb || n>LineFin) {
   NFitsRead++;
   long row1,row2,nrow;
   if(BuffSens>0) { // Cas remplissage forward
     row1 = n+1;
     row2 = row1+BuffLen-1; if(row2>NBline) row2 = NBline;
   } else if(BuffSens<0) { // Cas remplissage backward
     row2 = n+1;
     row1 = row2-BuffLen+1; if(row1<1) row1 = 1;
   } else { // Cas remplissage centre
     row1 = n+1 - BuffLen/2; if(row1<1) row1 = 1;
     row2 = n+1 + BuffLen/2; if(row2>NBline) row2 = NBline;
   }
   nrow = row2 - row1 + 1;
   LineDeb = row1-1; LineFin = row2-1;
   //cout<<"DBG-FitsRead: row1="<<row1<<" row2="<<row2<<" nrow="<<nrow
   //    <<" LineDeb,Fin="<<LineDeb<<","<<LineFin<<endl;
   for(int icol=0;icol<NBcol;icol++) {
     if(!ColReadable[icol]) continue;
     fits_read_col_dbl(FitsPtr,icol+1,row1,1,nrow,NulVal,Buffer[icol],&anynul,&sta);
     if(sta) printerror(sta);
   }
 }

 int_4 ibuf = n-LineDeb;
 for(int icol=0;icol<NBcol;icol++)
   if(ColReadable[icol]) mRet[icol] = (Buffer[icol])[ibuf];
     else mRet[icol] = 0.;

 return mRet;
}

r_8 FitsBTNtuIntf::GetCell(int n, int k) const
{
 if(n<0 || n>=NBline || k<0 || k>=NBcol) return 0.;
 r_8 * r = GetLineD(n);
 return r[k];
}

/*!
  Retourne une chaine de caracteres avec la declaration des noms de 
  variables. si "nomx!=NULL" , des instructions d'affectation
  a partir d'un tableau "nomx[i]" sont ajoutees (pour interface NTuple).
*/
string FitsBTNtuIntf::VarList_C(const char* nomx) const
{
 string rets;
 rets = "\ndouble";
 int icol;
 for(icol=0;icol<NBcol;icol++) {
  rets += " " + ColName[icol];
  if(icol!=NBcol-1) rets += ","; else rets += ";\n";
 }
 if(!nomx) return rets;
 char buff[256];
 for(icol=0;icol<NBcol;icol++) {
   sprintf(buff," = %s[%d];\n",nomx,icol);
   rets += ColName[icol] + buff;
 }
 return rets;
}

/////////////////////////////////////////////////
void FitsBTNtuIntf::printerror(int sta) const
{
 int stat = sta;
 fits_report_error(stdout,stat);
 return;
}

void FitsBTNtuIntf::Print(ostream& os,int lp) const
{
 os<<"FitsBTNtuIntf:Print ("<<BuffLen<<","<<NulVal<<")"
   <<" ncols="<<NBcol<<" nrows="<<NBline;
 if(lp>0) os<<" fitsrd="<<NFitsRead<<"*ncols";
 os<<"\n"<<FitsFN<<"["<<IHdu<<"]"<<endl;
 if(NBcol<=0 || lp<1) return;
 for(int icol=0;icol<NBcol;icol++) {
   os<<" "<<ColName[icol]<<"("<<ColReadable[icol]<<")";
   if(icol%4==3 || icol==NBcol-1) os<<endl;
 }
}
