#include "pexceptions.h"
#include "fitsautoreader.h"
#include "dvlist.h"

///////////////////////////////////////////////////////////////////////
// ---- gestion de persistance I/O format fits--
// objets
////////////////////////////////////////////////////////////////////

/*!
  \class SOPHYA::FITS_AutoReader
  \ingroup FitsIOServer
  FITS reader with automatic mapping on SOPHYA objects.
*/

FITS_AutoReader::FITS_AutoReader() {InitNull();};
FITS_AutoReader::FITS_AutoReader(const char inputfile[])
{
  InitNull();
  inFits_  = new FitsInFile (inputfile);
  filename_ = string(inputfile);
}
FITS_AutoReader::FITS_AutoReader(string const & inputfile)
{
  InitNull();
  inFits_  = new FitsInFile (inputfile);
  filename_ = inputfile;
}
FITS_AutoReader::~FITS_AutoReader()
{
  if (inFits_ != NULL) delete inFits_;
  if (dobj_ != NULL) delete dobj_;
}

int FITS_AutoReader::NbBlocks()
{
  return inFits_->NbBlocks();
}

 // parametre toutCharger : uniquement pour les BINTABLE et s'il ne
 // s'agit pas explicitement d'un objet SOPHYA, on charge toute la 
 // table en memoire sous forme d'un xntuple si toutCharger= true.
 // et si le nombre d'entrees est superieur a 1000.
 // sinon (c-a-d : il ne s'agit pas d'un objet connu de SOPHYA
 //                c'est une BINTABLE avec plus de 1000 entrees
 //                totuCharger = false)
 // on ouvre une FitsBTNtuIntf (lecture ulterieure, bufferisee, des valeurs)
AnyDataObj* FITS_AutoReader::ReadObject(int hdunum, bool toutCharger) const
{
  if (hdunum<=0) 
    {
      throw PException(" FITS_AutoReader::ReadObject : hdu number must be positive");
    }
  inFits_->ReadHeader(hdunum);
  if (inFits_->IsFitsEOF()) return NULL;
  if (inFits_->IsFitsERROR())
    {
      throw IOExc("FITS_AutoReader::ReadObject: FITSIO error in reading");
    }
  DVList dvl=inFits_->DVListFromFits();
  string nameObj = dvl.GetS("CONTENT");
  //    cout << " SOPHYA object identified as: " << dvl.GetS("CONTENT") << endl;
  if (nameObj == string("TArray") )
    {
      return newTArray();
    }
  else if  (nameObj == string("SphereHEALPix") )
    {
      return newSphereHEALPix();
    }
  else if  (nameObj == string("LocalMap") )
    {
      return newLocalMap();
    }
  else if  (nameObj == string("NTuple") )
    {
      return newNTuple();
    }
  else if  (nameObj == string("XNTuple") )
    {
      return newXNTuple();
    }
  // on n'a trouve aucun nom d'objet connu de SOPHYA, on fait une 
  // recherche qualitative.
  //
  // si c'est une image fits, on cree un TArray
  else if (inFits_->IsFitsImage())
    {
      return newTArray();
    }
  // si c'est une bintable on cherche le mot cle ORDERING pour identifier 
  // une spherehealpix
  else if (inFits_->IsFitsTable())
    {
      if (dvl.GetS("ORDERING") != string("")) return newSphereHEALPix();
      // sinon on cherche ntuple ou xntuple
      else
	{
	  int index=0;
          int k;
	  for (k=0; k < inFits_->NbColsFromFits(); k++) 
	    {
	      if (inFits_->ColTypeFromFits(k) != FitsFile::FitsDataType_float)
		{
		index = 1;
		break;
	      }
	  }
	if (index == 0)  return newNTuple();
	else 
	  {
	    if (!toutCharger ) 
	      {
		int maxEntries=0;
                int k;
		for ( k=0; k < inFits_->NbColsFromFits(); k++) 
		  {
		    if (inFits_->NentriesFromFits(k) > maxEntries ) maxEntries =  inFits_->NentriesFromFits(k); 
		  }
		if ( maxEntries > 1000) return newFitsBTNtuIntf(hdunum);
		else return newXNTuple();
	      }
	    else  return newXNTuple();

	  }
      }
    }
  else
    {
      cout << " WARNING ( FITS_AutoReader::ReadObject) : object not recognized as a  SOPHYA object" << endl;
      return new DVList(dvl);
    }
  
}

AnyDataObj*  FITS_AutoReader::newTArray() const
{
      FitsFile::FitsDataType dtype = inFits_->ImageType();
      switch (dtype)
	{
	case FitsFile::FitsDataType_double :
	  {
	    TArray<r_8>* matptr = new TArray<r_8>;
	    FITS_TArray<r_8> fta(matptr);
	    fta.Read(*inFits_, inFits_->currentHeaderIndex());
	    return matptr;
	  }
	case FitsFile::FitsDataType_float :
	  {
	    TArray<r_4>* matptr = new TArray<r_4>;
	    FITS_TArray<r_4> fta(matptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return matptr;
	  }
	case  FitsFile::FitsDataType_int :
	  {
	    TArray<int_4>* matptr = new TArray<int_4>;
	    FITS_TArray<int_4> fta(matptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return matptr;
	  }
	default :
	  cout << "type = " << (int) dtype << endl;
	  throw IOExc("FITS_AutoReader::ReadObject : unsupported data type for TArray");
	}
}

AnyDataObj*  FITS_AutoReader::newSphereHEALPix() const
{
      FitsFile::FitsDataType dtype;
      if (inFits_->IsFitsTable()) dtype = inFits_->ColTypeFromFits(0);
      else
	{
	  cout << " WARNING : Sperehealpix not binary table : unusual " << endl;
	  dtype = inFits_->ImageType();
	}
      switch (dtype)
	{
	case FitsFile::FitsDataType_double :
	  {
	    SphereHEALPix<r_8>* sphptr = new SphereHEALPix<r_8>;
	    FITS_SphereHEALPix<r_8> fta(sphptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return sphptr;
	  }
	case FitsFile::FitsDataType_float :
	  {
	    SphereHEALPix<r_4>* sphptr = new SphereHEALPix<r_4>;
	    FITS_SphereHEALPix<r_4> fta(sphptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return sphptr;
	  }
        //CMV: Bon c'est quand meme sympa de pouvoir re-lire ce
	// qu'on a ecrit !! Je fais cette modif mais a priori
	// ce n'est que du sparadra car ca devrait planter
	// sur certaines machines (int!=long).
	case  FitsFile::FitsDataType_int :
	case  FitsFile::FitsDataType_long :
	  {
	    SphereHEALPix<int_4>* sphptr = new SphereHEALPix<int_4>;
	    FITS_SphereHEALPix<int_4> fta(sphptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return sphptr;
	  }
	default :
	  cout << "type = " << (int) dtype << endl;
	  throw IOExc("FITS_AutoReader::ReadObject : unsupported data type for SphereHEALpix");
	}
}

AnyDataObj*  FITS_AutoReader::newLocalMap() const
{
      FitsFile::FitsDataType dtype;
      if (inFits_->IsFitsTable()) dtype = inFits_->ColTypeFromFits(0);
      else
	{
	  cout << " WARNING : Localmap not binary table : unusual " << endl;
	  dtype = inFits_->ImageType();
	}
      switch (dtype)
	{
	case FitsFile::FitsDataType_double :
	  {
	    LocalMap<r_8>* locmptr = new LocalMap<r_8>;
	    FITS_LocalMap<r_8> fta(locmptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return locmptr;
	  }
	case FitsFile::FitsDataType_float :
	  {
	    LocalMap<r_4>* locmptr = new LocalMap<r_4>;
	    FITS_LocalMap<r_4> fta(locmptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return locmptr;
	  }
	case  FitsFile::FitsDataType_int :
	  {
	    LocalMap<int_4>* locmptr = new LocalMap<int_4>;
	    FITS_LocalMap<int_4> fta(locmptr);
	    fta.Read(*inFits_,  inFits_->currentHeaderIndex());
	    return locmptr;
	  }
	default :
	  cout << "type = " << (int) dtype << endl;
	  throw IOExc("FITS_AutoReader::ReadObject : unsupported data type for LocalMap");
	}
}
NTuple* FITS_AutoReader::newNTuple() const
{
      NTuple* ntptr = new NTuple;
      FITS_NTuple fta(ntptr);
      fta.Read(*inFits_,  inFits_->currentHeaderIndex());
      return ntptr;
}
XNTuple* FITS_AutoReader::newXNTuple() const
{
      XNTuple* xntptr = new XNTuple;
      FITS_XNTuple fta(xntptr);
      fta.Read(*inFits_,  inFits_->currentHeaderIndex());
      return xntptr;
}

FitsBTNtuIntf* FITS_AutoReader::newFitsBTNtuIntf(int hdunum) const
{
      FitsBTNtuIntf* btnptr = new FitsBTNtuIntf(filename_,hdunum);
      return btnptr;
}

