/*

  xmunipack - tune panel adjuster

  Copyright © 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 <wx/spinctrl.h>
#include <cfloat>
#include <cmath>


// --- TuneAdjusterBase

MuniTuneAdjusterBase::MuniTuneAdjusterBase(wxWindow *w, wxWindowID id):
  wxControl(w,id,wxDefaultPosition,wxDefaultSize,wxBORDER_NONE),
  factor(100.0),value(0.0),slider(0),entry(0)
{
}

void MuniTuneAdjusterBase::Create(int ivalue, int imin, int imax,
			      double Value, double minValue, double maxValue,
			      double step, unsigned int digits, long spinstyle,
			      const wxString& cmin, const wxString& cmax)
{
  slider = new wxSlider(this,wxID_ANY,ivalue,imin,imax,wxDefaultPosition,
			wxDefaultSize,wxSL_HORIZONTAL & ~wxSL_LABELS);

  entry = new wxSpinCtrlDouble(this,wxID_ANY,"",wxDefaultPosition,
  			       wxDefaultSize,spinstyle,minValue,maxValue,
			       Value,step);
  if( digits > 0 )
    entry->SetDigits(digits);

  wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL);
  topsizer->Add(new wxStaticText(this,wxID_ANY,cmin),
		wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  topsizer->Add(slider,wxSizerFlags(1).Align(wxALIGN_CENTER_VERTICAL).Border(wxLEFT|wxRIGHT));
  topsizer->Add(new wxStaticText(this,wxID_ANY,cmax),
		wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Border(wxRIGHT));
  topsizer->Add(entry,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  SetSizer(topsizer);

  int sid = slider->GetId();
  int eid = entry->GetId();

  Bind(wxEVT_SCROLL_THUMBTRACK,&MuniTuneAdjusterBase::OnScroll,this,sid);
  Bind(wxEVT_SCROLL_THUMBRELEASE,&MuniTuneAdjusterBase::OnScrollFinish,this,sid);
  Bind(wxEVT_COMMAND_TEXT_UPDATED,&MuniTuneAdjusterBase::OnEntry,this,eid);
  Bind(wxEVT_COMMAND_TEXT_ENTER,&MuniTuneAdjusterBase::OnEntryFinish,this,eid);
  Bind(wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED,&MuniTuneAdjusterBase::OnSpinDouble,this,eid);

  SetValue(Value);
}


void MuniTuneAdjusterBase::SetToolTip(const wxString& text)
{
  wxASSERT(entry && slider);
  slider->SetToolTip(text);
  entry->SetToolTip(text+" Use ENTER to apply your changes.");
}

void MuniTuneAdjusterBase::OnScroll(wxScrollEvent& event)
{
  //  wxLogDebug("MuniTuneAdjusterBase::OnScroll %d",int(event.GetPosition()));

  OnUpdateValue(event.GetPosition());

  wxCommandEvent e(wxEVT_SCROLL_THUMBTRACK,GetId());
  e.SetString(textentry);
  wxQueueEvent(GetParent(),e.Clone());
}

void MuniTuneAdjusterBase::OnScrollFinish(wxScrollEvent& event)
{
  OnUpdateValue(event.GetPosition());

  MuniTuneEvent e(EVT_TUNE,GetId());
  e.x = value;
  wxQueueEvent(GetParent(),e.Clone());
}

void MuniTuneAdjusterBase::SetSlider(double x)
{
  slider->SetValue(int(x));
}

void MuniTuneAdjusterBase::Entry(const wxString& text)
{
  double t;
  if( text.ToDouble(&t) ) {
    value = t;
    SetSlider(value);
  }
}

void MuniTuneAdjusterBase::OnEntry(wxCommandEvent& event)
{
  Entry(event.GetString());
}

void MuniTuneAdjusterBase::OnEntryFinish(wxCommandEvent& event)
{
  Entry(event.GetString());

  MuniTuneEvent e(EVT_TUNE,GetId());
  e.x = value;
  wxQueueEvent(GetParent(),e.Clone());
}

void MuniTuneAdjusterBase::OnSpinDouble(wxSpinDoubleEvent& event)
{
  Entry(event.GetString());

  MuniTuneEvent e(EVT_TUNE,GetId());
  e.x = value;
  wxQueueEvent(GetParent(),e.Clone());
}

// --- TuneAdjuster

MuniTuneAdjuster::MuniTuneAdjuster(wxWindow *w, wxWindowID id, double Value,
				   double minValue, double maxValue,
				   double step, unsigned int digits,
				   const wxString& cmin, const wxString& cmax):
  MuniTuneAdjusterBase(w,id)
{
  int imin = int(factor*minValue);
  int imax = int(factor*maxValue + 0.5);
  int ivalue = int(factor*Value + 0.5);
  value = Value;
  textentry.Printf("%f",value);

  Create(ivalue,imin,imax,Value,minValue,maxValue,step,digits,
	 wxSP_ARROW_KEYS,cmin,cmax);
}

void MuniTuneAdjuster::SetValue(double t)
{
  value = t;
  textentry.Printf("%.4g",value);
  entry->SetValue(value);
  SetSlider(value);
}

void MuniTuneAdjuster::OnUpdateValue(int i)
{
  value = i / factor;
  textentry.Printf("%.4g",value);
  entry->SetValue(value);
}


void MuniTuneAdjuster::SetSlider(double x)
{
  int n = factor * x;
  slider->SetValue(n);
}



// --- TuneAdjusterLog


MuniTuneLogjuster::MuniTuneLogjuster(wxWindow *w, wxWindowID id, double Value,
				     double minValue, double maxValue,
				     double step, unsigned int digits,
				     const wxString& cmin,
				     const wxString& cmax):
  MuniTuneAdjusterBase(w,id)
{
  wxASSERT(minValue > 0 && maxValue > 0 && Value > 0);

  int imin = factor*log10(minValue);
  int imax = factor*log10(maxValue);
  int ivalue = factor*log10(Value);
  value = Value;
  textentry.Printf("%f",value);

  Create(ivalue,imin,imax,value,minValue,maxValue,step,digits,0,cmin,cmax);
}


void MuniTuneLogjuster::SetValue(double t)
{
  value = t;
  textentry.Printf("%.4g",value);
  entry->SetValue(value);
  SetSlider(value);
}

void MuniTuneLogjuster::OnUpdateValue(int i)
{
  double x = i / factor;
  value = pow(10.0,x);
  textentry.Printf("%.4g",value);
  entry->SetValue(value);

}
void MuniTuneLogjuster::SetSlider(double x)
{
  int n = factor * log10(x);
  slider->SetValue(n);
}
