#include "complexplaneplotwindow.h"

#include <set>
#include <sstream>

#include <gtkmm/messagedialog.h>

#include "../structures/antennainfo.h"
#include "../structures/date.h"
#include "../structures/image2d.h"

#include "../util/rfiplots.h"

#include "../algorithms/combinatorialthresholder.h"
#include "../algorithms/fringestoppingfitter.h"

#include "../plot/plotmanager.h"

#include "../imaging/uvimager.h"

#include "rfiguiwindow.h"

ComplexPlanePlotWindow::ComplexPlanePlotWindow(RFIGuiWindow &rfiGuiWindow,
                                               PlotManager &plotManager)
    : _rfiGuiWindow(rfiGuiWindow),
      _plotManager(plotManager),
      _detailsFrame("Location details"),
      _detailsLabel(),
      _xPositionLabel("time start position:"),
      _yPositionLabel("frequency start position:"),
      _lengthLabel("time length:"),
      _ySumLengthLabel("Frequency averaging size:"),
      _xPositionScale(Gtk::ORIENTATION_HORIZONTAL),
      _yPositionScale(Gtk::ORIENTATION_HORIZONTAL),
      _lengthScale(Gtk::ORIENTATION_HORIZONTAL),
      _ySumLengthScale(Gtk::ORIENTATION_HORIZONTAL),
      _realVersusImaginaryButton("Real versus imaginary"),
      _timeVersusRealButton("Time versus real"),
      _allValuesButton("All values"),
      _unmaskedValuesButton("Unmasked values"),
      _maskedValuesButton("Masked values"),
      _fittedValuesButton("Fitted values (constant freq)"),
      _individualSampleFitButton("Channel fitted values (varying freq)"),
      _fringeFitButton("Fringe fitted (varying freq)"),
      _dynamicFringeFitButton("Dynamic fringe fitted (varying freq+amp)"),
      _plotButton("Plot"),
      _xMax(_rfiGuiWindow.GetOriginalData().ImageWidth()),
      _yMax(_rfiGuiWindow.GetOriginalData().ImageHeight()) {
  _detailsFrame.add(_detailsBox);
  _detailsBox.show();

  _detailsBox.pack_start(_detailsLabel);
  _detailsLabel.set_line_wrap(true);
  _detailsLabel.set_max_width_chars(40);
  _detailsLabel.show();

  _mainBox.pack_start(_detailsFrame);
  _detailsFrame.show();

  _mainBox.pack_start(_xPositionLabel);
  _xPositionLabel.show();
  _xPositionScale.set_range(0.0, _rfiGuiWindow.GetOriginalData().ImageWidth());
  _xPositionScale.signal_value_changed().connect(
      sigc::mem_fun(*this, &ComplexPlanePlotWindow::onTimeStartChanged));
  _mainBox.pack_start(_xPositionScale);
  _xPositionScale.show();

  _mainBox.pack_start(_yPositionLabel);
  _yPositionLabel.show();
  _yPositionScale.set_range(0.0, _rfiGuiWindow.GetOriginalData().ImageHeight());
  _yPositionScale.signal_value_changed().connect(
      sigc::mem_fun(*this, &ComplexPlanePlotWindow::onFreqChanged));
  _mainBox.pack_start(_yPositionScale);
  _yPositionScale.show();

  _mainBox.pack_start(_lengthLabel);
  _lengthLabel.show();
  _lengthScale.set_range(1.0, _rfiGuiWindow.GetOriginalData().ImageWidth());
  _lengthScale.signal_value_changed().connect(
      sigc::mem_fun(*this, &ComplexPlanePlotWindow::onTimeDurationChanged));
  _mainBox.pack_start(_lengthScale);
  _lengthScale.show();

  _mainBox.pack_start(_ySumLengthLabel);
  _ySumLengthLabel.show();
  _ySumLengthScale.set_range(1.0,
                             _rfiGuiWindow.GetOriginalData().ImageHeight());
  _ySumLengthScale.signal_value_changed().connect(
      sigc::mem_fun(*this, &ComplexPlanePlotWindow::onFreqSizeChanged));
  _mainBox.pack_start(_ySumLengthScale);
  _ySumLengthScale.show();

  _mainBox.pack_start(_realVersusImaginaryButton);
  _realVersusImaginaryButton.show();
  Gtk::RadioButtonGroup group = _realVersusImaginaryButton.get_group();
  _timeVersusRealButton.set_group(group);
  _realVersusImaginaryButton.set_active(true);
  _mainBox.pack_start(_timeVersusRealButton);
  _timeVersusRealButton.show();

  _mainBox.pack_start(_allValuesButton);
  _allValuesButton.set_active(true);
  _allValuesButton.show();

  _mainBox.pack_start(_maskedValuesButton);
  _maskedValuesButton.show();

  _mainBox.pack_start(_unmaskedValuesButton);
  _unmaskedValuesButton.show();

  _mainBox.pack_start(_fittedValuesButton);
  _fittedValuesButton.set_active(true);
  _fittedValuesButton.show();

  _mainBox.pack_start(_individualSampleFitButton);
  _individualSampleFitButton.show();

  _mainBox.pack_start(_fringeFitButton);
  _fringeFitButton.show();

  _mainBox.pack_start(_dynamicFringeFitButton);
  _dynamicFringeFitButton.show();

  _plotButton.signal_clicked().connect(
      sigc::mem_fun(*this, &ComplexPlanePlotWindow::onPlotPressed));
  _buttonBox.pack_start(_plotButton);
  _plotButton.show();

  _mainBox.pack_start(_buttonBox);
  _buttonBox.show();

  add(_mainBox);
  _mainBox.show();

  _observationTimes = _rfiGuiWindow.SelectedMetaData()->ObservationTimes();

  setDetailsLabel();
}

ComplexPlanePlotWindow::~ComplexPlanePlotWindow() {}

void ComplexPlanePlotWindow::onPlotPressed() {
  if (_rfiGuiWindow.HasImage()) {
    try {
      Plot2D &plot = _plotManager.NewPlot2D("Complex plane");
      size_t x = (size_t)_xPositionScale.get_value();
      size_t y = (size_t)_yPositionScale.get_value();
      size_t length = (size_t)_lengthScale.get_value();
      size_t avgSize = (size_t)_ySumLengthScale.get_value();
      bool realVersusImaginary = _realVersusImaginaryButton.get_active();
      const TimeFrequencyData &data = _rfiGuiWindow.GetActiveData();

      if (_allValuesButton.get_active()) {
        Plot2DPointSet *pointSet;
        if (realVersusImaginary)
          pointSet = &plot.StartLine("Data", Plot2DPointSet::DrawPoints);
        else
          pointSet = &plot.StartLine("Data (real)", Plot2DPointSet::DrawPoints);
        Mask2DPtr mask =
            Mask2D::CreateSetMaskPtr<false>(_rfiGuiWindow.AltMask()->Width(),
                                            _rfiGuiWindow.AltMask()->Height());
        RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                       mask, realVersusImaginary, false);

        if (!realVersusImaginary) {
          pointSet =
              &plot.StartLine("Data (imaginary)", Plot2DPointSet::DrawPoints);
          RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                         mask, realVersusImaginary, true);
        }
      }

      if (_unmaskedValuesButton.get_active()) {
        Plot2DPointSet *pointSet = &plot.StartLine("Without RFI");
        RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                       _rfiGuiWindow.AltMask(),
                                       realVersusImaginary, false);
        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Without RFI (I)");
          RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                         _rfiGuiWindow.AltMask(),
                                         realVersusImaginary, true);
        }
      }

      if (_maskedValuesButton.get_active()) {
        Plot2DPointSet *pointSet = &plot.StartLine("Only RFI");
        Mask2DPtr mask = Mask2D::MakePtr(*_rfiGuiWindow.AltMask());
        mask->Invert();
        RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                       mask, realVersusImaginary, false);
        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Only RFI (I)");
          RFIPlots::MakeComplexPlanePlot(*pointSet, data, x, length, y, avgSize,
                                         mask, realVersusImaginary, true);
        }
      }

      if (_fittedValuesButton.get_active()) {
        Plot2DPointSet *pointSet;
        if (realVersusImaginary)
          pointSet = &plot.StartLine("Fit");
        else
          pointSet = &plot.StartLine("Fit (real)");
        size_t middleY = (2 * y + avgSize) / 2;
        Baseline baseline(_rfiGuiWindow.SelectedMetaData()->Antenna1(),
                          _rfiGuiWindow.SelectedMetaData()->Antenna2());
        long double fringeCount = UVImager::GetFringeCount(
            x, x + length, middleY, _rfiGuiWindow.SelectedMetaData());
        long double fringeFrequency = fringeCount / length;
        Mask2DPtr mask =
            Mask2D::CreateSetMaskPtr<false>(_rfiGuiWindow.AltMask()->Width(),
                                            _rfiGuiWindow.AltMask()->Height());
        RFIPlots::MakeFittedComplexPlot(*pointSet, data, x, length, y, avgSize,
                                        mask, fringeFrequency,
                                        realVersusImaginary, false);
        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Fit (imaginary)");
          RFIPlots::MakeFittedComplexPlot(*pointSet, data, x, length, y,
                                          avgSize, mask, fringeFrequency,
                                          realVersusImaginary, true);
        }
      }

      if (_individualSampleFitButton.get_active()) {
        FringeStoppingFitter fitter;
        fitter.Initialize(data);
        fitter.SetFitChannelsIndividually(true);
        fitter.SetFringesToConsider(1.0L);
        fitter.SetMaxWindowSize(256);
        fitter.SetReturnFittedValue(true);
        fitter.SetReturnMeanValue(false);

        fitter.SetMetaData(_rfiGuiWindow.SelectedMetaData());
        fitter.PerformStaticFrequencyFitOnOneChannel(y);

        Plot2DPointSet *pointSet;
        if (realVersusImaginary)
          pointSet = &plot.StartLine("Fit");
        else
          pointSet = &plot.StartLine("Fit (real)");
        Mask2DPtr mask =
            Mask2D::CreateSetMaskPtr<false>(_rfiGuiWindow.AltMask()->Width(),
                                            _rfiGuiWindow.AltMask()->Height());
        RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                       length, y, avgSize, mask,
                                       realVersusImaginary, false);

        fitter.SetReturnFittedValue(false);
        fitter.SetReturnMeanValue(true);
        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Fit (imaginary)");
          RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                         length, y, avgSize, mask,
                                         realVersusImaginary, true);
        }

        fitter.PerformStaticFrequencyFitOnOneChannel(y);

        pointSet = &plot.StartLine("Center");
        RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                       length, y, avgSize, mask,
                                       realVersusImaginary, false);

        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Center (I)");
          RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                         length, y, avgSize, mask,
                                         realVersusImaginary, true);
        }
      }

      if (_fringeFitButton.get_active() ||
          _dynamicFringeFitButton.get_active()) {
        /*FringeStoppingFitter fitter;
        Image2DPtr zero = Image2D::CreateZeroImagePtr(data.ImageWidth(),
        data.ImageHeight()); Image2DPtr ones =
        Image2D::CreateZeroImagePtr(data.ImageWidth(), data.ImageHeight());
        for(size_t yi=0;yi<ones->Height();++yi)
                for(size_t xi=0;xi<ones->Width();++xi)
                        ones->SetValue(xi, yi, 1.0L);
        TimeFrequencyData data(StokesIPolarisation, ones, zero);
        fitter.Initialize(data);
        fitter.SetFitChannelsIndividually(true);

        fitter.SetMetaData(_rfiGuiWindow.TimeFrequencyMetaData());
        fitter.PerformFringeStop();

        plot.StartLine("Fringe rotation");
        Mask2DPtr mask =
        Mask2D::CreateSetMaskPtr<false>(_rfiGuiWindow.AltMask()->Width(),
        _rfiGuiWindow.AltMask()->Height()); RFIPlots::MakeComplexPlanePlot(plot,
        fitter.Background(), x, length, y, avgSize, mask, realVersusImaginary,
        false);

        if(!realVersusImaginary)
        {
                plot.StartLine("Fringe rotation (I)");
                RFIPlots::MakeComplexPlanePlot(plot, fitter.Background(), x,
        length, y, avgSize, mask, realVersusImaginary, true);
        }*/

        FringeStoppingFitter fitter;
        fitter.Initialize(data);

        fitter.SetMetaData(_rfiGuiWindow.SelectedMetaData());
        // fitter.PerformFringeStop();
        fitter.SetReturnFittedValue(true);
        if (_dynamicFringeFitButton.get_active())
          fitter.PerformDynamicFrequencyFit(y, y + avgSize, 200);
        else
          fitter.PerformDynamicFrequencyFit(y, y + avgSize);

        Plot2DPointSet *pointSet;
        if (realVersusImaginary)
          pointSet = &plot.StartLine("Fit");
        else
          pointSet = &plot.StartLine("Fit (real)");
        Mask2DPtr mask =
            Mask2D::CreateSetMaskPtr<false>(_rfiGuiWindow.AltMask()->Width(),
                                            _rfiGuiWindow.AltMask()->Height());
        RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                       length, y, avgSize, mask,
                                       realVersusImaginary, false);

        if (!realVersusImaginary) {
          pointSet = &plot.StartLine("Fit (imaginary)");
          RFIPlots::MakeComplexPlanePlot(*pointSet, fitter.Background(), x,
                                         length, y, avgSize, mask,
                                         realVersusImaginary, true);
        }
      }

      _plotManager.Update();

    } catch (std::exception &e) {
      Gtk::MessageDialog dialog(*this, e.what(), false, Gtk::MESSAGE_ERROR);
      dialog.run();
    }
  }
}

void ComplexPlanePlotWindow::setDetailsLabel() {
  size_t x = (size_t)_xPositionScale.get_value();
  size_t y = (size_t)_yPositionScale.get_value();
  size_t length = (size_t)_lengthScale.get_value();
  size_t avgSize = (size_t)_ySumLengthScale.get_value();
  size_t middleY = (2 * y + avgSize) / 2;
  TimeFrequencyMetaDataCPtr metaData = _rfiGuiWindow.SelectedMetaData();

  double timeStart = _observationTimes[x];
  double deltaTime;
  if (_observationTimes.size() > 1)
    deltaTime = _observationTimes[1] - _observationTimes[0];
  else
    deltaTime = 1.0;
  long double frequency = metaData->Band().channels[middleY].frequencyHz;
  Baseline baseline(metaData->Antenna1(), metaData->Antenna2());
  long double delayRA = metaData->Field().delayDirectionRA;
  long double delayDec = metaData->Field().delayDirectionDec;
  long double intFringeFreq =
      UVImager::GetFringeCount(x, x + length, y, metaData);
  long double midFringeFreq = UVImager::GetFringeStopFrequency(
      (x * 2 + length) / 2, baseline, delayRA, delayDec, frequency, metaData);

  std::stringstream s;
  s << "Start time: " << Date::AipsMJDToString(timeStart) << std::endl
    << "Frequency: " << frequency / 1000000.0L << "Mhz" << std::endl
    << "Baseline: " << baseline.Distance() << "m" << std::endl
    << "Delay direction: " << delayRA << "RA, " << delayDec << "dec."
    << std::endl
    << "(=" << RightAscension::ToString(delayRA) << " RA, "
    << Declination::ToString(delayDec) << " dec.)" << std::endl
    << "Mid fringe stopping freq: " << midFringeFreq << "(Hz)" << std::endl
    << "Fringe count: " << intFringeFreq << std::endl
    << "Fringe length: " << 1.0L / intFringeFreq << "(s)" << std::endl
    << "Time step: " << deltaTime << "(s)" << std::endl
    << "Samples/fringe: " << (1.0L / (deltaTime * intFringeFreq)) << std::endl
    << "Fringes in domain: " << intFringeFreq << std::endl;

  _detailsLabel.set_text(s.str());
}
