/*

  xmunipack - tune panel

  Copyright © 2009-2013, 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 "tune.h"
#include <wx/wx.h>
#include <wx/event.h>
#include <vector>
#include <typeinfo>

using namespace std;

#define xDEFAULT_FRAME_STYLE ( wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT ) & ~( wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX )


// ---- MuniTune

MuniTune::MuniTune(wxWindow *w, wxWindowID id, const wxPoint& pos,
		   const wxSize& size, int icon_size, const FitsArray& array,
		   const FitsTone& t, const FitsItt& i, const FitsPalette& p):
  wxFrame(w,id,"Adjustments",pos,size,xDEFAULT_FRAME_STYLE),
  tone(t),itt(i),pal(p),shrinking(true),colouring(false),
  toneabs(false),render(true), ittline(true),nitevision(false), etune(0)
{
  //  wxLogDebug("MuniTune::MuniTune gray");

  // tune display
  display = new FitsGrayDisplay(array);

  wxNotebook *book = new wxNotebook(this,wxID_ANY);

  // prescale
  book->AddPage(CreateScaleTab(book),"Scale");

  // ITT
  book->AddPage(CreateIttTab(book),"Tone");

  // Palette
  book->AddPage(CreateLutTab(book),"Palette");

  // preview
  int width,height;
  InitMini(array,icon_size,&width,&height);
  mini = new MuniMiniDisplay(this,width,height);

  // arrange all
  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
  topsizer->Add(mini,wxSizerFlags().Border().Center());
  topsizer->Add(book,wxSizerFlags().Border().Expand());
  topsizer->Add(new wxButton(this, ID_RESET, "Reset All"),
		wxSizerFlags().Center().Border());
  SetSizerAndFit(topsizer);

  // events
  Bind(wxEVT_CLOSE_WINDOW,&MuniTune::OnClose,this);
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniTune::OnReset,this,ID_RESET);
  Bind(EVT_TUNE,&MuniTune::OnTuneFine,this);
  Bind(wxEVT_IDLE,&MuniTune::OnIdle,this);
  Bind(wxEVT_SCROLL_THUMBTRACK,&MuniTune::OnScroll,this);
}

MuniTune::MuniTune(wxWindow *w, wxWindowID id, const wxPoint& pos,
		   const wxSize& size, int icon_size, const FitsArray& array,
		   const FitsTone& t, const FitsItt& i, const FitsColor& cc):
  wxFrame(w,id,"Adjustments",pos,size,xDEFAULT_FRAME_STYLE),
  tone(t),itt(i),color(cc),shrinking(true),colouring(true),
  toneabs(false),render(true), ittline(true), nitevision(false), lutus(0),
  etune(0)
{
  // tune display
  display = new FitsColourDisplay(array);

  wxNotebook *book = new wxNotebook(this,wxID_ANY);

  // prescale
  book->AddPage(CreateScaleTab(book),"Scale");

  // ITT
  book->AddPage(CreateIttTab(book),"Tone");

  // Color
  book->AddPage(CreateColourTab(book),"Colours");

  // Night vision
  book->AddPage(CreateNiteTab(book),"Nite");

  // preview
  int width,height;
  InitMini(array,icon_size,&width,&height);
  mini = new MuniMiniDisplay(this,width,height);

  // arrange all
  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
  topsizer->Add(mini,wxSizerFlags().Border().Center());
  topsizer->Add(book,wxSizerFlags().Border().Expand());
  topsizer->Add(new wxButton(this, ID_RESET, "Reset All"),
		wxSizerFlags().Center().Border());
  SetSizerAndFit(topsizer);

  Bind(wxEVT_CLOSE_WINDOW,&MuniTune::OnClose,this);
  Bind(wxEVT_COMMAND_BUTTON_CLICKED,&MuniTune::OnReset,this,ID_RESET);
  Bind(EVT_TUNE,&MuniTune::OnTuneFine,this);
  Bind(wxEVT_IDLE,&MuniTune::OnIdle,this);
  Bind(wxEVT_SCROLL_THUMBTRACK,&MuniTune::OnScroll,this);
}

MuniTune::~MuniTune()
{
  //  wxLogDebug("MuniTune::~MuniTune()");
  delete display;
}


void MuniTune::InitMini(const FitsArray& a,int icon_size,int *width,int *height)
{
  wxASSERT(display && shrinking);

  if( a.Width() > a.Height() ) {
    int s = wxMax(a.Width() / (2*icon_size),1);
    *width = a.Width() / s;
    *height = a.Height() / s;
    display->SetShrink(s);
  }
  else {
    int s = wxMax(a.Height() / (2*icon_size),1);
    *width = a.Height() / s;
    *height = a.Height() / s;
    display->SetShrink(s);
  }
  //wxLogDebug("preview: %d %d %d",*width,*height,icon_size);
}


void MuniTune::OnClose(wxCloseEvent& event)
{
  delete etune;
  etune = 0;
  wxQueueEvent(GetParent(),event.Clone());
}

void MuniTune::OnIdle(wxIdleEvent& event)
{
  if( render ) {
    // The very first Idle event is spend on shrinking
    if( shrinking ) {
      shrinking = false;
      display->Render();
      FitsImage img(display->GetShrinked());
      delete display;

      if( colouring ) {
	display = new FitsColourDisplay(img);
	display->SetTone(tone);
	display->SetItt(itt);
	display->SetColour(color);
      }
      else {
	display = new FitsGrayDisplay(img);
	display->SetTone(tone);
	display->SetItt(itt);
	display->SetPalette(pal);
      }
      event.RequestMore();
      return;
    }

    int flags = colouring ?
      OP_TUNE_SCALE | OP_TUNE_ITT | OP_TUNE_COLOUR | OP_TUNE_RGB :
      OP_TUNE_SCALE | OP_TUNE_ITT | OP_TUNE_PAL | OP_TUNE_RGB;

    // all other Idle events works on the shrinked canvas
    //    display->SetOperations(OP_TUNE_SCALE | OP_TUNE_ITT | OP_TUNE_PAL);
    display->SetOperations(flags);
    display->Render();
    FitsBitmap picture(display->GetBitmap());
    wxImage img(picture.GetWidth(),picture.GetHeight(),
		picture.NewTopsyTurvyRGB());
    mini->SetImage(img);
    render = false;
  }

  if( etune ) {
    wxQueueEvent(GetParent(),etune->Clone());
    delete etune;
    etune = 0;
  }
}

MuniTuneAdjuster *MuniTune::CreateToneBlack(wxWindow *tpanel)
{
  float r, rmin, rmax, inc;
  int id,digits;
  //  if( tone.GetKind() == TONE_KIND_ABS ) {
  if( toneabs ) {
    r = tone.GetBlack();
    rmin = tone.GetBlackMin();
    rmax = tone.GetBlackMax();
    inc = (rmax - rmin) / 1000;
    digits = 3;
    id = ID_TONE_BLACK;
  }
  else { //if( itt.GetKind() == TONE_KIND_REL ) {
    r = tone.GetQblack();
    rmin = 0.0;
    rmax = 1.0;
    inc = 0.01;
    digits = 2;
    id = ID_TONE_QBLACK;
  }

  MuniTuneAdjuster *b = new MuniTuneAdjuster(tpanel,id,r,rmin,rmax,inc,
					     digits,L"☾",L"☼");
  b->SetToolTip("Black point is a value corresponding to black on display.");
  return b;
}

MuniTuneLogjuster *MuniTune::CreateToneSense(wxWindow *tpanel)
{
  double r, rmin, rmax, inc;
  int id,digits;
  //  if( tone.GetKind() == TONE_KIND_ABS ) {
  if( toneabs ) {
    r = tone.GetSense();
    rmin = 1e-2*r;
    rmax = 1e2*r;
    inc = 1e-2*r;
    digits = 3;
    id = ID_TONE_SENSE;

  }
  else { // tone.GetKind() == TONE_KIND_REL ) {
    r = tone.GetRsense();
    rmin = 1e-2;
    rmax = 1e2;
    inc = 1e-2;
    digits = 3;
    id = ID_TONE_RSENSE;
  }

  MuniTuneLogjuster *s =
    new MuniTuneLogjuster(tpanel,id,r,rmin,rmax,inc,digits,L"○",L"◑");
  s->SetToolTip("Sensitivity adjusts a range of levels to display.");
  return s;
}

MuniTuneAdjuster *MuniTune::CreateIttAmount(wxWindow *tpanel)
{
  adjamount = new MuniTuneAdjuster(tpanel,ID_ITT_AMP,itt.GetAmp(),0.0,2.0,
				   0.01,2,L"-",L"∼");
  adjamount->SetToolTip("Amount of curvature of tone function.");
  return adjamount;
}

MuniTuneAdjuster *MuniTune::CreateIttZero(wxWindow *tpanel)
{
  MuniTuneAdjuster *adjzero = new MuniTuneAdjuster(tpanel,ID_ITT_ZERO,
						   itt.GetZero(),-1.0,1.0,
						   0.01,2,L"↓",L"↑");
  adjzero->SetToolTip("Zero is relative black of tone function.");
  return adjzero;
}


wxNotebookPage *MuniTune::CreateScaleTab(wxNotebook *book)
{
  wxSizerFlags vsizer(1);
  vsizer.Align(wxALIGN_CENTER_VERTICAL).Border(wxLEFT|wxRIGHT).Expand();

  wxNotebookPage *tpanel = new wxPanel(book);
  wxBoxSizer *isizer = new wxBoxSizer(wxVERTICAL);

  // type
  wxStaticText *label = new wxStaticText(tpanel,wxID_ANY,"Pre-scale:");
  radio_rel = new wxRadioButton(tpanel,wxID_ANY,"Relative",wxDefaultPosition,
					       wxDefaultSize,wxRB_GROUP);
  radio_abs = new wxRadioButton(tpanel,wxID_ANY,"Absolute");

  wxBoxSizer *radios = new wxBoxSizer(wxHORIZONTAL);
  radios->Add(label,
	      wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Border(wxRIGHT));
  radios->Add(radio_rel,wxSizerFlags().Border(wxRIGHT));
  radios->Add(radio_abs,wxSizerFlags().Border(wxLEFT));
  isizer->Add(radios,wxSizerFlags().Center().Border());

  if( toneabs )
    radio_abs->SetValue(true);
  else
    radio_rel->SetValue(true);

  adjblack = CreateToneBlack(tpanel);
  isizer->Add(adjblack,vsizer);

  adjsense = CreateToneSense(tpanel);
  isizer->Add(adjsense,vsizer);

  tpanel->SetSizer(isizer);

  Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED,&MuniTune::OnRadioItt,this,
       radio_abs->GetId());
  Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED,&MuniTune::OnRadioItt,this,
       radio_rel->GetId());

  return tpanel;
}

wxNotebookPage *MuniTune::CreateIttTab(wxNotebook *book)
{
  wxSizerFlags vsizer(1);
  vsizer.Align(wxALIGN_CENTER_VERTICAL).Border(wxLEFT|wxRIGHT).Expand();

  wxNotebookPage *tpanel = new wxPanel(book);
  wxBoxSizer *isizer = new wxBoxSizer(wxVERTICAL);

  wxBoxSizer *typess = new wxBoxSizer(wxHORIZONTAL);
  type_itt = new wxChoice(tpanel,ID_CHOICE_ITT,wxDefaultPosition,
			  wxDefaultSize,FitsItt::Type_str());
  type_itt->SetStringSelection(itt.GetItt_str());

  typess->Add(new wxStaticText(tpanel,wxID_ANY,"Function:"),
	      wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Border(wxRIGHT));
  typess->Add(type_itt);

  isizer->Add(typess,wxSizerFlags().Center().Border());

  adjamount = CreateIttAmount(tpanel);
  isizer->Add(adjamount,vsizer);

  adjzero = CreateIttZero(tpanel);
  isizer->Add(adjzero,vsizer);

  Bind(wxEVT_COMMAND_CHOICE_SELECTED,&MuniTune::OnChoiceItt,this,ID_CHOICE_ITT);
  Bind(wxEVT_UPDATE_UI,&MuniTune::OnUpdateIttpar,this,ID_ITT_AMP);
  Bind(wxEVT_UPDATE_UI,&MuniTune::OnUpdateIttpar,this,ID_ITT_ZERO);

  tpanel->SetSizer(isizer);

  return tpanel;
}

wxNotebookPage *MuniTune::CreateColourTab(wxNotebook *book)
{
  wxSizerFlags vsizer(1);
  vsizer.Align(wxALIGN_CENTER_VERTICAL).Border(wxLEFT|wxRIGHT).Expand();

  wxNotebookPage *cpanel = new wxPanel(book);

  wxBoxSizer *csizer = new wxBoxSizer(wxVERTICAL);

  csizer->Add(new wxStaticText(cpanel, wxID_ANY,"Saturation and Hue"),
	      wxSizerFlags().Center().Border());

  adjsatur = new MuniTuneAdjuster(cpanel,ID_COLOR_SATUR,color.GetSaturation(),
				  0.0,3.0,0.1,1);
  adjsatur->SetToolTip("Color saturation sets amount of colors in image.");
  csizer->Add(adjsatur,vsizer);

  adjhuee = new MuniTuneAdjuster(cpanel,ID_COLOR_HUE,color.GetHue(),
				 -180.0,180.0,10.0);
  adjhuee->SetToolTip("Hue rotates the color space.");
  csizer->Add(adjhuee,vsizer);

  cpanel->SetSizer(csizer);

  return cpanel;
}

wxNotebookPage *MuniTune::CreateNiteTab(wxNotebook *book)
{
  wxSizerFlags vsizer(1);
  vsizer.Align(wxALIGN_CENTER_VERTICAL).Border(wxLEFT|wxRIGHT).Expand();

  // Night
  wxNotebookPage *npanel = new wxPanel(book);

  wxBoxSizer *nsizer = new wxBoxSizer(wxVERTICAL);
  nitecheck = new wxCheckBox(npanel,ID_CHECK_NITE,"Night vision");
  nsizer->Add(nitecheck,wxSizerFlags().Center().Border());

  adjnthresh = new MuniTuneAdjuster(npanel,ID_COLOR_NITETHRESH,
				    color.GetNiteThresh(),0.0,100.0,1.0);
  adjnthresh->SetToolTip("Threshold is intensity where night vision (bad ligthting conditions) grades to day vision (good lighthing)");
  nsizer->Add(adjnthresh,vsizer);
  adjmeso = new MuniTuneAdjuster(npanel,ID_COLOR_NITEWIDTH,color.GetNiteWidth(),
				 1.0,20.0,1.0);
  adjmeso->SetToolTip("Mesotopic layer is width of curve connecting night and day vision");
  nsizer->Add(adjmeso,vsizer);

  npanel->SetSizer(nsizer);

  Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,&MuniTune::OnCheckNite,this,
       ID_CHECK_NITE);
  Bind(wxEVT_UPDATE_UI,&MuniTune::OnUpdateNiteadj,this,ID_COLOR_NITEWIDTH);
  Bind(wxEVT_UPDATE_UI,&MuniTune::OnUpdateNiteadj,this,ID_COLOR_NITETHRESH);

  nitecheck->SetValue(color.GetNiteVision());

  return npanel;
}

wxNotebookPage *MuniTune::CreateLutTab(wxNotebook *book)
{
  wxNotebookPage *ppanel = new wxPanel(book);

  lutch = new wxChoice(ppanel,ID_CHOICE_PAL,wxDefaultPosition,
		       wxDefaultSize,FitsPalette::Type_str());
  lutch->SetStringSelection(pal.GetPalette_str());

  invcheck = new wxCheckBox(ppanel,ID_CHECK_INVERSE,"Inverse");
  invcheck->SetValue(pal.GetInverse());

  lutus = new MuniLUTus(ppanel);
  lutus->SetPalette(pal);

  wxBoxSizer *cchoice = new wxBoxSizer(wxHORIZONTAL);
  cchoice->Add(lutch,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER).Border(wxRIGHT));
  cchoice->Add(invcheck,wxSizerFlags(1).Align(wxALIGN_CENTER_VERTICAL|
					      wxALIGN_CENTER).Border(wxLEFT));
  wxBoxSizer *lsizer = new wxBoxSizer(wxVERTICAL);
  lsizer->Add(cchoice,wxSizerFlags().Border().Center());
  lsizer->Add(lutus,wxSizerFlags().Border().Expand());

  ppanel->SetSizer(lsizer);

  Bind(wxEVT_COMMAND_CHOICE_SELECTED,&MuniTune::OnChoicePal,this,ID_CHOICE_PAL);
  Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,&MuniTune::OnCheckInverse,this,
       ID_CHECK_INVERSE);

  return ppanel;
}


void MuniTune::OnRadioItt(wxCommandEvent& event)
{
  toneabs = event.GetId() == radio_abs->GetId();

  wxWindow *tpanel = adjblack->GetParent();
  wxASSERT(tpanel && tpanel == adjsense->GetParent());
  wxSizer *isizer = tpanel->GetSizer();

  MuniTuneAdjuster *b = CreateToneBlack(tpanel);
  isizer->Replace(adjblack,b);
  adjblack->Destroy();
  adjblack = b;

  MuniTuneLogjuster *s = CreateToneSense(tpanel);
  isizer->Replace(adjsense,s);
  adjsense->Destroy();
  adjsense = s;

  isizer->Layout();
}


void MuniTune::OnChoiceItt(wxCommandEvent& event)
{
  MuniTuneEvent ev(EVT_TUNE,ID_ITT_TYPE);
  ev.SetString(event.GetString());
  wxQueueEvent(GetParent(),ev.Clone());

  ittline = event.GetString() == itt.Type_str(ITT_LINE);
  display->SetItt(event.GetString());
  render = true;
}

void MuniTune::OnChoicePal(wxCommandEvent& event)
{
  MuniTuneEvent ev(EVT_TUNE,ID_PALETTE_TYPE);
  ev.SetString(event.GetString());
  wxQueueEvent(GetParent(),ev.Clone());

  lutus->SetPalette(event.GetString());
  display->SetPalette(event.GetString());
  render = true;
}

void MuniTune::OnCheckInverse(wxCommandEvent& event)
{
  MuniTuneEvent ev(EVT_TUNE,ID_PALETTE_INVERSE);
  ev.SetInt(event.IsChecked());
  wxQueueEvent(GetParent(),ev.Clone());

  lutus->SetInversePalette(event.IsChecked());
  display->SetInversePalette(event.IsChecked());
  render = true;
}

void MuniTune::OnCheckNite(wxCommandEvent& event)
{
  MuniTuneEvent ev(EVT_TUNE,ID_COLOR_NITEVISION);
  ev.SetInt(event.IsChecked());
  wxQueueEvent(GetParent(),ev.Clone());

  nitevision = event.IsChecked();
  display->SetNiteVision(event.IsChecked());
  render = true;
}

void MuniTune::OnScrollFinish(wxScrollEvent& event)
{
  OnScroll(event);
}


void MuniTune::OnScroll(wxScrollEvent& event)
{
  wxString entry = event.GetString();
  double x;

  if( entry.ToDouble(&x) )
    SetScroll(event.GetId(),x);

  render = true;
}


void MuniTune::OnTuneFine(MuniTuneEvent& event)
{

  if( event.GetId() == ID_ITT_TYPE ) {
    display->SetItt(event.GetString());
    itt.SetItt(event.GetString());
  }

  else if( event.GetId() == ID_COLOR_NITEVISION ) {
    display->SetNiteVision(event.GetInt());
    color.SetNiteVision(event.GetInt());
  }

  else if( event.GetId() == ID_PALETTE_TYPE ) {
    display->SetPalette(event.GetString());
    pal.SetPalette(event.GetString());
  }

  else if( event.GetId() == ID_PALETTE_INVERSE ) {
    display->SetInversePalette(event.GetInt());
    pal.SetInverse(event.GetInt());
  }

  else
    SetScroll(event.GetId(),event.x);

  render = true;

  delete etune;
  etune = static_cast<MuniTuneEvent *>(event.Clone());
}

void MuniTune::SetScroll(int id, double x)
{
  switch(id) {
  case ID_TONE_BLACK:    display->SetBlack(x);  tone.SetBlack(x); break;
  case ID_TONE_SENSE:    display->SetSense(x);  tone.SetSense(x); break;
  case ID_TONE_QBLACK:   display->SetQblack(x); tone.SetQblack(x); break;
  case ID_TONE_RSENSE:   display->SetRsense(x); tone.SetRsense(x);break;
  case ID_ITT_AMP:       display->SetAmp(x);     itt.SetAmp(x);   break;
  case ID_ITT_ZERO:      display->SetZero(x);    itt.SetZero(x);  break;
  case ID_COLOR_SATUR:   display->SetSaturation(x);color.SetSaturation(x);break;
  case ID_COLOR_HUE:     display->SetHue(x);   color.SetHue(x);   break;
  case ID_COLOR_NITETHRESH:
    display->SetNiteThresh(x); color.SetNiteThresh(x);break;
  case ID_COLOR_NITEWIDTH:
    display->SetNiteWidth(x); color.SetNiteWidth(x); break;
  }
}

void MuniTune::OnReset(wxCommandEvent& event)
{
  tone.Reset();
  itt.Reset();
  pal.Reset();
  color.Reset();
  //  toneabs = typeid(*display) == typeid(FitsColourDisplay);

  wxWindow *w;

  w = FindWindowById(ID_TONE_BLACK);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(tone.GetBlack());

  w = FindWindowById(ID_TONE_QBLACK);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(tone.GetQblack());

  w = FindWindowById(ID_TONE_SENSE);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(tone.GetSense());

  w = FindWindowById(ID_TONE_RSENSE);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(tone.GetRsense());

  w = FindWindowById(ID_ITT_AMP);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(itt.GetAmp());

  w = FindWindowById(ID_ITT_ZERO);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(itt.GetZero());

  w = FindWindowById(ID_CHOICE_PAL);
  if( w )
    static_cast<wxChoice *>(w)->SetStringSelection(pal.GetPalette_str());

  w = FindWindowById(ID_CHECK_INVERSE);
  if( w )
    static_cast<wxCheckBox *>(w)->SetValue(pal.GetInverse());

  w = FindWindowById(ID_CHOICE_ITT);
  if( w )
    static_cast<wxChoice *>(w)->SetStringSelection(itt.GetItt_str());

  w = FindWindowById(ID_COLOR_SATUR);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(color.GetSaturation());

  w = FindWindowById(ID_COLOR_HUE);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(color.GetHue());

  w = FindWindowById(ID_CHECK_NITE);
  if( w )
    static_cast<wxCheckBox *>(w)->SetValue(color.GetNiteVision());

  w = FindWindowById(ID_COLOR_NITETHRESH);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(color.GetNiteThresh());

  w = FindWindowById(ID_COLOR_NITEWIDTH);
  if( w )
    static_cast<MuniTuneAdjuster *>(w)->SetValue(color.GetNiteWidth());

  if( lutus )
    lutus->SetPalette(pal);

  //  radio_abs->SetValue(toneabs);

  display->SetItt(itt);
  display->SetTone(tone);
  display->SetColour(color);
  display->SetPalette(pal);
  render = true;

  wxQueueEvent(GetParent(),new MuniTuneEvent(EVT_TUNE,ID_RESET));
}


void MuniTune::OnUpdateIttpar(wxUpdateUIEvent& event)
{
  event.Enable(!ittline);
}

void MuniTune::OnUpdateNiteadj(wxUpdateUIEvent& event)
{
  event.Enable(nitevision);
}



// ---- xTrafo

// class xTrafo
// {
// public:
//   xTrafo(double,double,double,double);

//   void SetAxes(double,double,double,double);
//   void SetArea(int,int,int,int);

//   void Linear(double,double,double *,double *);
//   void Scale(double,double,double *,double *);

// private:

//   double xmin,xmax,ymin,ymax;
//   double imin, imax, jmin, jmax;
//   double dx,dy;
// };


// xTrafo::xTrafo(double x0,double y0,double x1,double y1):
//   xmin(x0), xmax(x1), ymin(y0), ymax(y1),
//   imin(0.0), imax(-1.0), jmin(0.0), jmax(-1.0), dx(1.0), dy(1.0)
// {
//   SetAxes(x0,y0,x1,y1);
// }


// void xTrafo::SetAxes(double x0,double y0,double x1,double y1)
// {
//   xmin = x0;
//   xmax = x1;
//   ymin = y0;
//   ymax = y1;

//   if( fabs(xmin) < DBL_EPSILON && fabs(xmax) < DBL_EPSILON ) {
//     xmin = 0.0;
//     xmax = 1.0;
//   }
//   if( fabs(ymin - ymax) < DBL_EPSILON ) {
//     ymin = -1.0;
//     ymax = 1.0;
//   }

//   dx = (xmax - xmin)/double(imax - imin);
//   dy = (ymax - ymin)/double(jmax - jmin);
// }

// void xTrafo::SetArea(int i, int j, int w, int h)
// {
//   imin = i;
//   imax = i + w;
//   jmin = j;
//   jmax = j + h;

//   dx = (xmax - xmin)/double(imax - imin);
//   dy = (ymax - ymin)/double(jmax - jmin);
// }


// void xTrafo::Linear(double x, double y, double *i, double *j)
// {
//   *i = imin + (x - xmin)/dx;
//   *j = jmin + (ymax - y)/dy;
// }

// void xTrafo::Scale(double w, double h, double *i, double *j)
// {
//   *i = w/dx;
//   *j = h/dy;
// }



// // ---- MuniGraph ---------------------------------------------

// BEGIN_EVENT_TABLE(MuniGraph, wxPanel)
//   EVT_PAINT(MuniGraph::OnPaint)
//   EVT_SIZE(MuniGraph::OnSize)
// END_EVENT_TABLE()




// MuniGraph::MuniGraph(wxWindow *w): wxPanel(w,wxID_ANY,wxDefaultPosition,
// 					   wxSize(300,185)), itt(0),
// 				   strip_width(12), big_tic(5),small_tic(3)
// {
//   SetBackgroundStyle(wxBG_STYLE_CUSTOM);
//   sf = wxFont(*wxSMALL_FONT);
// }

// MuniGraph::~MuniGraph() { hlist.clear(); }

// wxSize MuniGraph::DoGetBestSize() const
// {
//   return wxSize(300,185);
// }

// void MuniGraph::SetItt(const FitsItt& i)
// {
//   itt = i;
//   Refresh();
// }

// void MuniGraph::SetHisto(const FitsHisto& hl)
// {
//   hlist.clear();
//   hlist.push_back(hl);
//   Refresh();
// }

// void MuniGraph::SetHisto(const vector<FitsHisto>& hl)
// {
//   hlist = hl;
//   Refresh();
// }

// void MuniGraph::OnPaint(wxPaintEvent& event)
// {
//   Create();
// }

// void MuniGraph::OnSize(wxSizeEvent& event)
// {
//   Create();
//   event.Skip();
// }


// void MuniGraph::Create()
// {

//   wxSize size = GetClientSize();
//   int width = size.GetWidth();
//   int height = size.GetHeight() - sf.GetPointSize() - 2 - big_tic;

//   // search for maximum in histogram
//   /*
//   int hmax = 0;
//   for(std::vector<FitsHisto>::iterator k = hlist.begin();k != hlist.end();++k){
//     FitsHisto hist = *k;
//     for(int i = 0; i < hist.NBins(); i++ ) {
//       if( hist.Hist(i) > hmax )
// 	hmax = hist.Hist(i);
//     }
//   }
//   */

//   // estimate y-scale from histogram
//   int hmax = 0;
//   for(std::vector<FitsHisto>::iterator k = hlist.begin();k != hlist.end();++k){
//     FitsHisto hist = *k;
//     for(int i = 0; i < hist.NBins(); i++ ) {
//       int x = 2.0*hist.Hist(i);
//       if( hist.Cents(i) - itt.GetMed() > itt.GetMad() && x > hmax ) {
// 	hmax = x;
// 	continue;
//       }
//     }
//   }


//   wxImage i(size.GetWidth(),size.GetHeight(),false);
//   if( ! i.IsOk() ) return;
//   //  wxLogDebug(_("%d %d"),size.GetWidth(),size.GetHeight());
//   //  wxASSERT(i.IsOk());
//   wxBitmap paper(i);

//   wxMemoryDC dc(paper);
//   wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
//   if( gc ) {

//     double xmin = itt.GetRangeMin();
//     double xmax = itt.GetRangeMax();

//     xTrafo trafo(xmin,0.0,xmax,double(hmax));
//     trafo.SetArea(0,0,size.GetWidth(),height);

//     // clear
//     gc->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
//     wxGraphicsPath gp(gc->CreatePath());
//     gp.AddRectangle(0,0,size.GetWidth(),size.GetHeight());
//     gc->FillPath(gp);

//     // histogram
//     const unsigned char opacity = hlist.size() > 1 ? 128 : 255;
//     wxColour colour(128,128,128,opacity);
//     int pit = 0;
//     for(std::vector<FitsHisto>::iterator k = hlist.begin();k!=hlist.end();++k){
//       FitsHisto hist = *k;

//       if( ! hist.IsOk() ) continue;

//       if( hlist.size() > 1 ) {
// 	switch (pit) {
// 	case 0: colour.Set(255,0,0,opacity); break;
// 	case 1: colour.Set(0,255,0,opacity); break;
// 	case 2: colour.Set(0,0,255,opacity); break;
// 	default: colour.Set(64,64,64,opacity); break;
// 	}
//       }
//       gc->SetBrush(wxBrush(colour));

//       for(int l = 0; l < hist.NBins(); l++ ) {
// 	double bw = hist.BinWidth();
// 	double bh = hist.Hist(l);
// 	double x,y,w,h;

// 	trafo.Linear(hist.Cents(l)-bw/2.0,bh,&x,&y);
// 	trafo.Scale(bw,bh,&w,&h);
// 	gc->DrawRectangle(x,y,w,h);
//       }

//       pit++;
//       if( pit == 3 ) pit = 0;
//     }

//     gc->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

//     double ybase = height;

//     wxPoint2DDouble lines[] = { wxPoint2DDouble(0.0,ybase),
// 				wxPoint2DDouble(size.GetWidth(),ybase) };
//     gc->DrawLines(2,lines);

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

//     // rounding to only one place of 1,2,5
//     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;
//     float xtics[] = {1.0, 2.0, 5.0, 10.0 };
//     for(int i = 1; i < 4; i++ )
//       if( xtics[i-1] <= tic && tic < xtics[i] ) {
// 	tic = xtics[i];
// 	break;
//       }
//     tic = tic*e10;

//     // start on rounded position
//     double xs = trunc(xmin/tic)*tic;

//     // tics
//     double x = xs;
//     while( x < xmax ) {
//       double ytic = fabs(fmod(x -(xs+tic),5.0*tic)) < 0.5 ? big_tic : small_tic;
//       double i,j;
//       trafo.Linear(x,0.0,&i,&j);

//       wxPoint2DDouble lines[] = { wxPoint2DDouble(i,j),
// 				  wxPoint2DDouble(i,j+ytic) };
//       gc->DrawLines(2,lines);

//       x = x + tic;
//     }

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

//     x = xs + tic;
//     while( x < xmax ) {
//       wxString a;
//       double tw,th,u,v,xoff;
//       a.Printf(wxT("%g"),x);
//       gc->GetTextExtent(a,&tw,&th,&u,&v);
//       xoff = tw/2.0;

//       double i,j;
//       trafo.Linear(x,0.0,&i,&j);
//       gc->DrawText(a,i-xoff,j+big_tic);
//       x = x + 2.0*tic;
//     }

//     // plot chain
//     const int npoints = width / 10.0;
//     gc->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

//     trafo.SetAxes(xmin,-15.0,xmax,270.0);

//     double step = (xmax - xmin)/npoints;
//     for(int i = 1; i < npoints; i++) {
//       double f = xmin + i*step;
//       unsigned char c = itt.Fscale(f);
//       gc->SetBrush(wxColour(c,c,c,128));
//       double x,y;
//       trafo.Linear(f,double(c),&x,&y);
//       gc->DrawEllipse(x-3.0,y-3.0,6.0,6.0);
//     }
//     delete gc;
//   }

//   // draw
//   wxAutoBufferedPaintDC canvas(this);
//   canvas.Blit(0,0,size.GetWidth(),size.GetHeight(), &dc, 0, 0);
// }
