/*

  xmunipack - display panel

  Copyright © 2012-4, 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/>.

*/

#include "xmunipack.h"
#include "display.h"
#include <wx/wx.h>
#include <wx/scrolwin.h>
#include <wx/statline.h>
#include <list>

using namespace std;


// -- MuniDisplayPanel


MuniDisplayPanel::MuniDisplayPanel(wxWindow *w, MuniConfig *c):
  wxScrolledWindow(w,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_THEME),
  config(c)
{
  SetScrollRate(-1,1);

  wxColour bc(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWFRAME));

  wxFont bf(*wxITALIC_FONT);

  const long DefaultStyle = wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY|wxBORDER_NONE;

  // detail
  zoom = new MuniCanvasMini(this,config->detail_zoom,config->detail_scale);

  //  wxArrayString sdetail;
  //  sdetail.Add(_("Image"));
  //  sdetail.Add(_("Profile 2D"));
  //  sdetail.Add(_("Profile 3D"));
  //  sdetail.Add(_("Contours"));

  // wxChoice *dtvalue = new wxChoice(this,wxID_ANY,wxDefaultPosition,
  // 				   wxDefaultSize,sdetail);
  // wxStaticBoxSizer *zsizer = new wxStaticBoxSizer(wxVERTICAL,this);
  // zsizer->Add(zoom,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL).Border(wxTOP|wxBOTTOM));
  // zsizer->Add(dtvalue,wxSizerFlags().Expand().Align(wxALIGN_BOTTOM));


  // value
  wxBoxSizer *vs = new wxBoxSizer(wxHORIZONTAL);
  value_label = new wxStaticText(this,wxID_ANY,L"Value (!,?)");
  vs->Add(value_label,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT));
  wxButton *inttype = new wxButton(this,wxID_ANY,L"»",wxDefaultPosition,
				   wxDefaultSize,wxBU_EXACTFIT);
  vs->AddStretchSpacer();
  vs->Add(inttype,wxSizerFlags().Align(wxALIGN_RIGHT));

  val = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, DefaultStyle);
  val->SetBackgroundColour(bc);
  val->SetToolTip("Any value. An instrumental intensity in counts (ADU)");

  wxBoxSizer *valsizer = new wxBoxSizer(wxVERTICAL);
  valsizer->Add(vs,wxSizerFlags().Expand());
  valsizer->Add(val,wxSizerFlags().Expand());


  // coordinates
  wxBoxSizer *cs = new wxBoxSizer(wxHORIZONTAL);
  cs->Add(new wxStaticText(this,wxID_ANY,"Coordinates:"),wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT));
  ctype = new wxButton(this,wxID_ANY,L"»",wxDefaultPosition,wxDefaultSize,wxBU_EXACTFIT);
  cs->AddStretchSpacer();
  cs->Add(ctype,wxSizerFlags().Align(wxALIGN_RIGHT));

  wxFlexGridSizer *xgrid = new wxFlexGridSizer(2);
  xgrid->AddGrowableCol(1);

  wxStaticText *la = new wxStaticText(this,wxID_ANY,L"α");
  la->SetFont(bf);
  xgrid->Add(la,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  acoo = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, DefaultStyle);
  acoo->SetBackgroundColour(bc);
  acoo->SetToolTip("Spherical coordinate - longitude");
  xgrid->Add(acoo,wxSizerFlags().Expand());

  wxStaticText *ld = new wxStaticText(this,wxID_ANY,L"δ");
  ld->SetFont(bf);
  xgrid->Add(ld,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  dcoo = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, DefaultStyle);
  dcoo->SetBackgroundColour(bc);
  dcoo->SetToolTip("Spherical coordinate - latitude");
  xgrid->Add(dcoo,wxSizerFlags().Expand());

  wxStaticText *lx = new wxStaticText(this,wxID_ANY,"x");
  lx->SetFont(bf);
  xgrid->Add(lx,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  xcoo = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, DefaultStyle);
  xcoo->SetBackgroundColour(bc);
  xcoo->SetToolTip("Rectangular horizontal coordinate in pixels. Origin at left bottom corner.");
  xgrid->Add(xcoo,wxSizerFlags().Expand());

  wxStaticText *ly = new wxStaticText(this,wxID_ANY,"y");
  ly->SetFont(bf);
  xgrid->Add(ly,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  ycoo = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, DefaultStyle);
  ycoo->SetBackgroundColour(bc);
  ycoo->SetToolTip("Rectangular vertical coordinate in pixels. Origin at left bottom corner.");
  xgrid->Add(ycoo,wxSizerFlags().Expand());

  wxBoxSizer *coosizer = new wxBoxSizer(wxVERTICAL);
  coosizer->Add(cs,wxSizerFlags().Expand());
  coosizer->Add(xgrid,wxSizerFlags().Expand());

  // info
  /*
  wxFlexGridSizer *igrid = new wxFlexGridSizer(2);
  igrid->AddGrowableCol(1);

  wxStaticText *lobject = new wxStaticText(this,wxID_ANY,"Object:");
  lobject->SetFont(bf);
  igrid->Add(lobject,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *iobj = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
					wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  iobj->SetBackgroundColour(bc);
  iobj->SetToolTip("Target object name");
  igrid->Add(iobj,wxSizerFlags().Expand());

  wxStaticText *ldate = new wxStaticText(this,wxID_ANY,"Date:");
  ldate->SetFont(bf);
  igrid->Add(ldate,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxRIGHT));
  wxTextCtrl *idate = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
				     wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  idate->SetBackgroundColour(bc);
  idate->SetToolTip("Date of exposure start in UT");
  igrid->Add(idate,wxSizerFlags().Expand());

  wxStaticText *ltime = new wxStaticText(this,wxID_ANY,"Time:");
  ltime->SetFont(bf);
  igrid->Add(ltime,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *itime = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  itime->SetBackgroundColour(bc);
  itime->SetToolTip("Time of exposure start in UT");
  igrid->Add(itime,wxSizerFlags().Expand());

  wxStaticText *lexp = new wxStaticText(this,wxID_ANY,"Etime:");
  lexp->SetFont(bf);
  igrid->Add(lexp,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *iexp = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  iexp->SetToolTip("Exposure time in seconds");
  iexp->SetBackgroundColour(bc);
  igrid->Add(iexp,wxSizerFlags().Expand());

  wxStaticText *lband = new wxStaticText(this,wxID_ANY,"Band:");
  lband->SetFont(bf);
  igrid->Add(lband,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *iband = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  iband->SetBackgroundColour(bc);
  iband->SetToolTip("Spectral band description");
  igrid->Add(iband,wxSizerFlags().Expand());

  wxStaticText *lsize = new wxStaticText(this,wxID_ANY,"Size:");
  lsize->SetFont(bf);
  igrid->Add(lsize,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *isize = new wxTextCtrl(this, wxID_ANY, wxEmptyString,wxDefaultPosition,
			wxDefaultSize, wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  isize->SetBackgroundColour(bc);

  igrid->Add(isize,wxSizerFlags().Expand());
  isize->SetToolTip("Dimensions (width × height × ..) of image in pixels");
  */

  phisto = new MuniPlotHisto(this,false);


  // top sizer
  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
  topsizer->Add(zoom,wxSizerFlags().Center().Border());
  topsizer->Add(new wxStaticLine(this,wxID_ANY),wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT));
  topsizer->Add(valsizer,wxSizerFlags().Expand().Border());
  topsizer->Add(new wxStaticLine(this,wxID_ANY),wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT));
  topsizer->Add(coosizer,wxSizerFlags().Expand().Border());
  /*
  topsizer->Add(new wxStaticLine(this,wxID_ANY),wxSizerFlags().Expand().Border());
  topsizer->Add(igrid,wxSizerFlags().Expand());
  */
  topsizer->Add(new wxStaticLine(this,wxID_ANY),wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT));
  topsizer->Add(phisto,wxSizerFlags().Expand().Border());
  SetSizer(topsizer);

  // bindings
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniDisplayPanel::OnPopVal,this,inttype->GetId());
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniDisplayPanel::OnPopCoo,this,ctype->GetId());
  Bind(xEVT_SLEW,&MuniDisplayPanel::OnMouseMotion,this);
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateValue,this,val->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateXCoo,this,xcoo->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateYCoo,this,ycoo->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateACoo,this,acoo->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateDCoo,this,dcoo->GetId());
  /*
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateObject,this,iobj->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateDate,this,idate->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateTime,this,itime->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateExp,this,iexp->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateBand,this,iband->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateSize,this,isize->GetId());
  */
}

void MuniDisplayPanel::SetHdu(const FitsArray& h, const FitsHisto& hist)
{
  hdu = h;
  value = FitsValue(hdu,config->phsystemfile, config->fits_key_area,
		    config->fits_key_gain, config->fits_key_exptime,
		    config->fits_key_filter);
  coords = FitsCoo(hdu);

  coords.SetType(config->display_coo);
  value.SetType(config->display_val);

  ctype->Enable(coords.HasWCS());
  SetValueLabel(value.GetType());

  tobject = hdu.GetKey(config->fits_key_object);
  tband = hdu.GetKey(config->fits_key_filter);
  texp = hdu.GetKey(config->fits_key_exptime);

  wxString dateobs;
  dateobs = hdu.GetKey(config->fits_key_dateobs);
  FitsTime ft(dateobs);
  tdate = ft.Date();
  ttime = ft.Time();

  // dimensions
  wxString a;
  a.Printf("%d",(int) hdu.Naxes(0));
  for(int k = 1; k < hdu.Naxis(); k++) {
    wxString b;
    b.Printf(L" × %d",(int) hdu.Naxes(k));
    a += b;
  }
  tsize = a;

  // histogram
  phisto->SetHisto(hist);
}

void MuniDisplayPanel::OnPopVal(wxCommandEvent& event)
{
  wxArrayString l = FitsValue::Label_str();

  wxString label = FitsValue::Label_str(value.GetType());

  wxMenu popup;
  for(size_t i = 0; i < l.GetCount(); i++) {
    popup.AppendRadioItem(wxID_ANY,l[i]);
    long id = popup.FindItem(l[i]);
    Bind(wxEVT_COMMAND_MENU_SELECTED,&MuniDisplayPanel::OnValUnit,this,id);
    ids.push_back(id);
    labels.push_back(l[i]);
    if( l[i] == label )
      popup.Check(id,true);
    if( value.HasCal() ) {
      if( i == 0 )
	popup.Enable(id,false);
    }
    else {
      if( i > 1 )
	popup.Enable(id,false);
    }
  }

  PopupMenu(&popup);

  for(list<long>::const_iterator i = ids.begin(); i != ids.end(); ++i)
    Unbind(wxEVT_COMMAND_MENU_SELECTED,&MuniDisplayPanel::OnCooUnit,this,*i);
  ids.clear();
  labels.clear();
}

void MuniDisplayPanel::OnPopCoo(wxCommandEvent& event)
{
  wxArrayString l = FitsCoo::Label_str();

  wxString label = FitsCoo::Label_str(coords.GetType());

  wxMenu popup;
  for(size_t i = 0; i < l.GetCount(); i++) {
    popup.AppendRadioItem(wxID_ANY,l[i]);
    long id = popup.FindItem(l[i]);
    Bind(wxEVT_COMMAND_MENU_SELECTED,&MuniDisplayPanel::OnCooUnit,this,id);
    ids.push_back(id);
    labels.push_back(l[i]);
    if( l[i] == label )
      popup.Check(id,true);
  }

  PopupMenu(&popup);

  for(list<long>::const_iterator i = ids.begin(); i != ids.end(); ++i)
    Unbind(wxEVT_COMMAND_MENU_SELECTED,&MuniDisplayPanel::OnCooUnit,this,*i);
  ids.clear();
  labels.clear();

}

void MuniDisplayPanel::OnCooUnit(wxCommandEvent& event)
{
  list<wxString>::const_iterator l = labels.begin();
  for(list<long>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
    if( *i == event.GetId() )
      coords.SetType(*l);
    ++l;
  }
  config->display_coo = coords.GetType();
}

void MuniDisplayPanel::OnValUnit(wxCommandEvent& event)
{
  list<wxString>::const_iterator l = labels.begin();
  for(list<long>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
    if( *i == event.GetId() )
      value.SetType(*l);
    ++l;
  }
  config->display_val = value.GetType();

  SetValueLabel(value.GetType());
}

void MuniDisplayPanel::SetValueLabel(int type)
{
  if( type == UNIT_COUNT )
    value_label->SetLabel("Counts:");
  if( type == UNIT_MAG )
    value_label->SetLabel(value.HasCal() ? "Magnitudes:" : "Instr. Magnitudes:");
  if( type == UNIT_PHOTON )
    value_label->SetLabel("Photons:");
  if( type == UNIT_FLUX )
    value_label->SetLabel("Flux:");
  if( type == UNIT_FNU )
    value_label->SetLabel(L"Fν:");
  if( type == UNIT_FLAM )
    value_label->SetLabel(L"Fλ:");
  if( type == UNIT_ABMAG )
    value_label->SetLabel(L"ABmag:");
  if( type == UNIT_STMAG )
    value_label->SetLabel(L"STmag:");

  value_label->Refresh();
}

void MuniDisplayPanel::OnMouseMotion(MuniSlewEvent& event)
{
  int x = event.i_x;
  int y = event.i_y;
  // ZOOOOOM ??????

  val_str = value.Get_str(x,y);
  coords.GetPix(x,y,xcoo_str,ycoo_str);
  coords.GetCoo(x,y,fxcoo_str,fycoo_str);

  // image
  FitsBitmap picture(event.picture);
  if( !picture.IsOk()) return;
  int z = config->detail_scale;
  wxImage i(picture.GetWidth(),picture.GetHeight(),picture.NewTopsyTurvyRGB());
  if( i.IsOk() ) {
    i.Rescale(z*i.GetWidth(),z*i.GetHeight());
    zoom->SetImage(wxBitmap(i));
  }
}

void MuniDisplayPanel::OnUpdateValue(wxUpdateUIEvent& event)
{
  event.SetText(val_str);
}

void MuniDisplayPanel::OnUpdateXCoo(wxUpdateUIEvent& event)
{
  event.SetText(xcoo_str);
}

void MuniDisplayPanel::OnUpdateYCoo(wxUpdateUIEvent& event)
{
  event.SetText(ycoo_str);
}

void MuniDisplayPanel::OnUpdateACoo(wxUpdateUIEvent& event)
{
  event.SetText(fxcoo_str);
}

void MuniDisplayPanel::OnUpdateDCoo(wxUpdateUIEvent& event)
{
  event.SetText(fycoo_str);
}

/*
void MuniDisplayPanel::OnUpdateObject(wxUpdateUIEvent& event)
{
  event.SetText(tobject);
}

void MuniDisplayPanel::OnUpdateDate(wxUpdateUIEvent& event)
{
  event.SetText(tdate);
}

void MuniDisplayPanel::OnUpdateTime(wxUpdateUIEvent& event)
{
  event.SetText(ttime);
}

void MuniDisplayPanel::OnUpdateExp(wxUpdateUIEvent& event)
{
  event.SetText(texp);
}

void MuniDisplayPanel::OnUpdateBand(wxUpdateUIEvent& event)
{
  event.SetText(tband);
}

void MuniDisplayPanel::OnUpdateSize(wxUpdateUIEvent& event)
{
  event.SetText(tsize);
}
*/
