/*

  xmunipack - detail panel

  Copyright © 1997-2013, 2019 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 "display.h"
#include <math.h>
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/graphics.h>

#define STRIP_SIZE 20





// // ---- MuniStrip  ---------------------------------------------

// BEGIN_EVENT_TABLE(MuniStrip, wxPanel)
//   EVT_PAINT(MuniStrip::OnPaint)
//   EVT_SIZE(MuniStrip::OnSize)
// //  EVT_IDLE(MuniStrip::OnIdle)
// END_EVENT_TABLE()

// MuniStrip::MuniStrip(wxWindow *w, const wxSize& s, const FitsItt& i,
// 		     const FitsPalette& l):
// wxPanel(w,wxID_ANY,wxDefaultPosition,s), itt(i), pal(l),
//   strip_height(STRIP_SIZE), big_tic(8),small_tic(5)
// {
//   SetBackgroundStyle(wxBG_STYLE_CUSTOM);
//   sf = wxFont(*wxNORMAL_FONT);
//   Create();
// }

// void MuniStrip::SetItt(const FitsItt& i)
// {
//   itt = i;
//   Create();
// }

// void MuniStrip::SetPalette(const FitsPalette& l)
// {
//   pal = l;
//   Create();
// }

// void MuniStrip::OnPaint(wxPaintEvent& event)
// {
//   if( strip.IsOk() ) {
//     wxAutoBufferedPaintDC dc(this);
//     dc.DrawBitmap(strip,0,0);
//   }
// }

// void MuniStrip::OnSize(wxSizeEvent& event)
// {
//   //  Create();

//   wxPaintEvent ev;
//   OnPaint(ev);
// }

// void MuniStrip::OnIdle(wxIdleEvent& event)
// {
//   //  Create();
//   //  wxPaintEvent ev;
//   //  OnPaint(ev);
// }

// void MuniStrip::Create()
// {
//   wxSize size = GetSize();

//   wxImage itemp(size.GetWidth(),size.GetHeight(),false);

//   strip = wxBitmap(itemp);
//   wxMemoryDC dc(strip);
//   //  dc.Clear();
//   wxGraphicsContext *gc = wxGraphicsContext::Create(dc);

//   if(gc) {

//     gc->SetBrush(*wxTRANSPARENT_BRUSH);
//     gc->SetFont(*wxSMALL_FONT,
// 		wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

//     // canvas rectangle
//     gc->SetPen(*wxWHITE_PEN);
//     gc->SetBrush(*wxWHITE_BRUSH);

//     // clear
//     gc->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
//     gc->DrawRectangle(0,0,size.GetWidth(),size.GetHeight());

//     // strip
//     double dw = double(size.GetWidth())/double(pal.GetColors());
//     int wt = int(dw + 1.5);
//     for(int l = 0; l < pal.GetColors(); l++) {
//       int ll = int(l*dw);
//       wxColour c(pal.R(l),pal.G(l),pal.B(l));
//       gc->SetPen(wxPen(c));
//       gc->SetBrush(wxBrush(c));
//       gc->DrawRectangle(ll,0,wt,strip_height);
//     }

//     // limits
//     double xmin = itt.InvScale(0);
//     double xmax = itt.InvScale(1/*pal.GetColors()*/);

//     // tics
//     double tic = (xmax - xmin)/13.0;

//     // rounding to only one place
//     double p = log10(tic);
//     double e = trunc(p); if( p < 0.0 ) e = e - 1.0;
//     double e10 = pow(10.0,e);
//     tic = trunc(pow(10.0,p-e))*e10;
//     //    wxLogDebug(_("%f %f %f %f %f %f"),xmin,xmax,p,e,e10,tic);


//     // start to rounded position
//     double x0 = trunc((xmin - tic)/e10)*e10;

//     gc->SetPen(*wxBLACK_PEN);
//     gc->SetBrush(*wxWHITE_BRUSH);
//     gc->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
//     gc->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

//     // tics
//     double x = x0;
//     while( x < xmax ) {
//       //      double l = itt.Fscale(x)*dw;
//       double ytic = fabs(fmod(x -(x0+tic),5.0*tic)) < 0.5 ? big_tic : small_tic;

//       int l = int(256*itt.Scale(x)*dw);
//       wxPoint2DDouble lines[] = { wxPoint2DDouble(l,strip_height),
// 				  wxPoint2DDouble(l,strip_height+ytic) };
//       gc->DrawLines(2,lines);

//       x = x + tic;
//     }

//     // labels
//     x = x0 + tic;
//     while( x < xmax ) {

//       wxString a;
//       double tw,th,u,v,xoff;

//       a.Printf("%g",x);
//       gc->GetTextExtent(a,&tw,&th,&u,&v);
//       xoff = tw/2.0;
//       int l = int(256*itt.Scale(x)*dw);
//       gc->DrawText(a,l-xoff,strip_height+big_tic);
//       x = x + 10.0*tic;
//     }

//     delete gc;
//   }
//   dc.SelectObjectAsSource(wxNullBitmap);
// }

// ---- MuniCanvasMini ---------------------------------------------

MuniCanvasMini::MuniCanvasMini(wxWindow *w, int z, int s):
  wxWindow(w,wxID_ANY),size(z),scale(s)
{
  SetBackgroundStyle(wxBG_STYLE_CUSTOM);

  Bind(wxEVT_PAINT,&MuniCanvasMini::OnPaint,this);
  Bind(wxEVT_UPDATE_UI,&MuniCanvasMini::OnUpdate,this);
  Bind(EVT_SLEW,&MuniCanvasMini::OnMouseMotion,this);
  Bind(EVT_FULLTUNE,&MuniCanvasMini::OnFullTune,this);
}

wxSize MuniCanvasMini::DoGetBestSize() const
{
  int s = size*scale;
  return wxSize(s,s);
}

void MuniCanvasMini::SetArray(const FitsArray& a)
{
  image = FitsImage(a);
}

void MuniCanvasMini::OnPaint(wxPaintEvent& event)
{
  // draw image bitmap
  wxPaintDC dc(this);

  if( canvas.IsOk() )
    dc.DrawBitmap(canvas,0,0,false);
  else {
    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();
  }

  // dimensions
  int w = size*scale;
  int h = w;

  // draw decorations
  dc.SetBrush(*wxTRANSPARENT_BRUSH);
  dc.SetPen(*wxBLACK_PEN);
  dc.DrawRectangle(0,0,w,h);
  dc.SetPen(*wxWHITE_PEN);
  dc.DrawRectangle(1,1,w-2,h-2);

  // draw cross box
  int t = w/2 - scale/2 - 2;
  dc.SetBrush(*wxTRANSPARENT_BRUSH);

  dc.SetPen(*wxWHITE_PEN);
  dc.DrawRectangle(t-2+scale,t-2,scale+4,scale+4);

  dc.SetPen(*wxBLACK_PEN);
  dc.DrawRectangle(t-1+scale,t-1,scale+2,scale+2);
}

void MuniCanvasMini::OnUpdate(wxUpdateUIEvent& event)
{
  event.SetUpdateInterval(40); // = 1/25 s
  if( tone.IsOk() ) {
    Render(crosshair);
    Refresh();
  }
}


void MuniCanvasMini::OnMouseMotion(MuniSlewEvent& event)
{
  crosshair = wxPoint(event.x,event.y);
}

void MuniCanvasMini::Render(const wxPoint& point)
{
  if( ! image.IsOk() ) return;

  int d = size / 2;
  int width = image.GetWidth();
  int height = image.GetHeight();

  int x = point.x - d;
  int y = point.y - d;
  int w = size;
  int h = size;
  int xoff = 0;
  int yoff = 0;
  if( x < 0 ) {
    xoff = abs(x);
    x = 0;
    w = wxMax(w - xoff,0);
  }
  if( x + size > width )
    w = wxMax(w - wxMax(x + size - width,0),0);

  if( y + size > height ) {
    yoff = y + size - height;
    h = wxMax(h - wxMax(y + size - height,0),0);
  }
  if( y < 0 )  {
    h = wxMax(h - abs(y),0);
    y = 0;
  }

  wxImage img(size,size);

  //  wxLogDebug("%d %d %d %d %d %d",x,y,xoff,yoff,w,h);
  if( x >= 0 && y >= 0 && w > 0 && h > 0) {
    FitsImage sub(image.GetSubImage(x,y,w,h));
    FitsDisplay display(sub);
    display.SetTone(tone);
    display.SetPalette(pal);
    display.SetItt(itt);
    display.SetColor(colour);
    FitsBitmap b(display.GetImage());
    img = wxImage(b.GetWidth(),b.GetHeight(),b.NewTopsyTurvyRGB());
    if( xoff > 0 || yoff > 0 || h < size || w < size ) {
      // edges
      wxImage i(size,size);
      i.Paste(img,xoff,yoff);
      img = i;
    }
  }

  img.Rescale(scale*size,scale*size);
  canvas = wxBitmap(img);
}

void MuniCanvasMini::OnFullTune(MuniFullTuneEvent& e)
{
  tone = e.tone;
  itt = e.itt;
  pal = e.pal;
  colour = e.colour;
}

// ---- MuniDetail


MuniDetail::MuniDetail(wxWindow *w, wxWindowID id, const wxPoint& pos,
		       const wxSize& size, MuniConfig *c):
  wxDialog(w,id,"Detail",pos,size,wxDEFAULT_DIALOG_STYLE), config(c)
{
  wxColour bc(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWFRAME));

  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);

  // zoom
  wxStaticBoxSizer *zsizer = new wxStaticBoxSizer(wxVERTICAL,this);

  zoom = new MuniCanvasMini(this,config->detail_zoom,config->detail_scale);
  zsizer->Add(zoom,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL).Border(wxTOP|wxBOTTOM));

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

  wxChoice *dtvalue = new wxChoice(this,wxID_ANY,wxDefaultPosition,
				   wxDefaultSize,sdetail);
  zsizer->Add(dtvalue,wxSizerFlags().Expand().Align(wxALIGN_BOTTOM));

  topsizer->Add(zsizer,wxSizerFlags().Expand().Border());


  // values
  wxStaticBoxSizer *vsizer = new wxStaticBoxSizer(wxVERTICAL,this," Value ");
  wxTextCtrl *value = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize,
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  value->SetBackgroundColour(bc);
  vsizer->Add(value,wxSizerFlags().Expand().Border());

  valchoice = new wxChoice(this,ID_VALTYPE,wxDefaultPosition,
			   wxDefaultSize,FitsValue::Label_str());
  vsizer->Add(valchoice,wxSizerFlags().Expand().Border());
  topsizer->Add(vsizer,wxSizerFlags().Expand().Border());

  // coordinates
  wxStaticBoxSizer *csizer = new wxStaticBoxSizer(wxVERTICAL,this," Coordinates ");

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

  wxStaticText *xlabel = new wxStaticText(this,wxID_ANY,wxEmptyString);
  cgrid->Add(xlabel,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *xcoordinate = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize,
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  xcoordinate->SetBackgroundColour(bc);
  cgrid->Add(xcoordinate,wxSizerFlags().Expand());

  wxStaticText *ylabel = new wxStaticText(this,wxID_ANY,wxEmptyString);
  cgrid->Add(ylabel,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *ycoordinate = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize,
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  ycoordinate->SetBackgroundColour(bc);
  cgrid->Add(ycoordinate,wxSizerFlags().Expand());
  csizer->Add(cgrid,wxSizerFlags(1).Expand().Border());

  coochoice = new wxChoice(this,ID_COOTYPE,wxDefaultPosition,wxDefaultSize,
			   FitsCoo::Label_str());
  csizer->Add(coochoice,wxSizerFlags().Expand().Border());

  topsizer->Add(csizer,wxSizerFlags().Expand().Border());
  SetSizerAndFit(topsizer);


  Connect(wxEVT_CLOSE_WINDOW,wxCloseEventHandler(MuniDetail::OnClose));
  //  Connect(xEVT_SLEW,xSlewEventHandler(MuniDetail::OnMouseMotion));
  Connect(ID_COOTYPE,wxEVT_COMMAND_CHOICE_SELECTED,
	  wxCommandEventHandler(MuniDetail::OnChoiceCoo));
  Connect(ID_VALTYPE,wxEVT_COMMAND_CHOICE_SELECTED,
	  wxCommandEventHandler(MuniDetail::OnChoiceVal));

  Connect(value->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateValue));
  Connect(xcoordinate->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateXCoo));
  Connect(ycoordinate->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateYCoo));
  Connect(xlabel->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateXlabel));
  Connect(ylabel->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateYlabel));

}

void MuniDetail::OnClose(wxCloseEvent& event)
{
  wxQueueEvent(GetParent(),event.Clone());
}


void MuniDetail::Assign(const FitsCoo& coo, const FitsValue& val)
{
  values = val;
  valchoice->SetStringSelection(values.Label_str(values.GetType()));

  coords = coo;
  coochoice->SetStringSelection(coords.Label_str(coords.GetType()));
}

void MuniDetail::OnChoiceVal(wxCommandEvent& event)
{
  /*
  for(int i = UNIT_COUNT; i <= UNIT_ERG; i++)
    if( event.GetString() == FitsValue::Label_str(i) ) {
      values.SetType((units_type)i);
      return;
    }
  */
}

void MuniDetail::OnChoiceCoo(wxCommandEvent& event)
{
  /*
  for(int i = COO_PIXEL; i <= COO_EQSIX; i++)
    if( event.GetString() == FitsCoo::Label_str(i) ) {
      coords.SetType((coords_type)i);
      return;
    }
  */
}


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

  // // text
  // values_str = values.Get_str(x,y);
  // coords.GetStr(x,y,xcoo_str,ycoo_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 MuniDetail::OnUpdateValue(wxUpdateUIEvent& event)
{
  event.SetText(values_str);
}

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

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

void MuniDetail::OnUpdateXlabel(wxUpdateUIEvent& event)
{
  event.SetText(coords.GetType() == COO_PIXEL ? L"X:" : L"α:");
  Layout();
}

void MuniDetail::OnUpdateYlabel(wxUpdateUIEvent& event)
{
  event.SetText(coords.GetType() == COO_PIXEL ? L"Y:" : L"δ:");
  Layout();
}
