/* Interface Fits BINARY/ASCII Table     cmv 21/05/2001 */
/*    Ajout commandes generique lecture/ecriture FITS   */
/*                           Reza , Jan 2006            */


#include "sopnamsp.h"
#include "machdefs.h"
#include <stdlib.h>
#include <stdio.h>
#include <typeinfo>
#include <iostream>
#include <string.h>
#include <string>

#include "nobjmgr.h"
#include "servnobjm.h"
#include "nomgadapter.h"
#include "pistdimgapp.h"
#include "fbtntintf.h"

#include "nomhistadapter.h"
#include "swfitsdtable.h"

#include "fitsmanager.h"
#include "fitshandler.h"
#include "fiosinit.h"

////////////////////////////////////////////////////////////
class NOMAdapter_FitsBT : public NObjMgrAdapter {
public:
  NOMAdapter_FitsBT(FitsBTNtuIntf* o = NULL);
  virtual ~NOMAdapter_FitsBT();

  virtual NObjMgrAdapter* Clone(AnyDataObj* o);
  virtual string GetDataObjType();
  virtual AnyDataObj* CloneDataObj(bool share=false);
  virtual void SavePPF(POutPersist& s, string const & nom);
  virtual void Print(ostream& os, int lev=0);
  virtual NTupleInterface* GetNTupleInterface(bool& adel);

protected:
  FitsBTNtuIntf* mFBT;
};

////////////////////////////////////////////////////////////
NOMAdapter_FitsBT::NOMAdapter_FitsBT(FitsBTNtuIntf* o)
 : NObjMgrAdapter(o)
{
mFBT = o;
}

NOMAdapter_FitsBT::~NOMAdapter_FitsBT()
{
}

NObjMgrAdapter* NOMAdapter_FitsBT::Clone(AnyDataObj* o)
{
FitsBTNtuIntf* g = dynamic_cast<FitsBTNtuIntf *>(o);
if(g) return ( new NOMAdapter_FitsBT(g) );
return ( new NObjMgrAdapter(o) );
}

string NOMAdapter_FitsBT::GetDataObjType()
{
return( "FitsBTNtuIntf " );
}

AnyDataObj* NOMAdapter_FitsBT::CloneDataObj(bool /*share*/)
{
return( new FitsBTNtuIntf(*mFBT) );
}

void NOMAdapter_FitsBT::SavePPF(POutPersist& pos, string const & nom)
{
cout<<"NOMAdapter_FitsBT::SavePPF: not implemented"<<endl;
}

void NOMAdapter_FitsBT::Print(ostream& os, int lev)
{
mFBT->Print(os, lev);
}

NTupleInterface* NOMAdapter_FitsBT::GetNTupleInterface(bool& adel)
{
adel = false;
return(mFBT);
}

////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
extern "C" {
void fitsbtadapter_init();
void fitsbtadapter_end();
}

class fitsbtadapterExecutor : public CmdExecutor {
public:
  fitsbtadapterExecutor();
  virtual ~fitsbtadapterExecutor();
  virtual int Execute(string& keyw, vector<string>& args, string& toks);
};

///////////////////////////////////////////////////////////
fitsbtadapterExecutor::fitsbtadapterExecutor()
{
FitsIOServerInit();  // On s'assure de l'initialisation du module FitsIOServer

NamedObjMgr omg;
PIACmd * mpiac = omg.GetImgApp()->CmdInterpreter();

// On enregistre l'adaptateur
Services2NObjMgr* serv = omg.GetServiceObj();
serv->RegisterClass(new FitsBTNtuIntf, new NOMAdapter_FitsBT);
// ainsi que pour l'adaptateur de SwFitsDataTable
serv->RegisterClass(new SwFitsDataTable, new NOMAdapter_DataTable );

// On enregistre la commande 
string hgrp = "FileIO";
string kw = "fitsadapt";
string usage = "fitsadapt: NTuple adaptor for Binary and ASCII Fits table\n" ;
usage += "Usage: fitsadapt fitsfile [nomobj hdu buflen[,bufsens,lp]]";
usage += "\n fitsfile : Fits file name";
usage += "\n nomobj : Object name (needed if file name misleading as C++ variable)";
usage += "\n hdu : Fits HDU (if <=0 take first binary/ascii table)";
usage += "\n buflen[,bufsens,lp] : buffer length (sens) for fits reading (default is 100,1,0)";
usage += "\n See also  scanfits writefits readfits ";
mpiac->RegisterCommand(kw, usage, this, hgrp);

kw = "scanfits";
usage = "scanfits: scan fits files and print information on each HDU\n" ;
usage += "Usage: scanfits FileName [slev=0...3]\n";
usage += "slev=1,3: header keywords , slev=2,3: try to read each HDU\n";
usage += "See also: readfits writefits fitsadapt ";
mpiac->RegisterCommand(kw, usage, this, hgrp); 

kw = "writefits";
usage = "writefits: Write objects to a fits file \n" ;
usage += "Usage: writefits nameobj/pattern FileName \n";
usage += "See also: scanfits readfits fitsadapt ";
mpiac->RegisterCommand(kw, usage, this, hgrp); 

kw = "savefits";
usage = "savefits: alias for writefits \n" ;
mpiac->RegisterCommand(kw, usage, this, hgrp); 

kw = "readfits";
usage = "readfits: Read one or all HDU's of a fits file \n" ;
usage += "Usage: readfits [-nobsz] FileName [num_hdu] \n";
usage += " num_hdu=1,..,NbHDU (uses FitsManager) \n";
usage += " -nobsz: Ignore HDU BSCALE/BZERO values -> BSCALE=1, BZERO=0 \n";
usage += "See also rdfitsarr scanfits writefits fitsadapt swfitstable ";
mpiac->RegisterCommand(kw, usage, this, hgrp); 

kw = "openfits";
usage = "openfits: alias for readfits \n" ;
mpiac->RegisterCommand(kw, usage, this, hgrp); 

kw = "rdfitsarr";
usage = "rdfitsarr: Read a single IMAGE HDU of a fits file as an array \n" ;
usage += "Usage: rdfitsarr FileName [arrType=D] [num_hdu=1] [arrName] [compd]\n";
usage += " ArrType= I/L/F/D: Array data type (int_4, int_8, r_4, r_8)\n";
usage += " compd : call array.CompactTrailingDimensions()\n";
usage += "See also readfits ";
mpiac->RegisterCommand(kw, usage, this, hgrp); 


kw = "swfitstable";
usage = "swfitstable: Creates/read an SwFitsDataTable from a fits file\n" ;
usage += "   SwFitsDataTable is a table which uses the BINARY-TBL\n";
usage += "   of the fits file as swap space - useful for large tables\n";
usage += "Usage: swfitstable FileName [num_hdu=2] [segsize=512]\n";
usage += "See also: readfits scanfits";
mpiac->RegisterCommand(kw, usage, this, hgrp); 
}

fitsbtadapterExecutor::~fitsbtadapterExecutor()
{
}

int fitsbtadapterExecutor::Execute(string& kw, vector<string>& tokens, string&)
{
NamedObjMgr omg;
if(kw == "fitsadapt") {
  if(tokens.size()<1)
    {cout<<"Usage: fitsadapt fitsfile [nomobj hdu buflen[,bufsens]]"<<endl;
     return(0);}
    int ihdu=0;
    int_4 buflen=100, bufsens=1, lp=0;
    string fname = tokens[0];
    string nom_obj ="";
    if(tokens.size()>1) nom_obj = tokens[1];
    if(tokens.size()>2) sscanf(tokens[2].c_str(),"%d",&ihdu);
    if(tokens.size()>3) sscanf(tokens[3].c_str(),"%d,%d,%d",&buflen,&bufsens,&lp);
    FitsBTNtuIntf* fbt = new FitsBTNtuIntf(fname,ihdu,buflen,bufsens,lp);

    if(nom_obj=="" || nom_obj=="!") nom_obj = omg.GetServiceObj()->FileName2Name(fname);
    omg.AddObj(fbt,nom_obj);
 }
else if (kw == "scanfits") {
  if(tokens.size() < 1)  {
    cout<<"Usage: scanfits FileName [slev] "<<endl;
    return 1;
  }
  int slev = 0;
  if(tokens.size() > 1)  slev = atoi(tokens[1].c_str());
  FitsManager::ScanFile(tokens[0], slev); 
  return 0;
}
else if (kw == "swfitstable") {
  if(tokens.size() < 1)  {
    cout<<"Usage: swfitstable FileName [num_hdu=2] [segsize=512]"<<endl;
    return 1;
  }
  int numhdu = 2;
  if(tokens.size() > 1)  numhdu = atoi(tokens[1].c_str());
  int segsize = 512;
  if(tokens.size() > 2)  segsize = atoi(tokens[2].c_str());
  SwFitsDataTable* swfd = new SwFitsDataTable(tokens[0], numhdu, segsize);
  if (swfd != NULL) {
    string nom = omg.GetServiceObj()->FileName2Name(tokens[0]);
    omg.AddObj(swfd, nom, false);
    cout << "swfitstable: SwFitsDataTable object" << nom << " from" 
	 << tokens[0] << " HDU=" << numhdu << endl;
  }    
  return 0;
}
else if ((kw == "writefits") || (kw == "savefits")) {
  if(tokens.size() < 2)  {
    cout<<"Usage: writefits nameobj/pattern FileName "<<endl;
    return 1;
  }
  vector<string> noms;
  omg.GetObjList(tokens[0], noms);
  if (noms.size() < 1) {
    cout<<"writefits/Error: no object with this name "<<endl;
    return 2;
  }
  cout << "writefits: Opening/Creating fits file " << tokens[1] << endl;
  FitsInOutFile fos(tokens[1], FitsInOutFile::Fits_Create);
  for(int kk=0; kk<noms.size(); kk++) {
    AnyDataObj* co = omg.GetObj(noms[kk]);
    if (co == NULL) continue;
    fos.SetNextExtensionName(noms[kk]);
    try {
      FitsManager::Write(fos, *co);
      cout << "writefits: Object " << noms[kk] << " written to HDU No " << fos.CurrentHDU() << endl;  
    }
    catch (NotFoundExc & exc) {
      cout << "writefits/Error writing object " << noms[kk] << " -> " << exc.Msg() << endl;
    }
  }
  return 0;
}


else if ((kw == "readfits") || (kw == "openfits")) {
  if ((tokens.size()<1) || ((tokens[0]=="-nobsz")&&(tokens.size()<2)))  {
    cout<<"Usage: readfits [-nobsz] FileName [num_hdu]"<<endl;
    return 1;
  }
  int offa = 0; 
  bool fgnobsz = false;
  if (tokens[0] == "-nobsz") {
    fgnobsz = true;  offa = 1;
  }
  cout << "readfits: Opening  fits file " << tokens[offa];
  FitsInOutFile fis(tokens[offa], FitsInOutFile::Fits_RO);
  if (fgnobsz) fis.SetBScaleBZero();
  int k1 = 1;
  int k2 = fis.NbHDUs();
  if (fis.CurrentHDU() > 1) k1 = k2 = fis.CurrentHDU(); 
  bool fgshdu = false;
  if (tokens.size() > (offa+1)) {  // Numero de HDU specifie
    k2 = k1 = atoi(tokens[offa+1].c_str());
    fgshdu = true;
    cout << " - HDU Num " << k1 << endl;
  }
  else cout << " - HDU's " << k1 << "-" << k2 << endl;

  string bnom = omg.GetServiceObj()->FileName2Name(tokens[offa]);
  string nom = bnom;
  char bun[16];
  int kon = 0;
  for(int k=k1; k<=k2; k++) {
    try {
      if (fgshdu) fis.MoveAbsToHDU(k);
      FitsHandlerInterface* fhi = FitsManager::Read(fis);
      AnyDataObj* obj = fhi->DataObj();
      if (obj) {
       kon++;
       if (kon > 1)  { 
	 sprintf(bun, "%d", kon);
	 nom = bnom + bun;
       }
       omg.AddObj(obj, nom, false);
       cout << "readfits: " << (string)typeid(*obj).name()
	    << "  read From HDU " << k << " ->" << nom << endl;
      }
      else delete fhi; 
      //Reza, Jan 2006: Sinon, le fhi ne sera jamais supprime, tant pis 
      // Il ne prend pas beaucoup de place, une fois que son objet est delete
    }
    catch (NotFoundExc & exc) {
      cout << "readfits/Error - HDU=" << k+1 << " : " << exc.Msg() << endl;
    }
    if (k<k2) fis.MoveToNextHDU();
  }
  return 0;  
}
//-- Lecture d'un tableau depuis fits avec possibilite de specification type
else if (kw == "rdfitsarr") {
  if (tokens.size()<1) {  
    cout << "Usage: rdfitsarr FileName [arrType=D] [num_hdu=1] [arrName] [compd]" << endl;
    return 1;
  }
  char atype = 'D';
  if (tokens.size()>1) atype = tokens[1][0];
  if ((atype != 'I')&&(atype != 'L')&&(atype != 'F')&&(atype != 'D'))  atype = 'D';
  int ihdu = 1;
  if (tokens.size()>2)  ihdu = atoi(tokens[2].c_str());
  cout << "rdfitsarr: Opening  fits file " << tokens[0] << " HDU=" << ihdu << endl;
  FitsInOutFile fis(tokens[0], FitsInOutFile::Fits_RO);
  fis.MoveAbsToHDU(ihdu);
  string arrname = "arr";
  if (tokens.size()>3)  arrname = tokens[3];
  else arrname = omg.GetServiceObj()->FileName2Name(tokens[0]);
  bool fgcompd = false;
  if ((tokens.size()>4)&&(tokens[4]=="compd"))  fgcompd = true;

  switch (atype) {
    case 'I' : 
      {
      TArray<int_4> * arr = new TArray<int_4>;
      FitsManager::Read(fis, (*arr));
      if (fgcompd) arr->CompactTrailingDimensions();
      omg.AddObj(arr, arrname, false);
      }
      break;
    case 'L' : 
      {
      TArray<int_8> * arr = new TArray<int_8>;
      FitsManager::Read(fis, (*arr));
      if (fgcompd) arr->CompactTrailingDimensions();
      omg.AddObj(arr, arrname, false);
      }
      break;
    case 'F' : 
      {
      TArray<r_4> * arr = new TArray<r_4>;
      FitsManager::Read(fis, (*arr));
      if (fgcompd) arr->CompactTrailingDimensions();
      omg.AddObj(arr, arrname, false);
      }
      break;
    case 'D' : 
      {
      TArray<r_8> * arr = new TArray<r_8>;
      FitsManager::Read(fis, (*arr));
      if (fgcompd) arr->CompactTrailingDimensions();
      omg.AddObj(arr, arrname, false);
      }
      break;

  }
}

 return(0);
}

///////////////////////////////////////////////////////////
static fitsbtadapterExecutor * piafbtex = NULL;

void fitsbtadapter_init()
{
if(piafbtex) delete piafbtex;
piafbtex = new fitsbtadapterExecutor;
}

void fitsbtadapter_end()
{
if(piafbtex) delete piafbtex;
piafbtex = NULL;
}
