#include "opendialog.h"
#include "rfiguiwindow.h"

#include <gtkmm/error.h>

OpenDialog::OpenDialog(Gtk::Window& parent)
    : Gtk::Dialog("Open file or measurement set", parent),
      add_files_button_("Add..."),
      _unselectButton("Unselect"),
      // File tab elements
      _fileScroller(),
      _fileList(1, false, Gtk::SelectionMode::SINGLE),
      // Options tab elements
      _dataColumnLabel("Data column: "),
      _otherColumnLabel("Other column: "),
      _combineSPWsCB("Combine SPWs"),
      _concatenateFrequency("Spectrally concatenate multiple measurement sets"),
      _readingModeLabel("Reading mode: "),
      _readingModeExplanation(
          R"(Set how the measurement set is read: in direct mode, the information for one
baseline is gathered by scanning through the entire baseline each time a
baseline is requested. It is the recommended mode for interactive
experimentation. The indirect and memory modes reorder the baselines in a
temporary file or memory, respectively, which takes time at the start, but
allows faster browsing through the baselines.)"),
      _baselineAveragingCB("Average baselines together"),
      _baselineAveragingCombo(),
      _baIncludeAutosCB("Include auto-correlations"),
      _baIncludeFlaggedCB("Include flagged values"),
      _baNoDifference("Use sample values"),
      _baTimeDifference("Use time difference"),
      _baFrequencyDifference("Use frequency difference"),
      _intervalCB("Interval"),
      _intervalExplanation(
          "Limit the timeslots that are read. If only an interval start is "
          "entered, the data set is read to the end.") {
  grid_.set_expand(true);
  grid_.set_halign(Gtk::Align::FILL);
  grid_.set_column_spacing(15);
  grid_.set_row_spacing(5);
  grid_.set_margin_top(10);
  grid_.set_margin_start(10);
  grid_.set_margin_end(10);
  get_content_area()->append(grid_);

  _fileList.set_column_title(0, "Selected file(s)");
  _fileList.set_size_request(0, 100);
  _fileList.get_selection()->signal_changed().connect(
      [&]() { onFileListSelectionChanged(); });

  _fileScroller.set_child(_fileList);

  files_box_.append(_fileScroller);
  files_frame_.set_child(files_box_);

  size_t gridRow = 0;

  _unselectButton.set_sensitive(false);
  _unselectButton.signal_clicked().connect([&]() { onUnselectFile(); });
  _fileButtonBox.append(_unselectButton);

  add_files_button_.signal_clicked().connect([&]() { OpenFileDialog(); });
  _fileButtonBox.append(add_files_button_);

  _fileButtonBox.set_halign(Gtk::Align::END);
  files_box_.append(_fileButtonBox);

  grid_.attach(files_frame_, 0, gridRow, 2, 1);
  ++gridRow;

  initDataColumnButtons(gridRow);
  initReadingModeButtons(gridRow);
  initBaselineAveragingButtons(gridRow);

  //_optionsTab.attach(_intervalCB, 0, gridRow, 1, 1);
  auto onChange = [&]() {
    const bool enabled = _intervalCB.get_active();
    _intervalStartEntry.set_sensitive(enabled);
    _intervalEndEntry.set_sensitive(enabled);
  };
  _intervalCB.signal_toggled().connect(onChange);
  onChange();
  _intervalStartEntry.set_width_chars(5);
  _intervalStartEntry.set_text("0");
  _intervalBox.append(_intervalStartEntry);
  _intervalEndEntry.set_width_chars(5);
  _intervalEndEntry.set_text("...");
  _intervalBox.append(_intervalEndEntry);
  grid_.attach(_intervalBox, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_intervalExplanation, 0, gridRow, 2, 1);
  ++gridRow;

  grid_.attach(_bottomButtonBox, 0, gridRow, 2, 1);
  ++gridRow;

  add_button("Cancel", Gtk::ResponseType::CANCEL);
  _openButton = add_button("Open", Gtk::ResponseType::OK);
  _openButton->set_sensitive(false);
}

void OpenDialog::initDataColumnButtons(size_t& gridRow) {
  grid_.attach(_dataColumnLabel, 0, gridRow, 1, 1);
  _dataColumnCombo.append("DATA");
  _dataColumnCombo.append("CORRECTED_DATA");
  _dataColumnCombo.append("MODEL_DATA");
  _dataColumnCombo.append("other...");
  _dataColumnCombo.signal_changed().connect([&]() {
    const bool isOther = _dataColumnCombo.get_active_row_number() == 3;
    _otherColumnLabel.set_sensitive(isOther);
    _otherColumnEntry.set_sensitive(isOther);
  });
  _dataColumnCombo.set_active(0);
  _dataColumnCombo.set_hexpand(true);
  _dataColumnCombo.set_halign(Gtk::Align::FILL);
  grid_.attach(_dataColumnCombo, 1, gridRow, 1, 1);

  _otherColumnLabel.set_halign(Gtk::Align::END);
  _otherColumnGrid.attach(_otherColumnLabel, 0, 0, 1, 1);
  _otherColumnEntry.set_hexpand(true);
  _otherColumnEntry.set_halign(Gtk::Align::FILL);
  _otherColumnGrid.attach(_otherColumnEntry, 1, 0, 1, 1);
  _otherColumnGrid.set_margin_bottom(10);
  grid_.attach(_otherColumnGrid, 1, gridRow + 1, 1, 1);
  gridRow += 2;
  grid_.attach(_combineSPWsCB, 0, gridRow, 2, 1);
  _combineSPWsCB.signal_toggled().connect([&] {
    // The Combine SPWs and Concatenate Frequency are mutually exclusive.
    _concatenateFrequency.set_sensitive(!_combineSPWsCB.get_active());
  });
  ++gridRow;
  grid_.attach(_concatenateFrequency, 0, gridRow, 2, 1);
  _concatenateFrequency.signal_toggled().connect([&] {
    // The Combine SPWs and Concatenate Frequency are mutually exclusive.
    _combineSPWsCB.set_sensitive(!_concatenateFrequency.get_active());
  });
  ++gridRow;
  grid_.attach(_columnSeparator, 0, gridRow, 4, 1);
  ++gridRow;
}

void OpenDialog::initReadingModeButtons(size_t& gridRow) {
  _readingModeLabel.set_hexpand(false);
  grid_.attach(_readingModeLabel, 0, gridRow, 1, 1);
  grid_.attach(_readingModeCombo, 1, gridRow, 1, 1);
  ++gridRow;
  _readingModeCombo.append("Direct");
  _readingModeCombo.append("Indirect");
  _readingModeCombo.append("Memory");
  _readingModeCombo.set_active(0);
  grid_.attach(_readingModeExplanation, 0, gridRow, 2, 1);
  ++gridRow;
  //_readingModeExplanation.set_wrap(true);
  //_readingModeExplanation.set_width_chars(80);
  //_readingModeExplanation.set_max_width_chars(80);
  _readingModeExplanation.set_margin_bottom(10);
  grid_.attach(_readingSeparator, 0, gridRow, 2, 1);
  ++gridRow;
}

void OpenDialog::initBaselineAveragingButtons(size_t& gridRow) {
  grid_.attach(_baselineAveragingCB, 0, gridRow, 2, 1);
  auto onChange = [&]() {
    const bool enabled = _baselineAveragingCB.get_active();
    _baselineAveragingCombo.set_sensitive(enabled);
    _baIncludeAutosCB.set_sensitive(enabled);
    _baIncludeFlaggedCB.set_sensitive(enabled);
    _baNoDifference.set_sensitive(enabled);
    _baTimeDifference.set_sensitive(enabled);
    _baFrequencyDifference.set_sensitive(enabled);
  };
  _baselineAveragingCB.signal_toggled().connect(onChange);
  onChange();
  ++gridRow;
  _baselineAveragingCombo.append("Normal average");
  _baselineAveragingCombo.append("Average of absolute values");
  _baselineAveragingCombo.append("Average of squared values");
  _baselineAveragingCombo.append("Standard deviation");
  _baselineAveragingCombo.set_active(0);
  grid_.attach(_baselineAveragingCombo, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_baIncludeAutosCB, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_baIncludeFlaggedCB, 1, gridRow, 1, 1);
  ++gridRow;

  _baTimeDifference.set_group(_baNoDifference);
  _baFrequencyDifference.set_group(_baNoDifference);
  grid_.attach(_baNoDifference, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_baTimeDifference, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_baFrequencyDifference, 1, gridRow, 1, 1);
  ++gridRow;
  grid_.attach(_baselineAveragingSeparator, 0, gridRow, 2, 1);
  ++gridRow;
}

void OpenDialog::SetSelection(const std::vector<std::string>& selection) {
  _selection = selection;
  _fileList.clear_items();
  for (const std::string& f : _selection) _fileList.append(f);
  _openButton->set_sensitive(!_selection.empty());
}

void OpenDialog::OpenFileDialog() {
  file_dialog_ = Gtk::FileDialog::create();
  file_dialog_->set_title("Open data set in rfigui");
  file_dialog_->select_multiple_folders(
      [this](Glib::RefPtr<Gio::AsyncResult>& result) {
        try {
          auto files = file_dialog_->select_multiple_folders_finish(result);
          onSelectFile(files);
        } catch (const Gtk::DialogError&) {
        }
        file_dialog_.reset();
      });
}

void OpenDialog::onSelectFile(
    const std::vector<std::shared_ptr<Gio::File>>& files) {
  if (!files.empty()) {
    for (const std::shared_ptr<Gio::File>& file : files) {
      _selection.push_back(file->get_path());
      _fileList.append(_selection.back());
    }
    _openButton->set_sensitive(true);
  }
}

void OpenDialog::onUnselectFile() {
  const std::vector<int> unselectList = _fileList.get_selected();
  for (std::vector<int>::const_reverse_iterator i = unselectList.rbegin();
       i != unselectList.rend(); ++i) {
    _selection.erase(_selection.begin() + *i);
  }
  _fileList.clear_items();
  for (const std::string& f : _selection) _fileList.append(f);
  _openButton->set_sensitive(!_selection.empty());
}

void OpenDialog::onFileListSelectionChanged() {
  const bool hasSelection =
      _fileList.get_selection()->count_selected_rows() != 0;
  _unselectButton.set_sensitive(hasSelection);
}

MSOptions OpenDialog::GetOptions() const {
  MSOptions options;
  switch (_readingModeCombo.get_active_row_number()) {
    case 0:
      options.ioMode = DirectReadMode;
      break;
    case 1:
      options.ioMode = ReorderingReadMode;
      break;
    case 2:
      options.ioMode = MemoryReadMode;
      break;
  }
  options.combineSPWs = _combineSPWsCB.get_active();
  options.concatenateFrequency = _concatenateFrequency.get_active();

  options.dataColumnName = _dataColumnCombo.get_active_text();
  if (options.dataColumnName == "other...")
    options.dataColumnName = _otherColumnEntry.get_text();

  if (_intervalCB.get_active()) {
    options.intervalStart = atoi(_intervalStartEntry.get_text().c_str());
    const std::string endStr = _intervalEndEntry.get_text();
    if (!endStr.empty() && endStr != "...")
      options.intervalEnd = atoi(_intervalEndEntry.get_text().c_str());
  }

  options.baselineIntegration.enable = _baselineAveragingCB.get_active();
  options.baselineIntegration.withAutos = _baIncludeAutosCB.get_active();
  options.baselineIntegration.withFlagged = _baIncludeFlaggedCB.get_active();
  options.baselineIntegration.mode = BaselineIntegrationMode::Average;
  switch (_baselineAveragingCombo.get_active_row_number()) {
    case 0:
      options.baselineIntegration.mode = BaselineIntegrationMode::Average;
      break;
    case 1:
      options.baselineIntegration.mode = BaselineIntegrationMode::AverageAbs;
      break;
    case 2:
      options.baselineIntegration.mode = BaselineIntegrationMode::Squared;
      break;
    case 3:
      options.baselineIntegration.mode = BaselineIntegrationMode::Stddev;
      break;
  }
  if (_baTimeDifference.get_active())
    options.baselineIntegration.differencing =
        BaselineIntegrationDifferencing::TimeDifference;
  else if (_baFrequencyDifference.get_active())
    options.baselineIntegration.differencing =
        BaselineIntegrationDifferencing::FrequencyDifference;
  else
    options.baselineIntegration.differencing =
        BaselineIntegrationDifferencing::NoDifference;
  return options;
}
