/*

  xmunipack - display panel

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

*/

#include "xmunipack.h"
#include "display.h"
#include <wx/wx.h>
#include <wx/scrolwin.h>
#include <wx/statline.h>
#include <wx/tokenzr.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), hist_init(false), mini_init(false)
{
  // 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,"Counts:");
  value_label->SetLabelMarkup(L"<small>Counts:</small>");
  vs->Add(value_label,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT));
  valtype = new wxButton(this,wxID_ANY,L"⚙",wxDefaultPosition,
			 wxDefaultSize,wxBU_EXACTFIT);
  vs->AddStretchSpacer();
  vs->Add(valtype,wxSizerFlags().Align(wxALIGN_RIGHT));

  xval = new wxStaticText(this, wxID_ANY,"X = ");
  xval->SetToolTip("Any value. An instrumental intensity in counts (ADU)");
  yval = new wxStaticText(this, wxID_ANY,"Y = ");
  yval->SetToolTip("Any value. An instrumental intensity in counts (ADU)");
  zval = new wxStaticText(this, wxID_ANY,"Z = ");
  zval->SetToolTip("Any value. An instrumental intensity in counts (ADU)");

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


  // coordinates
  wxBoxSizer *cs = new wxBoxSizer(wxHORIZONTAL);
  wxStaticText *coo_label = new wxStaticText(this,wxID_ANY,"Coordinates:");
  coo_label->SetLabelMarkup("<small>Coordinates:</small>");
  cs->Add(coo_label,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));

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

  label_alpha = new wxStaticText(this,wxID_ANY,L"α =  ");
  label_alpha->SetLabelMarkup(L"<i>α = </i>");
  label_alpha->Show(false);
  xgrid->Add(label_alpha,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  acoo = new wxStaticText(this, wxID_ANY, "");
  acoo->SetToolTip("Spherical coordinate - longitude");
  acoo->Show(false);
  xgrid->Add(acoo,wxSizerFlags().Expand());

  label_delta = new wxStaticText(this,wxID_ANY,L"δ");
  label_delta->SetLabelMarkup(L"<i>δ = </i>");
  label_delta->Show(false);
  xgrid->Add(label_delta,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  dcoo = new wxStaticText(this, wxID_ANY, "");
  dcoo->Show(false);
  dcoo->SetToolTip("Spherical coordinate - latitude");
  xgrid->Add(dcoo,wxSizerFlags().Expand());

  label_x = new wxStaticText(this,wxID_ANY,"x");
  label_x->SetLabelMarkup(L"<i>x = </i>");
  xgrid->Add(label_x,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  xcoo = new wxStaticText(this, wxID_ANY, "");
  xcoo->SetToolTip("Rectangular horizontal coordinate in pixels. Origin at left bottom corner.");
  xgrid->Add(xcoo,wxSizerFlags().Expand());

  label_y = new wxStaticText(this,wxID_ANY,"y");
  label_y->SetLabelMarkup(L"<i>y = </i>");
  xgrid->Add(label_y,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  ycoo = new wxStaticText(this, wxID_ANY, "");
  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 HTML window
  html = new wxHtmlWindow(this);

  // histogram
  phisto = new MuniPlotHisto(this);

  // top sizer
  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
  topsizer->Add(zoom,wxSizerFlags().Center());
  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(html,wxSizerFlags(1).Expand());
  topsizer->Add(phisto,wxSizerFlags().Expand().Border(wxBOTTOM));
  SetSizer(topsizer);

  // bindings
  Bind(wxEVT_IDLE,&MuniDisplayPanel::OnIdle,this);
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniDisplayPanel::OnPopVal,this,
       valtype->GetId());
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniDisplayPanel::OnPopCoo,this,
       ctype->GetId());
  Bind(EVT_SLEW,&MuniDisplayPanel::OnMouseMotion,this);
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateXValue,this,xval->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateYValue,this,yval->GetId());
  Bind(wxEVT_UPDATE_UI,&MuniDisplayPanel::OnUpdateZValue,this,zval->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());
}

void MuniDisplayPanel::SetArray(const FitsArray& a)
{
  wxASSERT(a.IsOk());

  array = a;
  value = FitsValue(array,config->phsystemfile, config->fits_key_area,
		    config->fits_key_exptime, config->fits_key_filter);
  coords = FitsCoo(array);

  // update values
  if( array.IsColour() ) {
    xval->Show(true);
    xval->SetForegroundColour(wxColour(92,0,0));
    yval->Show(true);
    yval->SetForegroundColour(wxColour(0,92,0));
    zval->Show(true);
    zval->SetForegroundColour(wxColour(0,0,92));
  }
  else {
    xval->Show(false);
    yval->SetForegroundColour(wxNullColour);
    zval->Show(false);
  }

  coords.SetType(coords.HasWCS() ? config->display_coo : COO_PIXEL);
  //  value.SetType(value.HasCal() ? config->display_val : UNIT_COUNT);

  //  valtype->Enable(value.HasCal());
  ctype->Enable(coords.HasWCS());
  SetValueLabel(value.GetType());
  UpdateCooPanel(coords.GetType());

  wxString tobject(array.GetKey(config->fits_key_object));
  wxString tband(array.GetKey(config->fits_key_filter));
  wxString texp(array.GetKey(config->fits_key_exptime));

  // to format of exposure time
  double e;
  if( texp.ToDouble(&e) ) {
    wxString line;
    if( e > 0.1 )
      line.Printf("%.1f s",e);
    else
      line.Printf("1/%.0f s",1.0/e);
    texp = line;
  }

  // date & time
  FitsTime ft(array.GetKey(config->fits_key_dateobs));
  wxString tdate = ft.Date();
  wxString ttime = ft.Time();

  // update info
  wxColour bkg(wxSystemSettings::GetColour(wxSYS_COLOUR_BACKGROUND));
  wxString wbody("<body bgcolor=\""+bkg.GetAsString()+"\"><small>");
  wxString wobject("<p align=\"center\"><b>"+tobject+"</b></p>");
  wxString wpar("<p align=\"center\">");
  wxString wdate, wtime, wband, wexp;
  if( tdate != "" ) wdate = tdate + "<br>";
  if( ttime != "" ) wtime = ttime + "<br>";
  if( tband != "" ) wband = tband + " filter<br>";
  if( texp != "" )  wexp = texp+"<br>";
  wxString tail("</p></small></body>");

  bool code = html->SetPage(wbody+wobject+wpar+wdate+wtime+wband+wexp+tail);
  wxASSERT(code);

  Refresh();

  // inform discanvas
  wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED,ID_COOTYPE);
  ev.SetInt(coords.GetType());
  wxQueueEvent(GetParent(),ev.Clone());

  wxCommandEvent kev(wxEVT_COMMAND_MENU_SELECTED,ID_VALTYPE);
  kev.SetInt(value.GetType());
  wxQueueEvent(GetParent(),kev.Clone());
}

void MuniDisplayPanel::OnIdle(wxIdleEvent& event)
{
  if( ! hist_init && array.IsOk() ) {
    phisto->SetArray(array);
    hist_init = true;
  }

  if( ! mini_init && array.IsOk() ) {
    zoom->SetArray(array);
    mini_init = true;
  }
}


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

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

  wxMenu popup;
  for(size_t i = 1; i < l.GetCount(); i++) { // counts are skiped
    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( false /*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;
  }

  UpdateCooPanel(coords.GetType());
  config->display_coo = coords.GetType();

  // send state change to discanvas
  wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED,ID_COOTYPE);
  ev.SetInt(coords.GetType());
  wxQueueEvent(GetParent(),ev.Clone());
}

void MuniDisplayPanel::UpdateCooPanel(int type)
{
  if( type == COO_PIXEL ) {
    label_x->Show(true); xcoo->Show(true);
    label_y->Show(true); ycoo->Show(true);
    label_alpha->Show(false); acoo->Show(false);
    label_delta->Show(false); dcoo->Show(false);
  }
  else {
    label_x->Show(false); xcoo->Show(false);
    label_y->Show(false); ycoo->Show(false);
    label_alpha->Show(true); acoo->Show(true);
    label_delta->Show(true); dcoo->Show(true);
  }
  xgrid->Layout();
  Layout();
  Refresh();

}


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;
  }

  SetValueLabel(value.GetType());
  config->display_val = value.GetType();

  // send state change to discanvas
  wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED,ID_VALTYPE);
  ev.SetInt(value.GetType());
  wxQueueEvent(GetParent(),ev.Clone());
}

void MuniDisplayPanel::SetValueLabel(int type)
{
  if( type == UNIT_COUNT )
    value_label->SetLabelMarkup("<small>Counts:</small>");
  if( type == UNIT_MAG )
    value_label->SetLabelMarkup("<small>Magnitudes:</small>");
  if( type == UNIT_PHOTON )
    value_label->SetLabelMarkup("<small>Photons:</small>");
  if( type == UNIT_INTENSITY )
    value_label->SetLabelMarkup(L"<small>Intensity:</small>");
  //  if( type == UNIT_STMAG )
  //    value_label->SetLabelMarkup("<small>ST mag:</small>");
  value_label->Refresh();
}

void MuniDisplayPanel::OnMouseMotion(MuniSlewEvent& event)
{
  wxASSERT(array.IsOk());

  int x = event.x;
  int y = event.y;

  if( array.IsColour() ) {
    xval_str = "X = " + value.Get_str(x,y,2);
    yval_str = "Y = " + value.Get_str(x,y,1);
    zval_str = "Z = " + value.Get_str(x,y,0);
  }
  else
    yval_str = value.Get_str(x,y);

  coords.GetPix(x,y,xcoo_str,ycoo_str);
  coords.GetCoo(x,y,acoo_str,dcoo_str);

  wxQueueEvent(zoom,event.Clone());
}

void MuniDisplayPanel::OnUpdateXValue(wxUpdateUIEvent& event)
{
  event.SetText(xval_str);
}
void MuniDisplayPanel::OnUpdateYValue(wxUpdateUIEvent& event)
{
  event.SetText(yval_str);
}
void MuniDisplayPanel::OnUpdateZValue(wxUpdateUIEvent& event)
{
  event.SetText(zval_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(acoo_str);
}

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

void MuniDisplayPanel::OnFullTune(MuniFullTuneEvent& e)
{
  wxQueueEvent(zoom,e.Clone());
}
