#include <iostream>
#include <sstream>

#include "plotpropertieswindow.h"
#include "plotwidget.h"
#include "xyplot.h"

PlotPropertiesWindow::PlotPropertiesWindow(XYPlot& plot,
                                           const std::string& title)
    : Gtk::Window(),
      _plot(plot),
      _titleEntry(),

      _applyButton("_Apply", true),
      _exportButton("_Export", true),
      _closeButton("_Close", true),

      _vRangeFrame("Vertical scale"),
      _minMaxVRangeButton("From min to max"),
      _winsorizedVRangeButton("Winsorized min and max"),
      _specifiedVRangeButton("Specified:"),
      _vRangeMinLabel("Scale minimum:"),
      _vRangeMaxLabel("Scale maximum:"),
      _vRangeMinEntry(),
      _vRangeMaxEntry(),

      _hRangeFrame("Horizontal scale"),
      _automaticHRangeButton("Automatic"),
      _hRangeMinLabel("Scale minimum:"),
      _hRangeMaxLabel("Scale maximum:"),
      _hRangeMinEntry(),
      _hRangeMaxEntry(),

      _xOptionsFrame("X options"),
      _xLogScaleButton("Logarithmic scale"),

      _yOptionsFrame("Y options"),
      _yNormalOptionsButton("Normal scale"),
      _yLogScaleButton("Logarithmic scale"),
      _yZeroSymmetricButton("Symmetric around zero"),

      _axesDescriptionFrame("Axes"),
      _hAxisDescriptionButton("Horizontal description"),
      _vAxisDescriptionButton("Vertical description"),
      _hAxisDescriptionEntry(),
      _vAxisDescriptionEntry(),

      _showAxes("Show axes"),
      _showAxisDescriptionsButton("Show axis descriptions") {
  set_title(title);

  initVRangeWidgets();
  initHRangeWidgets();
  initOptionsWidgets();
  initAxesDescriptionWidgets();

  _titleEntry.set_text(_plot.GetTitle());
  _topVBox.append(_titleEntry);

  _framesHBox.append(_framesRightVBox);

  _applyButton.signal_clicked().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onApplyClicked));
  _applyButton.set_icon_name("view-refresh");
  _bottomButtonBox.append(_applyButton);

  _exportButton.signal_clicked().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onExportClicked));
  _exportButton.set_icon_name("document-save");
  _bottomButtonBox.append(_exportButton);

  _closeButton.signal_clicked().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onCloseClicked));
  _closeButton.set_icon_name("window-close");
  _bottomButtonBox.append(_closeButton);

  _topVBox.append(_framesHBox);

  _showAxes.set_active(_plot.XAxis().Show() || _plot.YAxis().Show());
  _topVBox.append(_showAxes);

  _showAxisDescriptionsButton.set_active(_plot.ShowAxisDescriptions());
  _topVBox.append(_showAxisDescriptionsButton);

  _bottomButtonBox.set_homogeneous(true);
  _topVBox.append(_bottomButtonBox);

  set_child(_topVBox);
}

void PlotPropertiesWindow::initVRangeWidgets() {
  _vRangeFrame.set_child(_vRangeBox);

  _vRangeBox.append(_minMaxVRangeButton);
  _minMaxVRangeButton.signal_toggled().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onVRangeChanged));

  //_vRangeBox.append(_winsorizedVRangeButton);
  _winsorizedVRangeButton.set_group(_minMaxVRangeButton);
  _winsorizedVRangeButton.signal_toggled().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onVRangeChanged));
  _vRangeBox.append(_specifiedVRangeButton);

  _specifiedVRangeButton.set_group(_minMaxVRangeButton);
  _specifiedVRangeButton.signal_toggled().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onVRangeChanged));

  switch (_plot.YAxis().GetRangeDetermination()) {
    default:
    case RangeDetermination::MinMaxRange:
      _minMaxVRangeButton.set_active(true);
      break;
    case RangeDetermination::WinsorizedRange:
      _winsorizedVRangeButton.set_active(true);
      break;
    case RangeDetermination::SpecifiedRange:
      _specifiedVRangeButton.set_active(true);
      break;
  }
  onVRangeChanged();

  updateVMinMaxEntries();

  _vRangeBox.append(_vRangeMinLabel);
  _vRangeBox.append(_vRangeMinEntry);

  _vRangeBox.append(_vRangeMaxLabel);
  _vRangeBox.append(_vRangeMaxEntry);

  _framesHBox.append(_vRangeFrame);
}

void PlotPropertiesWindow::initHRangeWidgets() {
  _hRangeFrame.set_child(_hRangeBox);

  _hRangeBox.append(_automaticHRangeButton);
  _automaticHRangeButton.set_active(_plot.XAxis().GetRangeDetermination() !=
                                    RangeDetermination::SpecifiedRange);
  _automaticHRangeButton.signal_toggled().connect(
      sigc::mem_fun(*this, &PlotPropertiesWindow::onHRangeChanged));

  onHRangeChanged();

  updateHMinMaxEntries();

  _hRangeBox.append(_hRangeMinLabel);
  _hRangeBox.append(_hRangeMinEntry);

  _hRangeBox.append(_hRangeMaxLabel);
  _hRangeBox.append(_hRangeMaxEntry);

  _framesHBox.append(_hRangeFrame);
}

void PlotPropertiesWindow::initOptionsWidgets() {
  _xOptionsBox.append(_xLogScaleButton);
  _xLogScaleButton.set_active(_plot.XAxis().Logarithmic());
  _xOptionsFrame.set_child(_xOptionsBox);
  _framesRightVBox.append(_xOptionsFrame);

  _yOptionsBox.append(_yNormalOptionsButton);

  _yOptionsBox.append(_yLogScaleButton);
  _yLogScaleButton.set_group(_yNormalOptionsButton);

  _yOptionsBox.append(_yZeroSymmetricButton);
  _yZeroSymmetricButton.set_group(_yNormalOptionsButton);

  if (_plot.YAxis().Logarithmic())
    _yLogScaleButton.set_active(true);
  else
    _yNormalOptionsButton.set_active(true);

  _yOptionsFrame.set_child(_yOptionsBox);

  _framesRightVBox.append(_yOptionsFrame);
}

void PlotPropertiesWindow::initAxesDescriptionWidgets() {
  _axesDescriptionBox.append(_hAxisDescriptionButton);
  _axesDescriptionBox.append(_hAxisDescriptionEntry);
  _axesDescriptionBox.append(_vAxisDescriptionButton);
  _axesDescriptionBox.append(_vAxisDescriptionEntry);

  _axesDescriptionFrame.set_child(_axesDescriptionBox);

  _framesRightVBox.append(_axesDescriptionFrame);
}

void PlotPropertiesWindow::updateHMinMaxEntries() {
  auto range = _plot.RangeX(false);
  std::stringstream minStr;
  minStr << range.first;
  _hRangeMinEntry.set_text(minStr.str());

  std::stringstream maxStr;
  maxStr << range.second;
  _hRangeMaxEntry.set_text(maxStr.str());
}

void PlotPropertiesWindow::updateVMinMaxEntries() {
  auto range = _plot.RangeY(false);
  std::stringstream minStr;
  minStr << range.first;
  _vRangeMinEntry.set_text(minStr.str());

  std::stringstream maxStr;
  maxStr << range.second;
  _vRangeMaxEntry.set_text(maxStr.str());
}

void PlotPropertiesWindow::onApplyClicked() {
  _plot.SetTitle(_titleEntry.get_text());

  if (_minMaxVRangeButton.get_active()) {
    _plot.YAxis().SetRangeDetermination(RangeDetermination::MinMaxRange);
  } else if (_winsorizedVRangeButton.get_active()) {
    _plot.YAxis().SetRangeDetermination(RangeDetermination::WinsorizedRange);
  } else if (_specifiedVRangeButton.get_active()) {
    _plot.YAxis().SetRangeDetermination(RangeDetermination::SpecifiedRange);
    _plot.YAxis().SetMin(atof(_vRangeMinEntry.get_text().c_str()));
    _plot.YAxis().SetMax(atof(_vRangeMaxEntry.get_text().c_str()));
  }

  if (_automaticHRangeButton.get_active()) {
    _plot.XAxis().SetRangeDetermination(RangeDetermination::MinMaxRange);
  } else {
    _plot.XAxis().SetRangeDetermination(RangeDetermination::SpecifiedRange);
    _plot.XAxis().SetMin(atof(_hRangeMinEntry.get_text().c_str()));
    _plot.XAxis().SetMax(atof(_hRangeMaxEntry.get_text().c_str()));
  }

  _plot.XAxis().SetLogarithmic(_xLogScaleButton.get_active());

  if (_yNormalOptionsButton.get_active())
    _plot.YAxis().SetLogarithmic(false);
  else if (_yLogScaleButton.get_active())
    _plot.YAxis().SetLogarithmic(true);

  if (_hAxisDescriptionButton.get_active())
    _plot.XAxis().SetCustomDescription(
        _hAxisDescriptionEntry.get_text().c_str());
  else
    _plot.XAxis().SetAutomaticDescription();

  if (_vAxisDescriptionButton.get_active())
    _plot.YAxis().SetCustomDescription(
        _vAxisDescriptionEntry.get_text().c_str());
  else
    _plot.YAxis().SetAutomaticDescription();

  _plot.XAxis().SetShow(_showAxes.get_active());
  _plot.YAxis().SetShow(_showAxes.get_active());
  _plot.SetShowAxisDescriptions(_showAxisDescriptionsButton.get_active());

  if (OnChangesApplied) OnChangesApplied();

  updateHMinMaxEntries();
  updateVMinMaxEntries();
}

void PlotPropertiesWindow::onCloseClicked() { hide(); }

void PlotPropertiesWindow::onExportClicked() {
  dialog_ = std::make_unique<Gtk::FileChooserDialog>(
      "Specify image filename", Gtk::FileChooser::Action::SAVE);
  dialog_->set_transient_for(*this);

  // Add response buttons the the dialog:
  dialog_->add_button("_Cancel", Gtk::ResponseType::CANCEL);
  dialog_->add_button("_Save", Gtk::ResponseType::OK);

  const Glib::RefPtr<Gtk::FileFilter> pdfFilter = Gtk::FileFilter::create();
  static constexpr char pdfName[] = "Portable Document Format (*.pdf)";
  pdfFilter->set_name(pdfName);
  pdfFilter->add_pattern("*.pdf");
  pdfFilter->add_mime_type("application/pdf");
  dialog_->add_filter(pdfFilter);

  const Glib::RefPtr<Gtk::FileFilter> svgFilter = Gtk::FileFilter::create();
  static constexpr char svgName[] = "Scalable Vector Graphics (*.svg)";
  svgFilter->set_name(svgName);
  svgFilter->add_pattern("*.svg");
  svgFilter->add_mime_type("image/svg+xml");
  dialog_->add_filter(svgFilter);

  const Glib::RefPtr<Gtk::FileFilter> pngFilter = Gtk::FileFilter::create();
  static constexpr char pngName[] = "Portable Network Graphics (*.png)";
  pngFilter->set_name(pngName);
  pngFilter->add_pattern("*.png");
  pngFilter->add_mime_type("image/png");
  dialog_->add_filter(pngFilter);

  dialog_->signal_response().connect([this](int response) {
    if (response == Gtk::ResponseType::OK) {
      const Glib::RefPtr<Gtk::FileFilter> filter = dialog_->get_filter();
      if (filter->get_name() == pdfName)
        _plot.SavePdf(dialog_->get_file()->get_path(), _plot.Width(),
                      _plot.Height());
      else if (filter->get_name() == svgName)
        _plot.SaveSvg(dialog_->get_file()->get_path(), _plot.Width(),
                      _plot.Height());
      else
        _plot.SavePng(dialog_->get_file()->get_path(), _plot.Width(),
                      _plot.Height());
    }
    dialog_.reset();
  });
  dialog_->show();
}
