/*

  xmunipack - fits image

  Copyright © 2009-2011, 2018-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/>.

*/


#include "fits.h"
#include <cfloat>
#include <wx/wx.h>
#include <algorithm>

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



using namespace std;


// ------------   FitsImage

FitsImage::FitsImage() {}

FitsImage::FitsImage(int naxis, const long *naxes)
{
  if( !(naxis == 2 || naxis == 3) ) {
    wxFAIL_MSG("FitsImage::FitsImage implemented for 2D and 3D images only.");
    return;
  }

  int n = naxes[0]*naxes[1];

  if( naxis == 2 ) {
    long *ns = new long[2];
    float *d = new float[n];
    copy(naxes,naxes+2,ns);
    arrays.push_back(FitsArray(FitsHdu(),naxis,ns,d));
  }
  else if( naxis == 3 ) {
    for(int k = 0; k < 3; k++) {
      long *ns = new long[2];
      float *d = new float[n];
      copy(naxes,naxes+2,ns);
      arrays.push_back(FitsArray(FitsHdu(),2,ns,d));
    }
  }
}


FitsImage::FitsImage(const FitsImage& image)
{
  //  wxASSERT(image.size() > 0);
  if( this != &image )
    arrays = image.arrays;
}


FitsImage::FitsImage(const vector<FitsArray>& a)
{
  wxASSERT(a.size() > 0);
  arrays = a;
}

FitsImage::FitsImage(const FitsArray& array)
{
  if( array.Naxis() == 3 )
    for(int l = 0; l < array.Naxes(2); l++)
      arrays.push_back(array.Plane(l));

  else
    arrays.push_back(array);
}

FitsImage::FitsImage(const FitsArray& r,const FitsArray& g,const FitsArray& b)
{
  arrays.push_back(r);
  arrays.push_back(g);
  arrays.push_back(b);
}

FitsImage::FitsImage(const FitsFile& fits, int sel)
{
  wxASSERT(fits.IsOk());

  if( sel == -1 && fits.Type() == FITS_COLOUR ) {
    // colour

    for(int k = 1; k <= 3; k++) {
      FitsHdu hdu = fits.Hdu(k);
      arrays.push_back(FitsArray(hdu));
    }
  }
  else if( 0<=sel&&sel<int(fits.HduCount()) && fits.Hdu(sel).Type()==HDU_IMAGE){
    // gray

    wxASSERT( fits.Hdu(sel).Type() == HDU_IMAGE );
    arrays.push_back(fits.Hdu(sel));
  }
  else
    wxFAIL_MSG("FitsImage::FitsImage"+fits.GetName());
}

FitsImage::~FitsImage()
{
  arrays.clear();
}

bool FitsImage::IsOk() const
{
  return arrays.size() > 0;
}

bool FitsImage::IsColour() const
{
  return arrays.size() > 1;
}

const std::vector<FitsArray> FitsImage::GetArrays() const
{
  return arrays;
}


int  FitsImage::GetWidth() const
{
  if( arrays.empty() )
    return 0;
  else
    return arrays[0].Width();
}

int  FitsImage::GetHeight() const
{
  if( arrays.empty() )
    return 0;
  else
    return arrays[0].Height();
}

size_t FitsImage::GetCount() const
{
  return arrays.size();
}

long FitsImage::Npixels() const
{
  return GetWidth()*GetHeight()*GetCount();
}

FitsArray FitsImage::Item(size_t k) const
{
  wxASSERT(0 <= k && k < arrays.size());
  return arrays[k];
}

FitsImage FitsImage::Shrink(int s)
{
  vector<FitsArray> temp;

  for(vector<FitsArray>::const_iterator k=arrays.begin(); k!=arrays.end();++k){
    FitsGeometry g(*k);
    temp.push_back(g.Shrink(s));
  }
  return FitsImage(temp);
}

FitsImage FitsImage::Scale(int width, int height)
{
  vector<FitsArray> temp;

  int s = max(GetWidth()/width,GetHeight()/height);
  return Shrink(s);
}

FitsImage FitsImage::GetSubImage(int x, int y, int w, int h)
{
  wxASSERT(IsOk());
  vector<FitsArray> temp;

  for(vector<FitsArray>::const_iterator k=arrays.begin(); k!=arrays.end();++k){
    wxASSERT(k->Naxis() == 2);
    FitsGeometry g(*k);
    temp.push_back(g.GetSubArray(x,y,w,h));
  }

  return FitsImage(temp);
}


FitsImage FitsImage::Thumb(int wsize, int hsize)
{
  return Scale(wsize,hsize);
}

void FitsImage::SetSubImage(int x, int y, const FitsImage& srcimage)
{
  wxASSERT(srcimage.GetCount() == arrays.size());

  vector<FitsArray> src(srcimage.GetArrays());
  for(size_t k = 0; k < arrays.size(); k++) {
    FitsGeometry g(arrays[k]);
    g.SetSubArray(x,y,src[k]);
  }
}
