/*

  XMunipack - FITS headers

  Copyright © 2009-2013, 2017 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Munipack is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Munipack.  If not, see <http://www.gnu.org/licenses/>.

*/

#ifndef _FITS_H_
#define _FITS_H_
#endif

#include <fitsio.h>
#include <algorithm>
#include <vector>
#include <wx/wx.h>
#include <wx/string.h>
#include <wx/arrstr.h>
#include <wx/filename.h>

#ifdef __WXDEBUG__
#include <wx/debug.h>
#endif

#define APEREXTNAME "APERPHOT"
#define PHCALNAME "PHCAL"
#define FITS_LABEL_X "X"
#define FITS_LABEL_Y "Y"
#define FITS_LABEL_APCOUNT "APCOUNT"
#define FITS_COL_PHOTONS "PHOTONS"
#define FITS_KEY_CSPACE "CSPACE"

enum fits_type {
  FITS_GRAY, FITS_COLOR, FITS_MULTI, FITS_3D,
  FITS_SCI, FITS_FLAT, FITS_DARK, FITS_BIAS, FITS_UNKNOWN
};

enum hdu_type {
  HDU_UNKNOWN, HDU_HEAD, HDU_IMAGE, HDU_TABLE
};

enum hdu_flavour {
  HDU_IMAGE_LINE, HDU_IMAGE_FRAME, HDU_IMAGE_CUBE, HDU_IMAGE_COLOR,
  HDU_IMAGE_UNKNOWN,
  HDU_TABLE_BIN, HDU_TABLE_ASCII, HDU_TABLE_UNKNOWN,
  HDU_DUMMY
};

enum itt_type {
  ITT_FIRST,
  ITT_LINE, ITT_ASINH, ITT_LOG, ITT_SQRT, ITT_GAMMA,
  ITT_NORM, ITT_LOGIS, ITT_ATAN, ITT_SQR,
  ITT_LAST
};

enum itt_kind {
  ITT_KIND_FIRST,
  ITT_KIND_ABS, ITT_KIND_REL,
  ITT_KIND_LAST
};

enum color_palette {
  PAL_FIRST,
  PAL_GREY, PAL_SEPIA, PAL_VGA, PAL_AIPS0, PAL_STAIR, PAL_COLOR, PAL_SAW,
  PAL_RAIN, PAL_MAD, PAL_COOL, PAL_HEAT, PAL_SPRING, PAL_WRAP,
  PAL_LAST
};

enum color_space {
  COLORSPACE_FIRST,
  COLORSPACE_XYZ,
  COLORSPACE_LAST
};

enum color_temperature {
  COLORTEMP_D65
};

enum units_type {
  UNIT_FIRST,
  UNIT_COUNT, UNIT_MAG, UNIT_PHOTON, UNIT_FLUX, UNIT_FNU, UNIT_FLAM, UNIT_ABMAG,
  UNIT_STMAG,
  UNIT_LAST
};

enum coords_type {
  COO_FIRST,
  COO_PIXEL, COO_EQDEG, COO_EQSIX,
  COO_LAST
};



// -- content of HDUs


class FitsHeader: public wxArrayString
{
public:
  static bool ParseRecord(const wxString&,wxString&,wxString&,wxString&);
  bool FindKey(const wxString&,wxString&, wxString&) const;
  bool FindVal(const wxString&,wxString&, wxString&) const;
  wxString GetKey(const wxString&) const;
  wxString GetUnit(const wxString&) const;
  int Bitpix() const;
  wxString Bitpix_str() const;
  wxString Exposure_str(const wxString&) const;
  bool IsOk() const;
};


class FitsHdu: public wxObject
{
public:
  FitsHdu(): type(HDU_UNKNOWN),modified(false) {}
  FitsHdu(const FitsHeader& h): header(h),type(HDU_HEAD),modified(false) {}

  size_t GetCount() const;
  wxString Item(size_t i) const;

  virtual wxString GetKey(const wxString& a) const;
  virtual long GetKeyLong(const wxString& a) const;
  virtual double GetKeyDouble(const wxString& a) const;
  virtual wxString GetUnit(const wxString& a) const;
  virtual int Bitpix() const;
  virtual wxString Bitpix_str() const;
  virtual wxString Exposure_str(const wxString& a) const;
  virtual wxString GetExtname() const;

  virtual int Type() const;
  virtual wxString Type_str() const;
  virtual int Flavour() const;
  virtual wxString Flavour_str() const;

  // templates and helpers for derived classes (Array + Table)
  virtual int Naxis() const;
  virtual long Naxes(int n) const;
  virtual long Width() const;
  virtual long Height() const;
  virtual bool IsOk() const;
  virtual bool IsColor() const;
  virtual bool IsModified() const { return modified; }

  virtual inline float Pixel(int) const { return 0; }
  virtual inline float Pixel(int, int) const { return 0; }
  virtual inline wxString Pixel_str(int, int) const;

  virtual bool GetWCS(double&,double&,double&,double&,double&,double&,
		      double&) const;

protected:

  FitsHeader header;
  hdu_type type;
  bool modified;

};


inline wxString FitsHdu::Pixel_str(int x, int y) const
{
  wxString a;
  a.Printf("%g",Pixel(x,y));
  return a;
}



class FitsArrayData : public wxObjectRefData
{
public:
  FitsArrayData();
  FitsArrayData(int, long *, float *);
  FitsArrayData(const FitsArrayData&);
  FitsArrayData& operator = (const FitsArrayData&);
  virtual ~FitsArrayData();

  int naxis;
  long *naxes;
  float *array;
  // int *array;
  // double *array;
};


class FitsArray: public FitsHdu
{
public:
  FitsArray();
  FitsArray(const FitsHdu&, int, long *, float *);
  FitsArray(const FitsHdu&);
  virtual ~FitsArray();
  FitsArray Copy() const;

  int Naxis() const;
  long Naxes(const int n) const;
  inline float Pixel(int) const;
  inline float Pixel(int, int) const;
  const float *PixelData() const;
  FitsArray Plane(int) const;
  long Npixels() const { return npixels; }

  int Flavour() const;
  wxString Flavour_str() const;

  bool IsOk() const;

protected:

  wxObjectRefData* CreateRefData() const;
  wxObjectRefData *CloneRefData(const wxObjectRefData *) const;

private:

  long npixels;

};

inline float FitsArray::Pixel(int i) const
{
  FitsArrayData *data = static_cast<FitsArrayData *>(m_refData);
  wxASSERT(data);
  float *array = data->array;
  wxASSERT(array && 0 <= i && i < npixels);

  return *(array + i);
}

inline float FitsArray::Pixel(int x, int y) const
{
  FitsArrayData *data = static_cast<FitsArrayData *>(m_refData);
  /*
  wxASSERT(data && data->array && data->naxes && data->naxis == 2);
  wxASSERT(0 <= x && x < data->naxes[0] && 0 <= y && y < data->naxes[1]);
  */
  return *(data->array+y*data->naxes[0] + x);
}



class FitsArrayStat: public FitsArray
{
public:
  FitsArrayStat(const FitsArray&, int=0);
  void Statistics(int);
  float Med() const { return med; }
  float Mad() const { return mad; }
  float GetMin() const { return min; }
  float GetMax() const { return max; }

private:

  float med, mad, min, max;
  float QMed(int, float *, int);

};

// ?? move to fits.cpp ???
class FitsTableColumnData: public wxObjectRefData
{
public:
  FitsTableColumnData();
  FitsTableColumnData(int, long, float *);
  FitsTableColumnData(int, long, int *);
  FitsTableColumnData(int, long, char **);
  FitsTableColumnData(int, long, char *);
  FitsTableColumnData(int, long, double *);
  FitsTableColumnData(int, long, short *);
  FitsTableColumnData(int, long, long *);
  FitsTableColumnData(int, long, bool *);
  FitsTableColumnData(const FitsTableColumnData&);
  FitsTableColumnData& operator = (const FitsTableColumnData&);
  virtual ~FitsTableColumnData();

  int typecode;
  long nrows;
  bool *otable;
  char *btable;
  short *stable;
  int *itable;
  long *ltable;
  float *ftable;
  double *dtable;
  char **ctable;

};

class FitsTableColumn: public wxObject
{
public:
  FitsTableColumn();
  FitsTableColumn(int, long, double *);
  FitsTableColumn(int, long, float *);
  FitsTableColumn(int, long, int *);
  FitsTableColumn(int, long, char **);
  FitsTableColumn(int, long, char *);
  FitsTableColumn(int, long, bool *);
  FitsTableColumn(int, long, short *);
  FitsTableColumn(int, long, long *);
  //  FitsTableColumn(const FitsTableColumn&);
  //  FitsTableColumn& operator = (const FitsTableColumn&);
  FitsTableColumn Copy() const;
  virtual ~FitsTableColumn();

  int GetColType() const;
  long Nrows() const;
  inline float Cell(int x) const;
  inline wxString Cell_str(int x) const;

  const float *GetCol_float() const;
  const double *GetCol_double() const;
  const char **GetCol_char() const;
  const long *GetCol_long() const;

protected:

  wxObjectRefData* CreateRefData() const;
  wxObjectRefData *CloneRefData(const wxObjectRefData *) const;

};

inline float FitsTableColumn::Cell(int x) const
{
  //  wxASSERT(0 <= y && y < (int) columns.size());
	   //  FitsTableColumnData data(columns[y].GetData());
	   //  FitsTableColumn col(columns[y]);
  FitsTableColumnData *data = static_cast<FitsTableColumnData *>(m_refData);
	   //  wxASSERT(data && 0 <= x && x < data->nrows);
  wxASSERT(data && 0 <= x && x < data->nrows);

	   //if( data->ftable )
  return data->ftable[x];
	   //  else
	   //    return 0.0;

  /*
  FitsTableData *data = static_cast<FitsTableData *>(m_refData);
  wxASSERT(data && 0 <= x && x < data->ncols && 0 <= y && y < data->nrows);
  float *p = (float *) data->table[x*data->nrows + y];
  return (float) *p;
  */
  //return data->Cell_float(x,y);
}

inline wxString FitsTableColumn::Cell_str(int x) const
{
  /*
  wxASSERT(0 <= y && y < (int) columns.size());
  FitsTableColumnData *data = static_cast<FitsTableColumnData *>(m_refData);
  wxASSERT(data && 0 <= x && x < data->nrows);
  */

  FitsTableColumnData *data = static_cast<FitsTableColumnData *>(m_refData);
  wxASSERT(data && 0 <= x && x < data->nrows);

  wxString a;

  if( data->ftable )
    a.Printf("%g",data->ftable[x]);
  else if( data->dtable )
    a.Printf("%lg",data->dtable[x]);
  else if( data->stable )
    a.Printf("%d",data->stable[x]);
  else if( data->ltable )
    a.Printf("%ld",data->ltable[x]);
  else if( data->itable )
    a.Printf("%d",data->itable[x]);
  else if( data->btable )
    a.Printf("%d",data->btable[x]);
  else if( data->otable )
    a = data->otable[x] ? "T" : "F";
  else if( data->ctable )
    a = data->ctable[x];

//wxLogDebug("cell_str: "+a);
  return a;
}

// class FitsTableData: public wxObjectRefData
// {
// public:
//   FitsTableData();
//   //  FitsTableData(long, long, float *);
//   FitsTableData(long);
//   FitsTableData(const FitsTableData&);
//   FitsTableData& operator = (const FitsTableData&);
//   virtual ~FitsTableData();

//   void InsertColumn(long, long, float *);
//   void InsertColumn(long, long, int *);
//   void InsertColumn(long, long, char **);

//   inline float Cell_float(int,int) const;
//   //  inline int Cell_int(int,int) const;
//   //  inline char *Cell_char(int,int) const;
//   //  inline bool Cell_bool(int,int) const;

//   //  long ncols;
//   //  float *table;
//   //  int *typecode;
//   //  void *table;
//   //  FitsTableColumn *columns;
//   std::vector<FitsTableColumn> columns;
// };

// inline float FitsTableData::Cell_float(int x, int y) const
// {
//   wxASSERT(table && 0 <= x && x < ncols && 0 <= y && y < nrows && table[y] && typecode[y] == TFLOAT);
//   float *p = (float *) table[x*nrows + y];
//   return *p;
// 	   //   return (float) table[x*nrows + y];
// }

class FitsTableData: public wxObjectRefData
{
public:
  FitsTableData();
  FitsTableData(const std::vector<FitsTableColumn>&);

  std::vector<FitsTableColumn> columns;

};


class FitsTable: public FitsHdu
{
public:
  FitsTable();
  //  FitsTable(const FitsHdu&,int,long,long,float *);
  //  FitsTable(const FitsHdu&,int,long);
  FitsTable(const FitsHdu&,int,const std::vector<FitsTableColumn>&);
  FitsTable(const FitsHdu&);
  FitsTable Copy() const;

  //  inline float Cell(int,int) const;
  inline wxString Cell_str(int, int) const;
  //  const float *GetCol_float(int) const;
  //  const char **GetCol_char(int) const;
  //  const int *GetCol_int(int) const;
  int GetColType(int) const;
  bool IsOk() const;
  FitsTableColumn GetColumn(int) const;
  FitsTableColumn GetColumn(const wxString&) const;
  int GetColIndex(const wxString&) const;
  wxArrayString GetColLabels() const;

  long Nrows() const;
  int Ncols() const;
  int Naxis() const { return 2; }
  long Naxes(int n) const;
  //  inline float Pixel(int, int) const;

  int Flavour() const;
  wxString Flavour_str() const;

  void GetStarChart(wxOutputStream&);

protected:

  wxObjectRefData* CreateRefData() const;
  wxObjectRefData *CloneRefData(const wxObjectRefData *) const;

private:

  int fits_type;

};

// inline float FitsTable::Cell(int x, int y) const
// {
//   FitsTableData *data = static_cast<FitsTableData *>(m_refData);
//   wxASSERT(data);
//   const std::vector<FitsTableColumn> columns(data->columns);

//   wxASSERT(0 <= y && y < (int) columns.size());
//   return columns[y].Cell(x);
// 	   //  FitsTableColumnData data(columns[y].GetData());
//   // FitsTableColumn col(columns[y]);
//   // FitsTableColumnData *data = static_cast<FitsTableColumnData *>(col.m_refData);
//   // 	   //  wxASSERT(data && 0 <= x && x < data->nrows);
//   // 	   //  wxASSERT(0 <= x && x < data.Nrows());

//   // if( data->ftable )
//   //   return data->ftable[x];
//   // else
//   //   return 0.0;

//   /*
//   FitsTableData *data = static_cast<FitsTableData *>(m_refData);
//   wxASSERT(data && 0 <= x && x < data->ncols && 0 <= y && y < data->nrows);
//   float *p = (float *) data->table[x*data->nrows + y];
//   return (float) *p;
//   */
//   //return data->Cell_float(x,y);
// }

inline wxString FitsTable::Cell_str(int x, int y) const
{
  FitsTableData *data = static_cast<FitsTableData *>(m_refData);
  wxASSERT(data);
  const std::vector<FitsTableColumn> columns(data->columns);

  wxASSERT(0 <= y && y < (int) columns.size());
  return columns[y].Cell_str(x);

  /*
  wxASSERT(0 <= y && y < (int) columns.size());
  FitsTableColumnData *data = static_cast<FitsTableColumnData *>(columns[y].m_refData);
  wxASSERT(data && 0 <= x && x < data->nrows);

  wxString a;

  if( data->ftable )
    a.Printf("%g",data->ftable[x]);
  else if( data->itable )
    a.Printf("%d",data->itable[x]);
  else if( data->ctable )
    a = data->ctable[x];
  */

  /*
  switch(data->typecode[y]) {
  case TBYTE:
  case TINT:
  case TLONG:
    a.Printf("%d",(int **) data->table[x*data->nrows + y]); break;

  case TFLOAT:
     break;

  case TDOUBLE:
    a.Printf("%lf",(double **) data->table[x*data->nrows + y]); break;

  case TSTRING:
    a = wxString((char *) data->table[x*data->nrows + y]); break;

  }
  */

  //  wxString a;
  //  a.Printf("%g",Cell(x,y));
  //  return a;
}



// ---  FITS file

class FitsFile
{
public:

  FitsFile();
  FitsFile(const wxString&);
  FitsFile(const FitsHdu&);
  virtual ~FitsFile();
  void Clear();

  bool Status() const;
  size_t HduCount() const;
  int size() const;
  FitsHdu Hdu(size_t n) const;

  int Type() const;
  wxString Type_str() const;
  bool HasImage() const;
  bool HasPhotometry() const;
  bool HasPhcal() const;
  wxString GetURL() const;

  bool IsOk() const;
  bool IsModified() const;
  wxString GetName() const;
  wxString GetFullName() const;
  wxString GetPath() const;
  wxString GetFullPath() const;

  wxArrayString GetErrorMessage() const;
  wxString GetErrorDescription() const;

  bool Save(const wxString&);

private:

  wxString filename;
  bool status;
  int type;
  std::vector<FitsHdu> hdu;
  wxArrayString errmsg;
  wxString smsg;

  void Recognize();
  int merge_head(fitsfile *, const FitsHdu&, int *) const;

};


// --- meta classes

class FitsMetaHdu: public FitsHeader
{
public:
  FitsMetaHdu(const FitsHdu&, const wxImage&);
  FitsMetaHdu(const wxArrayString&, const wxImage&, int, long,
	      const std::vector<long>&, const wxString&, const wxString&);

  size_t Naxis() const;
  long Naxes(size_t n) const;
  long Width() const;
  long Height() const;
  long Nrows() const;
  int Ncols() const;
  int Type() const;
  int SubType() const;
  wxImage GetIcon() const;
  void SetIcon(const wxImage&);

  wxString Type_str() const;
  wxString SubType_str() const;

  std::vector<long> GetNaxes() const;
  wxString GetControlLabel() const;

private:

  int ncols, type, subtype;
  long nrows;
  std::vector<long> naxes;
  wxString type_str,subtype_str;
  wxImage icon;

};

class FitsMeta
{
public:
  FitsMeta();
  FitsMeta(const FitsFile&, const wxImage&, const std::vector<wxImage>&);
  FitsMeta(const wxString&, const wxString&, const std::vector<FitsMetaHdu>&,
	   const wxImage&, wxULongLong);
  void Clear();

  FitsMetaHdu Hdu(size_t) const;
  size_t HduCount() const;
  FitsMetaHdu *GetHdu(size_t);

  wxString Mtime() const;

  wxString GetURL() const;
  wxImage GetIcon() const;
  wxString GetKeys(const wxString &) const;
  wxString GetDateobs(const wxString &) const;
  wxString GetExposure(const wxString &) const;
  wxString GetFilter(const wxString &) const;

  int Type() const;
  wxString Type_str() const;

  bool IsOk() const;
  wxString GetName() const;
  wxString GetPath() const;
  wxULongLong GetSize() const;
  wxString GetFullName() const;
  wxString GetFullPath() const;
  wxString GetHumanReadableSize() const;

  void SetIcon(const wxImage&);
  void SetURL(const wxString&);

private:

  wxString url;
  wxString type_str;
  int type;
  std::vector<FitsMetaHdu> hdu;
  wxImage icon;
  wxULongLong size;

};


// image operations


class FitsHisto: public wxObject
{
public:
  FitsHisto();
  FitsHisto(int,int*,float*);
  FitsHisto(const FitsArray& , int=0);
  FitsHisto(const FitsArray& , float, float, int=0);
  virtual ~FitsHisto();


  int NBins() const;
  int Hist(int n) const;
  float Cents(int n) const;
  float BinWidth() const;
  float CentsMin() const;
  float CentsMax() const;

  bool IsOk() const;

private:

  float wbin,xmin,xmax;
  int npixels;

  void Create(int, const FitsArrayStat&);
};


class FitsGeometry: public FitsArray
{
public:
  FitsGeometry(const FitsArray&);

  FitsArray Scale(float);
  FitsArray Scale(const long *);
  FitsArray Scale(int,int);

  FitsArray SubArray(int,int,int,int);
  void SetSubArray(int,int,const FitsArray&);

  float MeanLine (float, float);
  float MeanRect (float, float, float, float);
};




// -- Intensity transfer table (ITT)


class FitsItt
{
public:
  FitsItt(int =ITT_LINE);
  FitsItt(int, int, const FitsArrayStat& );

  void SetItt(int);
  void SetItt(const wxString&);
  void SetKind(int);
  void SetSensitivity(float);
  void SetBlack(float);
  void SetBrightness(float);
  void SetContrast(float);
  void SetAmp(float);
  void SetZero(float);
  void SetRangeMin(float);
  void SetRangeMax(float);

  int GetItt() const;
  wxString GetItt_str() const;
  int GetKind() const;
  float GetSensitivity() const;
  float GetContrast() const;
  float GetBlack() const;
  float GetAmp() const;
  float GetZero() const;
  float GetMed() const;
  float GetMad() const;
  float GetRangeMin() const;
  float GetRangeMax() const;

  void Init(float, float);
  void Init(const FitsArrayStat&);
  void Reset();
  unsigned char Fscale(float) const;
  float Scale(float) const;
  float *Scale(int, const float *);
  float InvScale(int) const;

  static wxString Type_str(int);
  static wxArrayString Type_str();

  bool IsLinear() const;

private:

  int itt, kind;
  float black, sense, contrast, med, mad, amp, zero, rmax, rmin;

  float (FitsItt::*Func)(float) const;
  float Itt_linear(float) const;
  float Itt_asinh(float) const;
  float Itt_gamma(float) const;
  float Itt_log(float) const;
  float Itt_norm(float) const;
  float Itt_sqrt(float) const;
  float Itt_square(float) const;
  float Itt_logis(float) const;
  float Itt_atan(float) const;
  float invnorm(float) const;

};


class FitsColor: public wxObject
{
public:
  FitsColor();
  FitsColor(const wxString&,const FitsArray&);
  //  virtual ~FitsColor();

  void InitUV();
  void Init(const wxString&,const FitsArray&);

  void SetSaturation(float);
  void SetHue(float);
  //  void SetWhite(float);
  void SetWhitePoint(float =-1.0,float =-1.0);
  void SetNightVision(bool);
  void SetNightThresh(float);
  void SetNightWidth(float);
  void SetTrans(const wxString&);
  void SetTrans(const wxString&,const wxString&);
  void SetTrans(int,int);
  void SetTrans(int,int,float);
  void SetLevel(int,float);
  void SetWeight(int,float);
  float GetWeight(int) const;
  float GetLevel(int) const;
  float GetTrans(int,int) const;
  int GetColors() const;
  int GetBands() const;
  inline void XYZ_Luv(float,float,float,float,float&, float&, float&);
  inline void Luv_XYZ(float,float,float,float,float&, float&, float&);
  void XYZ_Luv(int,float*,float*,float*,float,float*, float*, float*);
  void Luv_XYZ(int,float*,float*,float*,float,float*, float*, float*);
  void NightVision(int,float*,float*,float*,float*);
  void Instr_XYZ(int,size_t,const float **,float*,float*,float*);
  void TuneColors(int,float*,float*,float*);

  float Scotopic(float,float,float);
  //  void Convert(float, float, float,
  //	       unsigned char &, unsigned char &, unsigned char &);
  //  void Transform(size_t, float *, float&, float&, float&);

  float GetSaturation() const { return saturation; }
  float GetHue() const { return 57.29577951*hue; }
  float GetWhitePoint_U() const { return uwhite; }
  float GetWhitePoint_V() const { return vwhite; }
  bool GetNightVision() const { return nvision; }
  float GetNightThresh() const { return nthresh; }
  float GetNightWidth() const { return nwidth; }
  float NightProfile(float) const;
  wxString GetColorspace() const;

  float InvGamma(float);

  static wxString Type_str(int);
  static wxArrayString Type_str();

private:

  wxString cspace;
  int ispace;
  bool nvision;
  float saturation, hue, nthresh, nwidth/*, Yn*/;
  float uwhite, vwhite;

};




// http://en.wikipedia.org/wiki/CIELUV_color_space
inline void FitsColor::XYZ_Luv(float X, float Y, float Z, float Yn,
			float &L, float &u, float &v)
{
  float s = X + 15.0*Y + 3.0*Z;
  float u1 = 4.0*X/s;
  float v1 = 9.0*Y/s;

  float yy = Y/Yn;
  if( yy > 0.0088565 /* = powf(6.0/29.0,3)*/ )
    L = 116.0*powf(yy,0.3333333) - 16.0;
  else
    L = 903.30*yy;   /* = pow(26.0/3.0,3) */

  float t = 13.0*L;
  u = t*(u1 - uwhite);
  v = t*(v1 - vwhite);
}

inline void FitsColor::Luv_XYZ(float L, float u, float v, float Yn,
			float &X, float &Y, float &Z)
{
  float s = 13.0*L;
  float u1 = u/s + uwhite;
  float v1 = v/s + vwhite;

  if( L <= 8.0 )
    Y = L*0.0011071*Yn; /* = powf(3.0/29.0,3) */
  else {
    float t = (L + 16.0)/116.0;
    Y = t*t*t*Yn;
    //    Y = ipowf((L + 16.0)/116.0,3)*Yn;
  }

  float t = Y/(4.0*v1);
  X = t*(9.0*u1);//-9.0*Y*u1/(v1*(u1 - 4.0) - v1*u1);
  Z = t*(12.0-3.0*u1-20.0*v1);//(9.0*Y - 15.0*Y*v1 - X*v1)/(3.0*v1);
}

// ---  FitsPalette

class FitsPalette: public wxObject
{
public:
  FitsPalette(int =PAL_GREY);
  virtual ~FitsPalette();

  void SetPalette(int);
  void SetPalette(const wxString&);
  void SetNegative(bool=false);
  int GetColors() const;
  int GetPalette() const;
  wxString GetPalette_str() const;
  bool GetNegative() const;

  unsigned char R(int i) const;
  unsigned char G(int i) const;
  unsigned char B(int i) const;
  void RGB(float, unsigned char&, unsigned char&, unsigned char&);
  unsigned char *RGB(int,float *);
  const unsigned char *RData() const;
  const unsigned char *GData() const;
  const unsigned char *BData() const;

  static wxString Type_str(int);
  static wxArrayString Type_str();

private:

  int pal;
  bool negative;

  void CreatePalette();
};



// projections

class FitsProjection
{
public:
  FitsProjection();
  FitsProjection(const wxString& t, double a,double d,double x,double y,
		 double s,double r, double z);
  FitsProjection(const wxString& t, double a,double d, double x, double y,
		 double cd11, double cd12, double cd21, double cd22);

  void ad2xy(double,double,double&,double&);
  void xy2ad(double,double,double&,double&);
  void gnomon(double,double,double&,double&);
  void ignomon(double,double,double&,double&);

private:

  wxString type;
  double acen, dcen, xcen, ycen, scale, angle, reflex;

};


// -- FitsCoo

class FitsCoo: public FitsArray
{
public:
  FitsCoo();
  FitsCoo(const FitsArray&);

  void SetType(int);
  void SetType(const wxString&);
  coords_type GetType() const;

  void GetEq(int,int,double&,double&);
  void RaSix(double,int&,int&,double&);
  void DecSix(double,char&,int&,int&,double&);

  wxString Get_str(int,int);
  void GetStr(int,int,wxString&,wxString&);

  void GetPix(int,int,wxString&,wxString&);
  void GetCoo(int,int,wxString&,wxString&);

  bool HasWCS() const;

  static wxString Label_str(int);
  static wxArrayString Label_str();

private:

  FitsProjection proj;
  coords_type type;
  bool haswcs;

};


// Photometric Systems

class PhotoFilter
{
public:
  PhotoFilter(): flux0(-1.0), leff(0.0), lwidth(0.0) {}
  PhotoFilter(const wxString& n,double f0, double le, double lw):
    name(n),flux0(f0),leff(le),lwidth(lw) {}

  bool IsOk() const { return flux0 > 0.0; }

  wxString name;
  double flux0, leff, lwidth;

};

class Photosys
{
  wxString name;
  std::vector<PhotoFilter> filters;

public:
  Photosys(const wxString& n, const std::vector<PhotoFilter>& f):
    name(n), filters(f) {}

  PhotoFilter GetFilter(const wxString&) const;
  wxString GetName() const { return name; }
};

class FitsPhotosystems
{
  std::vector<Photosys> phsystems;

public:
  FitsPhotosystems() {}
  FitsPhotosystems(const wxString&);

  bool IsOk() const;
  PhotoFilter GetFilter(const wxString&, const wxString&) const;

};

class PhotoConv
{
  double flux0, leff, lwidth, area, exptime, gain, scale;
  double cspeed, hplanck, ABspflux, STspflux;


 public:
  PhotoConv() {}
  PhotoConv(const PhotoFilter&, double, double, double, double);

  double GetFlux(double) const;
  double GetFnu(double) const;
  double GetFlam(double) const;
  double GetMag(double,double) const;
  double GetMag(double) const;
  double GetABMag(double) const;
  double GetSTMag(double) const;

};


class FitsValue
{
public:
  FitsValue();
  FitsValue(const FitsArray&, const wxString&, const wxString&, const wxString&,
	    const wxString&, const wxString&);
  FitsValue(const FitsArray&,const FitsArray&,const FitsArray&);

  void SetType(int);
  void SetType(const wxString&);
  units_type GetType() const;

  double Get(double) const;
  std::vector<double> Get(int,int) const;
  wxString Get_str(int,int) const;

  static wxString Label_str(int);
  static wxArrayString Label_str();

  bool HasCal() const { return hascal; }

private:

  units_type type;
  bool hascal;
  std::vector<FitsArray> arrays;
  std::vector<double> gain, area, exptime, scale;
  std::vector<wxString> filter;
  wxString photsys, fits_key_area, fits_key_gain, fits_key_exptime, fits_key_filter;
  FitsPhotosystems phsystems;
  std::vector<PhotoConv> phconv;

  bool init(const std::vector<FitsArray>&);
  void init_phsystem(const wxString&);
  double GetKeyDouble(const FitsHdu&, const wxString&) const;

};


class FitsTime
{
public:
  FitsTime():year(0), month(0), day(0), hour(0), minute(0), second(0),
	     milisecond(0) {}
  FitsTime(const wxString& );

  void SetDateTime(const wxString& );
  void SetDate(const wxString& );
  void SetTime(const wxString& );

  double Jd() const;
  wxString Date() const { return date; }
  wxString Time() const { return time; }

private:

  long year, month, day, hour, minute, second, milisecond;
  wxString date,time;

};


class FitsImage
{
public:
  FitsImage();
  FitsImage(const std::vector<FitsArray>&);
  FitsImage(const FitsArray&);
  FitsImage(const FitsArray&, const FitsArray&, const FitsArray&);
  FitsImage(const FitsFile&, int=0);
  virtual ~FitsImage();

  virtual FitsImage Thumb(int,int);
  virtual FitsImage GetSubImage(int,int,int,int);

  virtual FitsImage Scale(float);
  virtual FitsImage Scale(int,int);
  virtual void Rescale(float);
  virtual void SetSubImage(int,int,const FitsImage&);

  size_t GetCount() const;
  FitsArray Item(size_t) const;
  bool IsOk() const;
  bool IsColor() const;
  int GetWidth() const;
  int GetHeight() const;
  const std::vector<FitsArray> GetArrays() const;

private:

  std::vector<FitsArray> arrays;

};



class FitsBitmap: public wxObject
{
public:
  FitsBitmap();
  FitsBitmap(int,int,unsigned char *);
  virtual ~FitsBitmap();

  int GetWidth() const;
  int GetHeight() const;
  const unsigned char *GetRGB() const;
  unsigned char *NewRGB() const;
  unsigned char *NewTopsyTurvyRGB() const;
  bool IsOk() const;

};




class FitsDisplay
{
public:
  FitsDisplay(const wxString& =wxEmptyString);

  virtual void SetPalette(const FitsPalette&);
  virtual void SetItt(const FitsItt& );
  virtual void SetColor(const FitsColor& );
  virtual void SetTemperature(float);
  virtual void SetColorspace(const wxString&);

  FitsItt GetItt() const;
  FitsPalette GetPalette() const;
  FitsColor GetColor() const;
  float GetTemperature() const;
  wxString GetColorspace() const;
  wxArrayString GetArraySpaces() const;

  FitsBitmap GetImage(const FitsImage&);

private:

  FitsBitmap ConvertOfGray(const FitsArray&);
  //  FitsBitmap ConvertOfColor(const FitsArray&,const FitsArray&,const FitsArray&);
  FitsBitmap ConvertOfColor(const std::vector<FitsArray>&);
  FitsBitmap ConvertOfColorX(const std::vector<FitsArray>&);

  unsigned char *XYZ_sRGB(size_t, const float *, const float *, const float *);
  unsigned char *XYZ_AdobeRGB(size_t,const float *,const float *,const float *);

  inline unsigned char Cutoff(float) const;
  inline float Gamma(float);
  float *sRGBGamma(int,float *);
  float *AdobeGamma(int,float *);
  unsigned char *Crange(size_t,const float *);

  wxString cspace;
  wxArrayString spaces;
  FitsItt itt;
  FitsPalette pal;
  FitsColor color;
  float temp,xtemp,ytemp,ztemp;
};



inline unsigned char FitsDisplay::Cutoff(float f) const
{
  int j = int(f);

  if( j < 0 )
    return 0;
  else if( j > 255 )
    return 255;
  else
    return (unsigned char) j;
}

inline float FitsDisplay::Gamma(float r)
{
  if( r < 0.00304 )
    return 12.92*r;
  else
    return 1.055*powf(r,0.4166667) - 0.055;
}


// auxiliary  functions

bool FitsCopyFile(const wxString&,const wxString&);
//wxArrayString FitsColumns(const wxString&);
