/*

  XMunipack - FITS headers

  Copyright © 2009-2013, 2017-9 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_

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

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

#ifdef __WXOSX__
#define __ADOBERGB__
#else
#define __sRGB__
#endif


#define FINDEXTNAME "FIND"
#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_COLOUR, 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_COLOUR,
  HDU_IMAGE_UNKNOWN,
  HDU_TABLE_BIN, HDU_TABLE_ASCII, HDU_TABLE_UNKNOWN,
  HDU_DUMMY
};

enum zoom_type {
  ZOOM_FIRST,
  ZOOM_SHRINK, ZOOM_ZOOM,
  ZOOM_LAST
};

enum itt_type {
  ITT_FIRST,
  ITT_LINE, ITT_LOGIS, ITT_SQRT, ITT_SQR,
  ITT_LAST
};

enum tone_kind {
  TONE_KIND_FIRST,
  TONE_KIND_ABS, TONE_KIND_REL,
  TONE_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 tune_operation {
  OP_TUNE_ZOOM=2, OP_TUNE_SCALE=4, OP_TUNE_ITT=8, OP_TUNE_PAL=16,
  OP_TUNE_COLOUR=32, OP_TUNE_RGB=64
};

enum units_type {
  UNIT_FIRST,
  UNIT_COUNT, UNIT_PHOTON, UNIT_MAG, UNIT_INTENSITY,
  UNIT_LAST
};

enum phquantity_type {
  PHQUANTITY_FIRST,
  PHQUANTITY_COUNT, PHQUANTITY_PHOTON, PHQUANTITY_MAG, PHQUANTITY_INTENSITY,
  PHQUANTITY_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; // obsolete
  virtual long Height() const; // obsolete
  virtual long GetWidth() const;
  virtual long GetHeight() const;
  virtual bool IsOk() const;
  virtual bool IsColour() 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();

  int Naxis() const;
  long Naxes(const int n) const;
  long* Naxes() const;
  inline float Pixel(int) const;
  inline float Pixel(int, int) const;
  inline float Pixel(int, 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);
}

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


class FitsArrayStat: public FitsArray
{
public:
  FitsArrayStat(const FitsArray&, int=0);

  void InitStat();

  float Med() const { return GetMed(); }
  float Mad() const { return GetMad(); }
  float GetMed() const;
  float GetMad() const;
  float GetMin() const;
  float GetMax() const;
  static float QMed(long, float *, int);

private:

  bool statistics;
  int skip;
  float med, mad, xmin, xmax;

};

// ?? 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;


  if( data->ftable )
    return data->ftable[x];
  else if( data->dtable )
    return data->dtable[x];
  else if( data->stable )
    return data->stable[x];
  else if( data->ltable )
    return data->ltable[x];
  else if( data->itable )
    return data->itable[x];
  else if( data->btable )
    return data->btable[x];
  else if( data->otable )
    return data->otable[x] ? 1 : 0;
  else if( data->ctable ) {
    wxFAIL_MSG("FitsTableColumn::Cell: char to float unimplemented.");
  }

  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 float FitsTable::Cell(int row, int col) const
{
  FitsTableData *data = static_cast<FitsTableData *>(m_refData);
  wxASSERT(data);
  const std::vector<FitsTableColumn> columns(data->columns);

  wxASSERT(0 <= col && col < (int) columns.size());
  return columns[col].Cell(row);
}

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;
  FitsHdu FindHdu(const wxString&) const;

  int Type() const;
  wxString Type_str() const;
  bool HasImage() const;
  bool HasFind() 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 EmpiricalCDF
{
  long ncdf;
  float *xcdf, *ycdf;

 public:
  EmpiricalCDF(): ncdf(0),xcdf(0),ycdf(0) {}
  EmpiricalCDF(long, const float*);
  EmpiricalCDF(const EmpiricalCDF&);
  EmpiricalCDF& operator=(const EmpiricalCDF&);
  virtual ~EmpiricalCDF();

  bool IsOk() const;
  float GetQuantile(float) const;
  float GetInverse(float) const;
};


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

  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;

protected:

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

private:

  float wbin,xmin,xmax;

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


class FitsGeometry: public FitsArray
{

  FitsArray Shrink(int,const long *);
  FitsArray Zoom(int,const long *);

 public:
  FitsGeometry(const FitsArray&);

  FitsArray Shrink(int);
  FitsArray Zoom(int);
  FitsArray Scale(int,int);

  FitsArray GetSubArray(int,int,int,int);
  //virtual FitsImage PutSubImage(int,int,const FitsArray&);
  void SetSubArray(int,int,const FitsArray&);

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


class FitsTone
{
  bool initialised;
  float black, qblack, sense, rsense, refsense;
  EmpiricalCDF cdf_back;

 public:

  FitsTone();
  FitsTone(const FitsArray&);
  FitsTone(long, const float *);
  bool IsOk() const;
  void Setup(long, const float *);
  float Scale(float x) const { return (x - black) / sense; }
  float *Scale(long, const float *) const;
  void SetBlack(float);
  void SetQblack(float);
  void SetSense(float);
  void SetRsense(float);
  float GetSense() const { return sense; }
  float GetBlack() const { return black; }
  float GetQblack() const { return qblack; }
  float GetRsense() const { return rsense; }
  float GetBlackMin() const;
  float GetBlackMax() const;
  void Reset();

};


// -- Intensity transfer table (ITT)
class FitsItt
{
public:
  FitsItt(int =ITT_LINE);

  void SetItt(int);
  void SetItt(const wxString&);
  void SetAmp(float);
  void SetZero(float);
  int GetItt() const;
  wxString GetItt_str() const;
  float GetAmp() const;
  float GetZero() const;
  void Reset();
  float *Scale(long, const float *);

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

  bool IsLinear() const;

private:

  int itt;
  float amp, zero;

  float (FitsItt::*Func)(float) const;
  float Itt_linear(float) const;
  float Itt_sqrt(float) const;
  float Itt_square(float) const;
  float Itt_logis(float) const;

};


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

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

  void SetSaturation(float);
  void SetHue(float);
  void SetNiteVision(bool);
  void SetNiteThresh(float);
  void SetNiteWidth(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);
  void Reset();
  float GetWeight(int) const;
  float GetLevel(int) const;
  float GetTrans(int,int) const;
  int GetColors() const;
  int GetBands() const;
  void XYZ_Lab(long,float*,float*,float*,float*, float*, float*);
  void Lab_XYZ(long,float*,float*,float*,float*, float*, float*);
  void Lab_XYZ(float,float,float,float*, float*, float*);
  void NiteVision(long,float*,float*,float*,float*);
  void Instr_XYZ(long,size_t,const float **,float*,float*,float*);
  void TuneColors(long,float*,float*,float*);

  float Scotopic(float,float,float);

  float GetSaturation() const { return saturation; }
  float GetHue() const { return 57.29577951*hue; }
  bool GetNiteVision() const { return nitevision; }
  float GetNiteThresh() const { return nitethresh; }
  float GetNiteWidth() const { return nitewidth; }
  float NiteProfile(float) const;
  wxString GetColorspace() const;

  float InvGamma(float);

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

private:

  static const float Xn,Yn,Zn;

  wxString cspace;
  int ispace;
  float saturation, hue;
  bool nitevision;
  float nitethresh, nitewidth;

  void Lab_fun(long npix, float *I, float In, float *f);
  void Lab_invfun(long npix, float *f, float In, float *I);
  float Lab_invfun(float f);
};


// ---  FitsPalette

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

  void SetPalette(int);
  void SetPalette(const wxString&);
  void SetInverse(bool=false);
  int GetColors() const;
  int GetPalette() const;
  wxString GetPalette_str() const;
  bool GetInverse() const;
  void Reset();

  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(long,float *);

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

private:

  int pal;
  bool inverse;

  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&) const;
  void xy2ad(double,double,double&,double&) const;
  double GetScale() const;
  bool IsOk() const;

private:

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

  void gnomon(double,double,double&,double&) const;
  void ignomon(double,double,double&,double&) const;

};


// -- 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&) const;
  void RaSix(double,int&,int&,double&) const;
  void DecSix(double,char&,int&,int&,double&) const;

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

  bool HasWCS() const;

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

private:

  FitsProjection proj;
  coords_type type;
  bool haswcs;
  int digits;

};


// Photometric Systems

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

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

  wxString name;
  double leff, lwidth, flam;

};

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 flam, leff, lwidth, area, exptime, scale;
  static const double cspeed, hplanck, evolt, sqrtpi2, STspflux;

  double mag(double,double) const;
  double intensity(double) const;

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

  double GetIntensity(double) const;
  double GetMag(double) const;

};


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

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

  wxString Get_str(int,int) const;
  wxString Get_str(int,int,int) const;

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

  bool HasPhcal() const { return hascal; }

private:

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

  bool Init();
  void Init_phsystem(const wxString&);
  wxString ToString(double,int) const;
  double GetKeyDouble(const FitsHdu&, const wxString&) const;

};


class FitsTime
{
public:
  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(int, const long*);
  FitsImage(const 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 void SetSubImage(int,int,const FitsImage&);

  virtual FitsImage Shrink(int);
  virtual FitsImage Scale(int,int);

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

private:

  std::vector<FitsArray> arrays;

};



class FitsBitmap: public wxObject
{
public:
  FitsBitmap();
  FitsBitmap(const FitsBitmap&);
  FitsBitmap& operator = (const 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 FitsBaseDisplay
{
 protected:
  FitsBitmap bitmap;
  FitsTone tone;
  FitsItt itt;
  int shrink;
  long flags;

  inline unsigned char fclip255(float) const;
  inline float sRGBGamma(float) const;
  inline float AdobeGamma(float) const;

 public:
  FitsBaseDisplay();
  virtual ~FitsBaseDisplay() {};
  virtual void SetArray(const FitsArray&) = 0;
  virtual void SetImage(const FitsImage&) = 0;
  void SetShrink(int);
  void SetTone(const FitsTone&);
  void SetItt(const FitsItt& );
  virtual void SetColour(const FitsColor& ) {}
  virtual void SetPalette(const FitsPalette&) {}

  virtual void SetBlack(double);
  virtual void SetSense(double);
  virtual void SetQblack(double);
  virtual void SetRsense(double);
  virtual void SetAmp(double);
  virtual void SetZero(double);
  virtual void SetSaturation(double) {}
  virtual void SetHue(double) {}
  virtual void SetNiteThresh(double) {}
  virtual void SetNiteWidth(double) {}
  virtual void SetLevel(int,double) {}
  virtual void SetPalette(const wxString&) {}
  virtual void SetInversePalette(bool) {}
  virtual void SetNiteVision(bool) {}
  virtual void SetItt(const wxString&);
  virtual void SetOperations(long);

  virtual int GetWidth() const = 0;
  virtual int GetHeight() const = 0;
  virtual FitsBitmap GetBitmap() const { return bitmap; }
  virtual FitsImage GetImage() const = 0;
  virtual FitsImage GetShrinked() const = 0;
  virtual void Render() = 0;

};

inline unsigned char FitsBaseDisplay::fclip255(float f) const
{
  return std::min(std::max(int(f+0.5),0),255);
}

inline float FitsBaseDisplay::sRGBGamma(float r) const
{
  if( r < 0.0031308f )
    return 12.92f*r;
  else
    return 1.055f*powf(r,0.4166667f) - 0.055f;
}

inline float FitsBaseDisplay::AdobeGamma(float r) const
{
  const float q = 1.0/2.19921875;
  return powf(r,q);
}


class FitsGrayDisplay: public FitsBaseDisplay
{
  FitsArray original, scaled, zoomed, current;
  FitsPalette pal;

  float *sRGBGamma(long, const float *) const;
  float *AdobeGamma(long, const float *) const;

 public:
  FitsGrayDisplay(const FitsArray&);
  FitsGrayDisplay(const FitsImage&);
  void SetArray(const FitsArray&);
  void SetImage(const FitsImage&);
  void SetPalette(const FitsPalette&);
  void SetPalette(const wxString& p);
  void SetInversePalette(bool);
  void SetColourspace(const wxString&);
  int GetWidth() const;
  int GetHeight() const;
  FitsImage GetImage() const;
  FitsImage GetShrinked() const { return zoomed; }
  void Render();

};

class FitsColourDisplay: public FitsBaseDisplay
{
  std::vector<FitsArray> originals, scaled, zoomed;
  FitsColor colour;

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

 public:
  FitsColourDisplay(const std::vector<FitsArray>&);
  FitsColourDisplay(const FitsArray&);
  FitsColourDisplay(const FitsImage&);
  void SetArray(const FitsArray&);
  void SetImage(const FitsImage&);
  void SetColour(const FitsColor& );
  void SetSaturation(double);
  void SetHue(double);
  void SetNiteThresh(double);
  void SetNiteWidth(double);
  void SetLevel(int,double);
  void SetNiteVision(bool);
  void SetColourspace(const wxString&);
  int GetWidth() const;
  int GetHeight() const;
  FitsImage GetImage() const;
  FitsImage GetShrinked() const;
  void Render();

};

class FitsDisplay
{
public:
  FitsDisplay(const FitsArray&);
  FitsDisplay(const FitsImage&);
  FitsDisplay(const FitsDisplay&);
  FitsDisplay& operator = (const FitsDisplay&);
  virtual ~FitsDisplay();

  virtual void SetPalette(const FitsPalette&);
  virtual void SetTone(const FitsTone&);
  virtual void SetItt(const FitsItt& );
  virtual void SetColor(const FitsColor& );
  virtual void SetShrink(int);
  virtual void SetShrink(int,int);
  virtual void SetOperations(long);

  FitsTone GetTone() const;
  FitsItt GetItt() const;
  FitsPalette GetPalette() const;
  FitsColor GetColor() const;

  FitsBitmap GetImage() const;

private:

  FitsBaseDisplay *display;
  FitsTone tone;
  FitsItt itt;
  FitsPalette pal;
  FitsColor color;
};


// auxiliary  functions

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

wxString HumanFormat(double);


#endif
