// This may look like C code, but it is really -*- C++ -*-
//  Classe image generique   E.Aubourg , E. Lesquoy     
//                           Modifs R. Ansari   04/95

// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#ifndef RZIMAGE_SEEN
#define RZIMAGE_SEEN

#include <iostream.h>

#include "rzvect.h"
#include "datatypes.h"
#include "ppersist.h"
#include "dvlist.h"

namespace SOPHYA {class GeneralFit;}

// Flags de verifications sur les indices 

//#define IMGRGCHECK
//#define IMGVOIDCHECK


// ------------- Classe RzImage ------------------  

typedef union {   // il faut acceder le bon en fonction de datatype
  RzVect<uint_1>* u1;
  RzVect<uint_2>* u2;
  RzVect<int_2>*  i2;
  RzVect<int_4>*  i4;
  RzVect<r_4>*    r4;
  RzVect<r_8>*    r8;
} ImgVectP ;



class RzImage : public PPersist , public AnyDataObj {
  //class RzImage EXC_AWARE {
public:
  enum {classId = ClassId_Image };

                RzImage(PBaseDataTypes dType, int sizx, int sizy, 
                        int imgId = 0, char const * imgName = 0);
                RzImage(PBaseDataTypes dType=kpbdt_unknown);
                RzImage(const RzImage&, int sharePixels=0);

                RzImage(char *flnm);    // Creation/Lecture fichier de obtenu par Write()

  virtual       ~RzImage(); 

  int_4                 ClassId() const         { return classId; }
  static PPersist*      Create()                { return new RzImage;}

  RzImage&      operator= (RzImage const &);

  inline int_4  XSize() const {return siz_x;}
  inline int_4  YSize() const {return siz_y;}

  inline int_4  XOrg() const {return org_x;}
  inline int_4  YOrg() const {return org_y;}

  inline float  XPxSize() const {return pxsz_x;}
  inline float  YPxSize() const {return pxsz_y;}

  inline ImgVectP    Vect() const {return vect;}
  inline void * VoidP() const {return voidP;}
  inline int    IsFits() const {return isFits;}

  inline PBaseDataTypes    PixelType() const { return dataType; }
  inline char * PixelNomType() const { return DataName(dataType); }

  inline char * Nom()  { return name; }
  inline const char * Nom() const { return name; }
  inline int_4  Ident() const { return id; }

  DVList&       Info();
 
  void          Allocate(PBaseDataTypes dType, int sizx, int sizy, ImgVectP* pvpsh=NULL);
  void          DeAllocPixels();
  void          SetPixels(PBaseDataTypes dType, int sizx, int sizy, void* data);

  void          SetOrg(int_4 orgx, int_4 orgy);
  void          SetPxSize(float szx=1, float szy=1);
  void          SetAtt(int nbnul = -1, int nbsat = -1,
                       float minpix = 0, float maxpix = -1.,
                       float moypix = 0, float sigpix = -1., 
                       float fnd = -1., float sigfnd = -1.);

  void          SetNameId(int imgid = 0, char const * nom = NULL);

// Fonctions d'impressions 
  virtual void  Print(ostream& os) const;
  inline void   Print() const { Print(cout); }

  virtual int   CheckDyn(double min=-9.e19, double max=9.e19);

// Resultats d'un GeneralFit
  RzImage* 	FitResidus(GeneralFit& gfit);
  RzImage* 	FitFunction(GeneralFit& gfit);

  void          Save(char *flnm);
  virtual void  WriteSelf(POutPersist&) const;
  virtual void  ReadSelf(PInPersist&);

//  Fonctions d'acces aux pixels avec conversion 
// ATTENTION LENT ! EVITER AUX ENDROITS CRITIQUES.

  int           IValue(int i, int j) const ;    
  int           IValue(int k) const ;           
  float         FValue(int i, int j) const ;    
  float         FValue(int k) const ;
  double        DValue(int i, int j) const ;
  double        DValue(int k) const ;

// Quelques variables caracterisant la distribution des pixels 
  r_4       fond;         // Fond de l'image 
  r_4       sigmaFond;    // Fluctuation du fond
  int_4     nbNul;        // Nb de pixels < min
  int_4     nbSat;        // Nb de pixels > max 
  r_4       minPix;       // Valeur mini des pixels >= min <= max 
  r_4       maxPix;       // Valeur maxi des pixels >= min <= max 
  r_4       moyPix;       // Valeur moyenne des pixels
  r_4       sigPix;       // Sigma distribution des pixels 

protected:
  PBaseDataTypes  dataType;
  int_4     siz_x,siz_y;        //  Taille x,y    
  int_4     org_x,org_y;        //  Origines si image lu ds un fichier   
  r_4       pxsz_x, pxsz_y;     //  Taille des pixels 
  int_4     isFits;             //  Beurk. Ah que Reza ne me fait-il pas faire! 
  char name[32];                //  Nom de l'image (Gestion des objets) 
  int_4 id;                     //  Identificateur (Gestion des objets) 

  void* voidP;          // un pointeur sans type   -
                        // Vivement le RTTI qu'on puisse eliminer ces choses
  ImgVectP vect;        // il faut acceder le bon en fonction de datatype

  DVList* mInfo;        // Infos (variables) attachees a l'image
      
private:
  int (RzImage::* IVal_p)(int) const;
  float (RzImage::* FVal_p)(int) const;
  double (RzImage::* DVal_p)(int) const;
  
  int IVal_u1(int k) const;
  int IVal_u2(int k) const;
  int IVal_i2(int k) const;
  int IVal_r4(int k) const;
  int IVal_i4(int k) const;
  int IVal_r8(int k) const;
  
  float FVal_u1(int k) const;
  float FVal_u2(int k) const;
  float FVal_i2(int k) const;
  float FVal_r4(int k) const;
  float FVal_i4(int k) const;
  float FVal_r8(int k) const;
  
  double DVal_u1(int k) const;
  double DVal_u2(int k) const;
  double DVal_i2(int k) const;
  double DVal_r4(int k) const;
  double DVal_i4(int k) const;
  double DVal_r8(int k) const;
};

// Definition de l'operateur << 
inline ostream& operator << (ostream& s, RzImage const & img)
  {  img.Print(s);  return(s);  }

// Implementation des methodes d'acces pixels inline 

#ifdef IMGRGCHECK
#define RZCHECKIMG2(_x_,_y_)                                  \
      if ((_x_ >= siz_x) || (_y_ >= siz_y) ||              \
          (_x_ < 0) || (_y_ < 0)) THROW(rangeCheckErr);  \
      if (!voidP) THROW(nullPtrErr)
#define RZCHECKIMG1(_x_)                                  \
      if ((_x_ >= siz_x*siz_y) || (_x_ < 0)) THROW(rangeCheckErr);  \
      if (!voidP) THROW(nullPtrErr)
#else
#if defined(IMGVOIDCHECK)
#define RZCHECKIMG2(_x_,_y_) \
      if (!voidP) THROW(NullPtrErr)
#define RZCHECKIMG1(_x_,_y_) \
      if (!voidP) THROW(NullPtrErr)
#else
#define RZCHECKIMG2(_x_,_y_)
#define RZCHECKIMG1(_x_)
#endif
#endif /* IMGRGCHECK */

// Methodes d'acces pixels inline 

inline int RzImage::IValue(int k) const
{ 
RZCHECKIMG1(k);
return (this->*IVal_p)(k); 
}

inline int RzImage::IValue(int i, int j) const
{ 
RZCHECKIMG2(i,j);
return IValue(i+j*siz_x); 
}

inline float RzImage::FValue(int k) const
{ 
RZCHECKIMG1(k);
return (this->*FVal_p)(k); 
}

inline float RzImage::FValue(int i, int j) const
{ 
RZCHECKIMG2(i,j);
return FValue(i+j*siz_x); 
}

inline double RzImage::DValue(int k) const
{ 
RZCHECKIMG1(k);
return (this->*DVal_p)(k); 
}

inline double RzImage::DValue(int i, int j) const
{ 
RZCHECKIMG2(i,j);
return DValue(i+j*siz_x); 
}

#endif
