#include <iostream>
#include <sstream>

#include <gtkmm/filechooserdialog.h>
#include "../util/gtkmm-compat.h"

#include "imagepropertieswindow.h"
#include "heatmapwidget.h"

ImagePropertiesWindow::ImagePropertiesWindow(HeatMapWidget &imageWidget,
                                             const std::string &title)
    : Gtk::Window(),
      _imageWidget(imageWidget),
      _applyButton("_Apply", true),
      _exportButton("_Export", true),
      _exportDataButton("Export _data", true),
      _closeButton("_Close", true),

      _colorMapFrame("Color map"),

      _scaleFrame("Scale"),
      _minMaxScaleButton("From min to max"),
      _winsorizedScaleButton("Winsorized min and max"),
      _specifiedScaleButton("Specified:"),
      _scaleMinLabel("Scale minimum:"),
      _scaleMaxLabel("Scale maximum:"),
      _scaleMinEntry(),
      _scaleMaxEntry(),

      _optionsFrame("Options"),
      _normalOptionsButton("Normal scale"),
      _logScaleButton("Logarithmic scale"),
      _zeroSymmetricButton("Symmetric around zero"),

      _filterFrame("Interpolation"),
      _bestFilterButton("Best"),
      _nearestFilterButton("Nearest"),

      _axesFrame("Title & axes"),
      _showXYAxes("Show XY axes"),
      _showColorScale("Show color scale"),
      _showTitleButton("Show title"),
      _showXAxisDescriptionButton("x-axis desc"),
      _showYAxisDescriptionButton("y-axis desc"),
      _showZAxisDescriptionButton("z-axis desc"),
      _manualTitle("manual"),
      _manualXAxisDescription("manual"),
      _manualYAxisDescription("manual"),
      _manualZAxisDescription("manual") {
  set_title(title);

  initColorMapButtons();
  initScaleWidgets();
  initOptionsWidgets();
  initFilterWidgets();
  _framesHBox.pack_start(_filterAndOptionsBox);
  initAxisWidgets();

  gtkmm_set_image_from_icon_name(_applyButton, "gtk-apply");
  _applyButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onApplyClicked));
  _bottomButtonBox.pack_start(_applyButton);

  gtkmm_set_image_from_icon_name(_exportButton, "document-save-as");
  _exportButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onExportClicked));
  _bottomButtonBox.pack_start(_exportButton);

  _exportDataButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onExportDataClicked));
  _bottomButtonBox.pack_start(_exportDataButton);

  gtkmm_set_image_from_icon_name(_closeButton, "window-close");
  _closeButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onCloseClicked));
  _bottomButtonBox.pack_start(_closeButton);

  _topVBox.pack_start(_framesHBox);

  _topVBox.pack_start(_bottomButtonBox);

  add(_topVBox);
  _topVBox.show_all();
}

void ImagePropertiesWindow::initColorMapButtons() {
  _colorMapStore = Gtk::ListStore::create(_colorMapColumns);

  addColorMap("Grayscale", ColorMap::Grayscale);
  addColorMap("Inverted grayscale", ColorMap::Inverted);
  addColorMap("Hot/cold", ColorMap::HotCold);
  addColorMap("Red/blue", ColorMap::RedBlue);
  addColorMap("Black/red", ColorMap::BlackRed);
  addColorMap("Red/Yellow/Blue", ColorMap::RedYellowBlue);
  addColorMap("Fire", ColorMap::Fire);
  addColorMap("Cool", ColorMap::Cool);
  addColorMap("Cubehelix", ColorMap::CubeHelix);
  addColorMap("Cubehelix+", ColorMap::CubeHelixColourful);
  addColorMap("Viridis", ColorMap::Viridis);
  addColorMap("Rainbow", ColorMap::Rainbow);

  _colorMapCombo.set_model(_colorMapStore);
  _colorMapCombo.pack_start(_colorMapColumns.name);

  ColorMap::Type selectedMap = _imageWidget.Plot().GetColorMap();
  Gtk::ListStore::Children children = _colorMapStore->children();
  for (auto row : children) {
    if (row[_colorMapColumns.colorMap] == selectedMap) {
      _colorMapCombo.set_active(row);
      break;
    }
  }

  _colorMapBox.pack_start(_colorMapCombo, false, false);

  _colorMapFrame.add(_colorMapBox);

  _framesHBox.pack_start(_colorMapFrame);
}

void ImagePropertiesWindow::initScaleWidgets() {
  _scaleFrame.add(_scaleBox);

  Gtk::RadioButton::Group group;

  _scaleBox.pack_start(_minMaxScaleButton);
  _minMaxScaleButton.set_group(group);
  _minMaxScaleButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onScaleChanged));

  _scaleBox.pack_start(_winsorizedScaleButton);
  _winsorizedScaleButton.set_group(group);
  _winsorizedScaleButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onScaleChanged));
  _scaleBox.pack_start(_specifiedScaleButton);

  _specifiedScaleButton.set_group(group);
  _specifiedScaleButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ImagePropertiesWindow::onScaleChanged));

  switch (_imageWidget.Plot().Range()) {
    default:
    case HeatMapPlot::MinMax:
      _minMaxScaleButton.set_active(true);
      break;
    case HeatMapPlot::Winsorized:
      _winsorizedScaleButton.set_active(true);
      break;
    case HeatMapPlot::Specified:
      _specifiedScaleButton.set_active(true);
      break;
  }
  onScaleChanged();

  updateMinMaxEntries();

  _scaleBox.pack_start(_scaleMinLabel);
  _scaleBox.pack_start(_scaleMinEntry);

  _scaleBox.pack_start(_scaleMaxLabel);
  _scaleBox.pack_start(_scaleMaxEntry);

  _framesHBox.pack_start(_scaleFrame);
}

void ImagePropertiesWindow::initOptionsWidgets() {
  Gtk::RadioButton::Group group;

  _optionsBox.pack_start(_normalOptionsButton);
  _normalOptionsButton.set_group(group);

  _optionsBox.pack_start(_logScaleButton);
  _logScaleButton.set_group(group);

  _optionsBox.pack_start(_zeroSymmetricButton);
  _zeroSymmetricButton.set_group(group);

  switch (_imageWidget.Plot().ScaleOption()) {
    default:
    case HeatMapPlot::NormalScale:
      _normalOptionsButton.set_active(true);
      break;
    case HeatMapPlot::LogScale:
      _logScaleButton.set_active(true);
      break;
    case HeatMapPlot::ZeroSymmetricScale:
      _zeroSymmetricButton.set_active(true);
      break;
  }

  _optionsFrame.add(_optionsBox);

  _filterAndOptionsBox.pack_start(_optionsFrame);
}

void ImagePropertiesWindow::initFilterWidgets() {
  Gtk::RadioButton::Group group;

  _filterBox.pack_start(_bestFilterButton);
  _bestFilterButton.set_group(group);

  _filterBox.pack_start(_nearestFilterButton);
  _nearestFilterButton.set_group(group);

  switch (_imageWidget.Plot().CairoFilter()) {
    default:
    case Cairo::FILTER_BEST:
      _bestFilterButton.set_active(true);
      break;
    case Cairo::FILTER_NEAREST:
      _nearestFilterButton.set_active(true);
      break;
  }

  _filterFrame.add(_filterBox);

  _filterAndOptionsBox.pack_start(_filterFrame);
}

void ImagePropertiesWindow::initAxisWidgets() {
  _showXYAxes.set_active(_imageWidget.Plot().ShowXYAxes());
  _axesGeneralBox.pack_start(_showXYAxes);

  _showColorScale.set_active(_imageWidget.Plot().ShowColorScale());
  _axesGeneralBox.pack_start(_showColorScale);

  _axesHBox.pack_start(_axesGeneralBox);

  _showTitleButton.set_active(_imageWidget.Plot().ShowTitle());
  _titleBox.pack_start(_showTitleButton);
  _manualTitle.set_active(_imageWidget.Plot().ManualTitle());
  _titleBox.pack_start(_manualTitle);
  _titleEntry.set_text(_imageWidget.Plot().ManualTitleText());
  _titleBox.pack_start(_titleEntry);

  _axesVisibilityBox.pack_start(_titleBox);

  _showXAxisDescriptionButton.set_active(
      _imageWidget.Plot().ShowXAxisDescription());
  _xAxisBox.pack_start(_showXAxisDescriptionButton);

  _manualXAxisDescription.set_active(
      _imageWidget.Plot().ManualXAxisDescription());
  _xAxisBox.pack_start(_manualXAxisDescription);

  _xAxisDescriptionEntry.set_text(_imageWidget.Plot().XAxisDescription());
  _xAxisBox.pack_start(_xAxisDescriptionEntry);

  _axesVisibilityBox.pack_start(_xAxisBox);

  _showYAxisDescriptionButton.set_active(
      _imageWidget.Plot().ShowYAxisDescription());
  _yAxisBox.pack_start(_showYAxisDescriptionButton);

  _manualYAxisDescription.set_active(
      _imageWidget.Plot().ManualYAxisDescription());
  _yAxisBox.pack_start(_manualYAxisDescription);

  _yAxisDescriptionEntry.set_text(_imageWidget.Plot().YAxisDescription());
  _yAxisBox.pack_start(_yAxisDescriptionEntry);

  _axesVisibilityBox.pack_start(_yAxisBox);

  _showZAxisDescriptionButton.set_active(
      _imageWidget.Plot().ShowZAxisDescription());
  _zAxisBox.pack_start(_showZAxisDescriptionButton);

  _manualZAxisDescription.set_active(
      _imageWidget.Plot().ManualZAxisDescription());
  _zAxisBox.pack_start(_manualZAxisDescription);

  _zAxisDescriptionEntry.set_text(_imageWidget.Plot().ZAxisDescription());
  _zAxisBox.pack_start(_zAxisDescriptionEntry);

  _axesVisibilityBox.pack_start(_zAxisBox);

  _axesHBox.pack_start(_axesVisibilityBox);

  _axesFrame.add(_axesHBox);
  _topVBox.pack_start(_axesFrame);
}

void ImagePropertiesWindow::updateMinMaxEntries() {
  std::stringstream minStr;
  minStr << _imageWidget.Plot().Min();
  _scaleMinEntry.set_text(minStr.str());

  std::stringstream maxStr;
  maxStr << _imageWidget.Plot().Max();
  _scaleMaxEntry.set_text(maxStr.str());
}

void ImagePropertiesWindow::onApplyClicked() {
  _imageWidget.Plot().SetColorMap(
      (*_colorMapCombo.get_active())[_colorMapColumns.colorMap]);

  if (_minMaxScaleButton.get_active())
    _imageWidget.Plot().SetRange(HeatMapPlot::MinMax);
  else if (_winsorizedScaleButton.get_active())
    _imageWidget.Plot().SetRange(HeatMapPlot::Winsorized);
  else if (_specifiedScaleButton.get_active()) {
    _imageWidget.Plot().SetRange(HeatMapPlot::Specified);
    _imageWidget.Plot().SetMin(atof(_scaleMinEntry.get_text().c_str()));
    _imageWidget.Plot().SetMax(atof(_scaleMaxEntry.get_text().c_str()));
  }

  if (_normalOptionsButton.get_active())
    _imageWidget.Plot().SetScaleOption(HeatMapPlot::NormalScale);
  else if (_logScaleButton.get_active())
    _imageWidget.Plot().SetScaleOption(HeatMapPlot::LogScale);
  else if (_zeroSymmetricButton.get_active())
    _imageWidget.Plot().SetScaleOption(HeatMapPlot::ZeroSymmetricScale);

  if (_bestFilterButton.get_active())
    _imageWidget.Plot().SetCairoFilter(Cairo::FILTER_BEST);
  else if (_nearestFilterButton.get_active())
    _imageWidget.Plot().SetCairoFilter(Cairo::FILTER_NEAREST);

  _imageWidget.Plot().SetShowTitle(_showTitleButton.get_active());
  _imageWidget.Plot().SetManualTitle(_manualTitle.get_active());
  if (_manualTitle.get_active())
    _imageWidget.Plot().SetManualTitleText(_titleEntry.get_text());

  _imageWidget.Plot().SetShowXYAxes(_showXYAxes.get_active());
  _imageWidget.Plot().SetShowXAxisDescription(
      _showXAxisDescriptionButton.get_active());
  _imageWidget.Plot().SetShowYAxisDescription(
      _showYAxisDescriptionButton.get_active());
  _imageWidget.Plot().SetShowZAxisDescription(
      _showZAxisDescriptionButton.get_active());

  _imageWidget.Plot().SetManualXAxisDescription(
      _manualXAxisDescription.get_active());
  if (_manualXAxisDescription.get_active())
    _imageWidget.Plot().SetXAxisDescription(_xAxisDescriptionEntry.get_text());

  _imageWidget.Plot().SetManualYAxisDescription(
      _manualYAxisDescription.get_active());
  if (_manualYAxisDescription.get_active())
    _imageWidget.Plot().SetYAxisDescription(_yAxisDescriptionEntry.get_text());

  _imageWidget.Plot().SetManualZAxisDescription(
      _manualZAxisDescription.get_active());
  if (_manualZAxisDescription.get_active())
    _imageWidget.Plot().SetZAxisDescription(_zAxisDescriptionEntry.get_text());

  _imageWidget.Plot().SetShowColorScale(_showColorScale.get_active());

  _imageWidget.Update();

  updateMinMaxEntries();
}

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

void ImagePropertiesWindow::onExportClicked() {
  if (_imageWidget.Plot().HasImage()) {
    Gtk::FileChooserDialog dialog("Specify image filename",
                                  Gtk::FILE_CHOOSER_ACTION_SAVE);
    dialog.set_transient_for(*this);

    // Add response buttons the the dialog:
    dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
    dialog.add_button("_Save", Gtk::RESPONSE_OK);

    Glib::RefPtr<Gtk::FileFilter> pdfFilter = Gtk::FileFilter::create();
    std::string pdfName = "Portable Document Format (*.pdf)";
    pdfFilter->set_name(pdfName);
    pdfFilter->add_pattern("*.pdf");
    pdfFilter->add_mime_type("application/pdf");
    dialog.add_filter(pdfFilter);

    Glib::RefPtr<Gtk::FileFilter> svgFilter = Gtk::FileFilter::create();
    std::string svgName = "Scalable Vector Graphics (*.svg)";
    svgFilter->set_name(svgName);
    svgFilter->add_pattern("*.svg");
    svgFilter->add_mime_type("image/svg+xml");
    dialog.add_filter(svgFilter);

    Glib::RefPtr<Gtk::FileFilter> pngFilter = Gtk::FileFilter::create();
    std::string pngName = "Portable Network Graphics (*.png)";
    pngFilter->set_name(pngName);
    pngFilter->add_pattern("*.png");
    pngFilter->add_mime_type("image/png");
    dialog.add_filter(pngFilter);

    int result = dialog.run();

    if (result == Gtk::RESPONSE_OK) {
      Glib::RefPtr<const Gtk::FileFilter> filter = dialog.get_filter();
      if (filter->get_name() == pdfName)
        _imageWidget.SavePdf(dialog.get_filename());
      else if (filter->get_name() == svgName)
        _imageWidget.SaveSvg(dialog.get_filename());
      else
        _imageWidget.SavePng(dialog.get_filename());
    }
  }
}

void ImagePropertiesWindow::onExportDataClicked() {
  if (_imageWidget.Plot().HasImage()) {
    Gtk::FileChooserDialog dialog("Specify data filename",
                                  Gtk::FILE_CHOOSER_ACTION_SAVE);
    dialog.set_transient_for(*this);

    // Add response buttons the the dialog:
    dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
    dialog.add_button("_Save", Gtk::RESPONSE_OK);

    Glib::RefPtr<Gtk::FileFilter> pdfFilter = Gtk::FileFilter::create();
    std::string pdfName = "Text format: width; height; data1; data2... (*.txt)";
    pdfFilter->set_name(pdfName);
    pdfFilter->add_pattern("*.txt");
    pdfFilter->add_mime_type("text/plain");
    dialog.add_filter(pdfFilter);
    int result = dialog.run();

    if (result == Gtk::RESPONSE_OK) {
      _imageWidget.Plot().SaveText(dialog.get_filename());
    }
  }
}
