#include "rfiguimenu.h"

#include <gtkmm/icontheme.h>

RFIGuiMenu::RFIGuiMenu(Gio::ActionMap& actions)
    : actions_(actions),
      _inStrategyMode(false),
      _blockVisualizationSignals(false) {
  std::shared_ptr<Gio::Menu> main_menu = Gio::Menu::create();
  main_menu->append_submenu("_File", makeFileMenu());
  main_menu->append_submenu("_View", makeViewMenu());
  main_menu->append_submenu("_Strategy", makeStrategyMenu());
  main_menu->append_submenu("_Plot", makePlotMenu());
  main_menu->append_submenu("_Browse", makeBrowseMenu());
  main_menu->append_submenu("_Simulate", makeSimulateMenu());
  main_menu->append_submenu("_Data", makeDataMenu());
  _menuBar.set_menu_model(main_menu);

  makeToolbarActions();
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeFileMenu() {
  std::shared_ptr<Gio::Menu> file_menu = Gio::Menu::create();

  std::shared_ptr<Gio::Menu> open_section = Gio::Menu::create();
  addItem(open_section, "_Open...", "open_file", OnOpen);

  recent_files_menu_ = Gio::Menu::create();
  open_section->append_submenu("Open _recent", recent_files_menu_);
  file_menu->append_section(open_section);

  std::shared_ptr<Gio::Menu> save_section = Gio::Menu::create();
  addItem(save_section, "Write baseline flags", "write_baseline_flags",
          OnSaveBaselineFlags);
  addItem(save_section, "Export baseline...", "export_baseline",
          OnExportBaseline);
  addItem(save_section, "Close", "close", OnClose);
  file_menu->append_section(save_section);

  std::shared_ptr<Gio::Menu> quit_section = Gio::Menu::create();
  addItem(quit_section, "_About", "about", OnAbout);
  addItem(quit_section, "_Quit", "quit", OnQuit);
  file_menu->append_section(quit_section);

  return file_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeViewMenu() {
  std::shared_ptr<Gio::Menu> view_menu = Gio::Menu::create();

  std::shared_ptr<Gio::Menu> mode_section = Gio::Menu::create();
  std::shared_ptr<Gio::Menu> view_mode_menu = Gio::Menu::create();

  view_mode_menu->append("Data", "win.set_view_mode(0)");
  view_mode_menu->append("Strategy", "win.set_view_mode(1)");
  view_mode_action_ =
      Gio::SimpleAction::create_radio_integer("set_view_mode", 0);
  view_mode_action_->signal_change_state().connect(
      [&](const Glib::VariantBase& state) {
        int value = static_cast<const Glib::Variant<int>&>(state).get();
        onViewModeChanged(value);
      });
  actions_.add_action(view_mode_action_);

  mode_section->append_submenu("Mode", view_mode_menu);

  view_time_plot_action_ =
      addItem(mode_section, "Time plot", "time_plot", OnViewTimePlot);

  addItem(mode_section, "Plot properties...", "plot_properties",
          OnImagePropertiesPressed);
  view_menu->append_section(mode_section);

  std::shared_ptr<Gio::Menu> flags_section = Gio::Menu::create();
  addItem(flags_section, "Ori. flags", "showoriginalflags", [&]() {
    if (!_blockVisualizationSignals) OnToggleFlags();
  });

  addItem(flags_section, "Alt. flags", "showalternativeflags", [&]() {
    if (!_blockVisualizationSignals) OnToggleFlags();
  });
  view_menu->append_section(flags_section);

  std::shared_ptr<Gio::Menu> zoom_section = Gio::Menu::create();
  addItem(zoom_section, "Zoom _fit", "zoom_fit_best", [&]() {
    if (!_blockVisualizationSignals) OnZoomFit();
  });
  addItem(zoom_section, "Zoom in", "zoom_in", [&]() {
    if (!_blockVisualizationSignals) OnZoomIn();
  });
  addItem(zoom_section, "Zoom out", "zoom_out", [&]() {
    if (!_blockVisualizationSignals) OnZoomOut();
  });
  addItem(zoom_section, "Select zoom...", "select_zoom", [&]() {
    if (!_blockVisualizationSignals) OnZoomSelect();
  });
  view_menu->append_section(zoom_section);

  std::shared_ptr<Gio::Menu> stats_section = Gio::Menu::create();
  addItem(stats_section, "Statistics", "show_statistics", OnShowStats);
  view_menu->append_section(stats_section);

  return view_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeStrategyMenu() {
  std::shared_ptr<Gio::Menu> strategy_menu = Gio::Menu::create();

  std::shared_ptr<Gio::Menu> new_section = Gio::Menu::create();
  std::shared_ptr<Gio::Menu> new_strategy_menu = Gio::Menu::create();
  addItem(new_strategy_menu, "Empty", "empty_strategy", OnStrategyNewEmpty);
  addItem(new_strategy_menu, "Template", "new_strategy_from_template",
          OnStrategyNewTemplate);
  addItem(new_strategy_menu, "Default", "new_default_strategy",
          OnStrategyNewDefault);
  new_section->append_submenu("New", new_strategy_menu);
  strategy_menu->append_section(new_section);

  std::shared_ptr<Gio::Menu> open_section = Gio::Menu::create();
  addItem(open_section, "Open...", "open_strategy", OnStrategyOpen);

  open_defaults_menu_ = Gio::Menu::create();
  open_section->append_submenu("Open _default", open_defaults_menu_);
  strategy_menu->append_section(open_section);

  std::shared_ptr<Gio::Menu> save_section = Gio::Menu::create();
  addItem(save_section, "Save", "save_strategy", OnStrategySave);
  addItem(save_section, "Save as...", "save_strategy_as", OnStrategySaveAs);
  strategy_menu->append_section(save_section);

  // F9
  std::shared_ptr<Gio::Menu> execute_section = Gio::Menu::create();
  execute_strategy_ = addItem(execute_section, "E_xecute strategy",
                              "execute_strategy", OnExecuteLuaStrategy);

  // TODO Functionality is broken and should be removed, but
  // it might be interesting to do a comparison of Lua vs Python strategies
  // first
  execute_python_ = addItem(execute_section, "Execute _python script",
                            "execute_python_strategy", OnExecutePythonStrategy);
  execute_python_->set_enabled(false);
  strategy_menu->append_section(execute_section);
  return strategy_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makePlotMenu() {
  std::shared_ptr<Gio::Menu> plot_menu = Gio::Menu::create();
  std::shared_ptr<Gio::Menu> time_menu = Gio::Menu::create();
  addItem(time_menu, "_Mean (low res)", "plot_lowres_time_mean",
          OnPlotTimeMeanPressed);
  addItem(time_menu, "Mean (_scatter)", "plot_scatter_time_mean",
          OnPlotTimeScatterPressed);
  plot_menu->append_submenu("Time", time_menu);

  std::shared_ptr<Gio::Menu> frequency_menu = Gio::Menu::create();
  addItem(frequency_menu, "_Power (low res)", "plot_lowres_freq_power",
          OnPlotPowerSpectrumPressed);
  addItem(frequency_menu, "_Power (scatter)", "plot_scatter_freq_mean",
          OnPlotFrequencyScatterPressed);
  addItem(frequency_menu, "_Mean", "plot_freq_mean", OnPlotMeanSpectrumPressed);
  addItem(frequency_menu, "_S_um", "plot_freq_sum", OnPlotSumSpectrumPressed);
  plot_menu->append_submenu("Frequency", frequency_menu);

  addItem(plot_menu, "Plot _distribution", "plot_distribution",
          OnPlotDistPressed);
  addItem(plot_menu, "Plot _log-log dist", "plot_loglog_distribution",
          OnPlotLogLogDistPressed);
  addItem(plot_menu, "Plot _singular values", "plot_singular_values",
          OnPlotSingularValuesPressed);
  return plot_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeBrowseMenu() {
  std::shared_ptr<Gio::Menu> browse_menu = Gio::Menu::create();
  std::shared_ptr<Gio::Menu> jump_section = Gio::Menu::create();
  load_previous_ =
      addItem(jump_section, "Previous", "go-previous", OnLoadPrevious);
  load_previous_->set_enabled(false);
  reload_data_ =
      addItem(jump_section, "_Reload", "reload-data", OnReloadPressed);
  reload_data_->set_enabled(false);
  load_next_ = addItem(jump_section, "Next", "go-next", OnLoadPrevious);
  load_next_->set_enabled(false);
  browse_menu->append_section(jump_section);

  // "<control>G"
  std::shared_ptr<Gio::Menu> goto_section = Gio::Menu::create();
  addItem(browse_menu, "_Go to...", "goto", OnGoToPressed);
  browse_menu->append_section(goto_section);

  std::shared_ptr<Gio::Menu> baseline_section = Gio::Menu::create();
  addItem(browse_menu, "Longest baseline", "longest_baseline",
          OnLoadLongestBaselinePressed);
  addItem(browse_menu, "Median baseline", "median_baseline",
          OnLoadMedianBaselinePressed);
  addItem(browse_menu, "Shortest baseline", "shortest_baseline",
          OnLoadShortestBaselinePressed);
  browse_menu->append_section(baseline_section);
  return browse_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeSimulateMenu() {
  std::shared_ptr<Gio::Menu> simulate_menu = Gio::Menu::create();
  addItem(simulate_menu, "Simulate...", "simulate", OnSimulate);

  std::shared_ptr<Gio::Menu> test_sets_menu = Gio::Menu::create();
  addItem(test_sets_menu, "A Full spikes", "open_testset_a", OnOpenTestSetA);
  addItem(test_sets_menu, "B Half spikes", "open_testset_b", OnOpenTestSetB);
  addItem(test_sets_menu, "C Varying spikes", "open_testset_c", OnOpenTestSetC);
  addItem(test_sets_menu, "D 3 srcs + spikes", "open_testset_d",
          OnOpenTestSetD);
  addItem(test_sets_menu, "E 5 srcs + spikes", "open_testset_e",
          OnOpenTestSetE);
  addItem(test_sets_menu, "F 5 srcs + spikes", "open_testset_f",
          OnOpenTestSetF);
  addItem(test_sets_menu, "G Test set G", "open_testset_g", OnOpenTestSetG);
  addItem(test_sets_menu, "H filtered srcs + spikes", "open_testset_h",
          OnOpenTestSetH);
  addItem(test_sets_menu, "Noise", "open_testset_n", OnOpenTestSetNoise);
  simulate_menu->append_submenu("Open _testset", test_sets_menu);

  std::shared_ptr<Gio::Menu> modify_menu = Gio::Menu::create();
  addItem(modify_menu, "Static fringe", "add_static_fringe", OnAddStaticFringe);
  addItem(modify_menu, "Static 1 sigma fringe", "add_1sigma_fringe",
          OnAdd1SigmaFringe);
  addItem(modify_menu, "Set to 1", "set_to_one", OnSetToOne);
  addItem(modify_menu, "Set to i", "set_to_i", OnSetToI);
  addItem(modify_menu, "Set to 1+i", "set_to_1_plus_i", OnSetToOnePlusI);
  addItem(modify_menu, "Add correlator fault", "add_correlator_fault",
          OnAddCorrelatorFault);
  addItem(modify_menu, "Add NaNs", "add_nans", OnAddNaNs);
  addItem(modify_menu, "Multiply data...", "multiply_data", OnMultiplyData);
  simulate_menu->append_submenu("Modify", modify_menu);

  return simulate_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeDataMenu() {
  std::shared_ptr<Gio::Menu> data_menu = Gio::Menu::create();

  std::shared_ptr<Gio::Menu> view_section = Gio::Menu::create();
  addItem(view_section, "Select current view", "select_current_view",
          OnVisualizedToOriginalPressed);
  data_menu->append_section(view_section);

  std::shared_ptr<Gio::Menu> convert_section = Gio::Menu::create();

  std::shared_ptr<Gio::Menu> complex_menu = Gio::Menu::create();
  addItem(complex_menu, "_Real part", "get_real_part", OnKeepRealPressed);
  addItem(complex_menu, "_Imaginary part", "get_imaginary_part",
          OnKeepImaginaryPressed);
  addItem(complex_menu, "_Phase part", "get_phase_part", OnKeepPhasePressed);
  addItem(complex_menu, "_Unroll phase", "unroll_phase",
          OnUnrollPhaseButtonPressed);
  convert_section->append_submenu("Select _complex", complex_menu);

  std::shared_ptr<Gio::Menu> stokes_menu = Gio::Menu::create();
  addItem(stokes_menu, "Stokes _I", "get_stokes_i", OnKeepStokesIPressed);
  addItem(stokes_menu, "Stokes _Q", "get_stokes_q", OnKeepStokesQPressed);
  addItem(stokes_menu, "Stokes _U", "get_stokes_u", OnKeepStokesUPressed);
  addItem(stokes_menu, "Stokes _V", "get_stokes_v", OnKeepStokesVPressed);
  convert_section->append_submenu("Select _stokes", complex_menu);

  std::shared_ptr<Gio::Menu> circular_menu = Gio::Menu::create();
  addItem(circular_menu, "_RR", "get_rr", OnKeepRRPressed);
  addItem(circular_menu, "RL", "get_rl", OnKeepRLPressed);
  addItem(circular_menu, "LR", "get_lr", OnKeepLRPressed);
  addItem(circular_menu, "_LL", "get_ll", OnKeepLLPressed);
  convert_section->append_submenu("Select circ_ular", circular_menu);

  std::shared_ptr<Gio::Menu> linear_menu = Gio::Menu::create();
  addItem(linear_menu, "_XX", "get_xx", OnKeepXXPressed);
  addItem(linear_menu, "XY", "get_xy", OnKeepXYPressed);
  addItem(linear_menu, "YX", "get_yx", OnKeepYXPressed);
  addItem(linear_menu, "_YY", "get_yy", OnKeepYYPressed);
  convert_section->append_submenu("Select _linear", linear_menu);

  std::shared_ptr<Gio::Menu> segmentation_menu = makeSegmentationMenu();
  convert_section->append_submenu("_Segmentation", segmentation_menu);
  data_menu->append_section(convert_section);

  std::shared_ptr<Gio::Menu> store_section = Gio::Menu::create();
  addItem(store_section, "Store", "data_store", OnStoreData);
  addItem(store_section, "Recall", "data_recall", OnRecallData);
  addItem(store_section, "Subtract from mem", "data_memory_subtract",
          OnSubtractDataFromMem);
  data_menu->append_section(store_section);

  std::shared_ptr<Gio::Menu> averaging_section = Gio::Menu::create();
  addItem(averaging_section, "Temporal averaging...", "temporal_averaging",
          OnTemporalAveragingPressed);
  addItem(averaging_section, "Spectral averaging...", "spectral_averaging",
          OnSpectralAveragingPressed);
  addItem(averaging_section, "Prediction residual", "prediction_residual",
          OnPredictResidual);
  data_menu->append_section(averaging_section);

  std::shared_ptr<Gio::Menu> flags_section = Gio::Menu::create();
  addItem(flags_section, "Clear ori. flags", "clear_original_flags",
          OnClearOriginalFlagsPressed);
  addItem(flags_section, "Clear alt. flags", "clear_alternative_flags",
          OnClearAltFlagsPressed);
  data_menu->append_section(flags_section);

  return data_menu;
}

std::shared_ptr<Gio::Menu> RFIGuiMenu::makeSegmentationMenu() {
  std::shared_ptr<Gio::Menu> segmentation_menu = Gio::Menu::create();
  addItem(segmentation_menu, "Segment", "segment", OnSegment);
  addItem(segmentation_menu, "Cluster", "cluster", OnCluster);
  addItem(segmentation_menu, "Classify", "classify", OnClassify);
  addItem(segmentation_menu, "Remove small segments", "remove_small_segments",
          OnRemoveSmallSegments);
  return segmentation_menu;
}

void RFIGuiMenu::makeToolbarActions() {
  auto AddSeperator = [this]() {
    auto separator =
        Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::VERTICAL);
    separator->set_margin_start(10);
    separator->set_margin_end(10);
    _toolbar.append(*separator);
  };

  addTool(_tbOpen, OnOpen, "Open", "Open a file or directory on disk",
          "document-open");

  AddSeperator();

  addTool(
      _tbModeData, [&]() { onTBModeData(); }, "Data",
      "Switch to data-viewing mode.", "spectrum");
  _tbModeData.set_active(true);
  addTool(
      _tbModeStrategy, [&]() { onTBModeStrategy(); }, "Strategy",
      "Switch to the strategy editor.", "lua-editor");
  _tbModeStrategy.set_group(_tbModeData);

  AddSeperator();

  // Gtk::AccelKey("F9")
  addTool(_tbExecuteStrategy, OnExecuteLuaStrategy, "E_xecute strategy",
          "Run the Lua strategy script. This will not write to the opened set. "
          "The flagging results are displayed in the plot as yellow "
          "('alternative') flag mask.",
          "system-run");
  // F3
  addTool(_tbOriginalFlags, OnToggleFlags, "Ori flags",
          "Display the first flag mask on top of the visibilities. These flags "
          "are displayed in purple and indicate the flags as they originally "
          "were stored in the measurement set.",
          "showoriginalflags");
  // F4
  addTool(_tbAlternativeFlags, OnToggleFlags, "Alt flags",
          "Display the second flag mask on top of the visibilities. These "
          "flags are displayed in yellow and indicate flags found by running "
          "the strategy.",
          "showalternativeflags");

  addTool(_tbSelectVisualization, "Change visualization",
          "Switch visualization", "showoriginalvisibilities");

  _tfVisualizationMenu = Gio::Menu::create();
  _tbSelectVisualization.set_menu_model(_tfVisualizationMenu);

  AddSeperator();

  // F6
  addTool(_tbPrevious, OnLoadPrevious, "Previous",
          "Load and display the previous baseline. Normally, this steps from "
          "the baseline between antennas (i) and (j) to (i) and (j-1).",
          "go-previous");
  _tbPrevious.set_sensitive(false);
  // F5
  addTool(_tbReload, OnReloadPressed, "Reload",
          "Reload the currently displayed baseline. This will reset the purple "
          "flags to the measurement set flags, and clear the yellow flags.",
          "view-refresh");
  _tbReload.set_sensitive(false);
  // F7
  addTool(_tbNext, OnLoadNext, "Next",
          "Load and display the next baseline. Normally, this steps from the "
          "baseline between antennas (i) and (j) to (i) and (j+1).",
          "go-next");
  _tbNext.set_sensitive(false);

  AddSeperator();

  // <control>0
  addTool(_tbZoomFit, OnZoomFit, "Fit", "Zoom fit", "zoom-fit-best");
  addTool(_tbZoomIn, OnZoomIn, "+", "Zoom in", "zoom-in");
  addTool(_tbZoomOut, OnZoomOut, "-", "Zoom out", "zoom-out");

  AddSeperator();

  auto sig = [&]() {
    if (!_blockVisualizationSignals) OnTogglePolarizations();
  };
  addTool(_tbDisplayPP, sig, "PP",
          "Display the PP polarization. Depending on the polarization "
          "configuration of the measurement set, this will show XX or RR",
          "showpp");
  addTool(_tbDisplayPQ, sig, "PQ",
          "Display the PQ polarization. Depending on the polarization "
          "configuration of the measurement set, this will show XY or RL",
          "showpq");
  addTool(_tbDisplayQP, sig, "QP",
          "Display the QP polarization. Depending on the polarization "
          "configuration of the measurement set, this will show YX or LR",
          "showqp");
  addTool(_tbDisplayQQ, sig, "QQ",
          "Display the QQ polarization. Depending on the polarization "
          "configuration of the measurement set, this will show YY or LL",
          "showqq");
  _tbDisplayPP.set_active(true);
  _tbDisplayQQ.set_active(true);
}

void RFIGuiMenu::EnableRunButtons(bool enable) {
  // TODO disable file menu

  execute_strategy_->set_enabled(enable);
  // execute_python_->set_enabled(enable); TODO permanently disabled
  _tbOpen.set_sensitive(enable);
  _tbExecuteStrategy.set_sensitive(enable);
}

void RFIGuiMenu::SetRecentFiles(const std::vector<std::string>& recent_files) {
  recent_files_menu_->remove_all();
  const size_t n = std::min(recent_files.size(), size_t(10));
  for (size_t i = 0; i != n; ++i) {
    addItem(recent_files_menu_, recent_files[i].c_str(),
            "open_recent_" + std::to_string(i), [&, i]() { OnOpenRecent(i); });
  }
}

void RFIGuiMenu::SetStrategyDefaults(
    const std::vector<std::string>& strategy_defaults) {
  for (size_t i = 0; i != strategy_defaults.size(); ++i) {
    const std::string& label = strategy_defaults[i];
    addItem(open_defaults_menu_, label.c_str(),
            "open_default_" + std::to_string(i),
            [&, label]() { OnStrategyOpenDefault(label); });
  }
}

void RFIGuiMenu::onTBModeData() {
  if (_inStrategyMode && _tbModeData.get_active()) {
    _inStrategyMode = false;
    view_mode_action_->set_state(Glib::Variant<int>::create(0));
    OnViewData();
  }
}

void RFIGuiMenu::onTBModeStrategy() {
  if (!_inStrategyMode && _tbModeStrategy.get_active()) {
    _inStrategyMode = true;
    view_mode_action_->set_state(Glib::Variant<int>::create(1));
    OnViewStrategy();
  }
}

void RFIGuiMenu::onViewModeChanged(int state) {
  if (_inStrategyMode && state == 0) {
    _inStrategyMode = false;
    _tbModeData.set_active(true);
    OnViewData();
  }
  if (!_inStrategyMode && state == 1) {
    _inStrategyMode = true;
    _tbModeStrategy.set_active(true);
    OnViewStrategy();
  }
}
