/***************************************************************************
**
**  This file is part of HVGui.
**
**  HVGui is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  HVGui is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2020-05-25
**  Copyright: 2020
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include <GeopsyGui.h>

#include "CurveResultSheet.h"
#include "CurveResultWidget.h"
#include "PeakEditor.h"
#include "StationPeakModel.h"
#include "StudentTest.h"
#include "StatisticResults.h"
#include "CurveResultSheetOptions.h"
#include "FilterTimeWindows.h"

namespace HVGui {

  /*!
    \class CurveResultSheet CurveResultSheet.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  CurveResultSheet::CurveResultSheet(QWidget * parent)
    : AbstractResultSheet(parent)
  {
    TRACE;
  }

  /*!
    Description of destructor still missing
  */
  CurveResultSheet::~CurveResultSheet()
  {
    TRACE;
  }

  AbstractResultWidget * CurveResultSheet::createWidget()
  {
    TRACE;
    CurveResultWidget * w=new CurveResultWidget;
    connect(w, SIGNAL(peakChanged()), this, SIGNAL(peakChanged()));
    return w;
  }

  void CurveResultSheet::addActions()
  {
    TRACE;
    AbstractResultSheet::addActions();
    QAction * a;

    menuFormat->addSeparator();

    a=new QAction(tr("&Options"), this);
    connect(a, SIGNAL(triggered()), this, SLOT(options()));
    menuFormat->addAction(a);

    a=new QAction(tr("&Load results"), this);
    a->setStatusTip(tr("Plot results previously saved"));
    QObject::connect(a, SIGNAL(triggered()), this, SLOT(loadResults()));
    menuTools->insertAction(saveResultsAction, a);

    menuTools->addSeparator();

    a=new QAction(tr("&Edit peaks"), this);
    a->setStatusTip(tr( "Do you think that the automatically computed peak frequency is not correct? Fix it ..."));
    connect(a, SIGNAL(triggered()), this, SLOT(editPeaks()) );
    menuTools->addAction(a);

    a=new QAction(tr("&Statistical comparison"), this);
    a->setStatusTip(tr("Use statistical tests to quantify how well two spectral curves compare."));
    connect(a, SIGNAL(triggered()), this, SLOT(studentTest()) );
    menuTools->addAction(a);

    a=new QAction(tr("&Filter windows"), this);
    a->setStatusTip(tr("Filter time windows according to curve features."));
    connect(a, SIGNAL(triggered()), this, SLOT(filterWindows()));
    menuTools->addAction(a);
  }

  void CurveResultSheet::setLimits(double maxAmplitude)
  {
    TRACE;
    const CurveResults * res=static_cast<const CurveResults *>(firstValidResult());
    if(res) {
      const SamplingParameters& r=res->frequencySampling();
      for(QList<AbstractResultWidget *>::iterator it=_results.begin(); it!=_results.end(); it++) {
        static_cast<CurveResultWidget *>(*it)->setLimits(r, maxAmplitude);
      }
    }
  }

  void CurveResultSheet::options()
  {
    TRACE;
    if(_stations.isEmpty()) return;
    static const QString title=tr("Options");
    if(!selectAll(title) ) return;
    Dialog * d=new Dialog(this);
    CurveResultSheetOptions * optionWidget=new CurveResultSheetOptions;
    d->setMainWidget(optionWidget);
    Settings::getWidget(optionWidget);
    optionWidget->setVisibleLayers(static_cast<CurveResultWidget *>(_results.first()));
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(optionWidget);
      for(QList<AbstractResultWidget *>::iterator it=_results.begin(); it!=_results.end(); it++) {
        CurveResultWidget * w=static_cast<CurveResultWidget *>(*it);
        if(w->isSelected()) {
          optionWidget->visibleLayers(w);
          w->deepUpdate();
        }
      }
    }
    delete d;
  }

  void CurveResultSheet::loadResults(QStringList fileNames)
  {
    TRACE;
    if(_stations.isEmpty()) return;
    if(fileNames.count()>0) {
      // Check that either all stations exist or none of them exist. If not, ask user
      enum LoadType {Unknown, NewStations, ExistingStations, Mixed};
      LoadType loadType=Unknown;
      QString newStation, existingStation;
      for(QStringList::Iterator it=fileNames.begin(); it!=fileNames.end(); ++it) {
        QFileInfo fi(*it);
        AbstractStation * s=station(fi.baseName());
        switch(loadType) {
        case Unknown:
          loadType=s ? ExistingStations : NewStations;
          break;
        case NewStations:
          if(s) {
            loadType=Mixed;
          }
          break;
        case ExistingStations:
          if(!s) {
            loadType=Mixed;
          }
          break;
        case Mixed:
          break;
        }
        if(s) {
          existingStation+=s->name()+"\n";
        } else {
          newStation+=fi.baseName()+"\n";
        }
      }
      if(loadType==Mixed) {
        switch(Message::warning(MSG_ID, tr("Loading results"),
                                tr("Some of the selected files are not part of the current processing.\n\n"
                                   "Station(s) in the current processing:\n"
                                   "%1\n"
                                   "New station(s):\n"
                                   "%2\n"
                                   "Do you want to add them as new plots?").arg(existingStation).arg(newStation),
                                Message::yes(), Message::no(), Message::cancel())) {
        case Message::Answer0:
          break;
        case Message::Answer1:
          loadType=ExistingStations;
          break;
        case Message::Answer2:
          return;
        }
      }
      double y=_sheet.printBottom();
      double maximumY=static_cast<CurveResultWidget *>(_results.last())->maximumY();
      for(QStringList::Iterator it=fileNames.begin(); it!=fileNames.end(); ++it) {
        QFileInfo fi(*it);
        AbstractStation * s=station(fi.baseName());
        if(s) {
          // TODO: take result index into account
          static_cast<CurveResults *>(s->results(0))->load(*it);
        } else if(loadType!=ExistingStations) {
          CurveResults * res=new CurveResults(nullptr);
          if(res->load(*it)) {
            res->setName(fi.fileName());
            CurveResultWidget * w=static_cast<CurveResultWidget *>(addWidget());
            w->setPrintTop(y);
            y+=w->printHeight();
            w->setValues(res);
            w->setLimits(res->frequencySampling(), maximumY);
          } else {
            delete res;
          }
        }
      }
      emit peakChanged();
    }
  }

  void CurveResultSheet::editPeaks()
  {
    TRACE;
    static const QString title=tr("Edit peaks");
    if(!selectAll(title)) return;

    Dialog * d=new Dialog(this);
    PeakEditor * e=new PeakEditor(this);
    d->setMainWidget(e, Dialog::Close);
    QList<AbstractResultWidget *> resAbs=selectedResults();
    QList<CurveResults *> res;
    for(QList<AbstractResultWidget *>::iterator it=resAbs.begin(); it!=resAbs.end(); it++) {
      res.append(static_cast<CurveResults *>((*it)->values()));
    }
    StationPeakModel * m=new StationPeakModel(res, e);
    e->setModel(m);
    connect(e, SIGNAL(touched()), this, SLOT(setPeaks()));
    Settings::getRect(d, "PeakEditor");
    if(d->exec()==QDialog::Accepted) {
      Settings::setRect(d, "PeakEditor");
    }
    emit peakChanged();
    delete d;
  }

  void CurveResultSheet::setPeaks()
  {
    TRACE;
    for(QList<AbstractResultWidget *>::iterator it=_results.begin(); it!=_results.end(); it++) {
      if((*it)->isSelected()) {
        (*it)->showValues();
      }
    }
  }

  void CurveResultSheet::studentTest()
  {
    TRACE;
    StudentTest * d=new StudentTest(this);
    // Gather all similar AbstractResultSheet (with the same Y axis title)
    QWidgetList windows=GeopsyGuiEngine::instance()->subWindowList(this);
    StudentTest::ResultLists curves;
    for(int i=0; i<windows.count(); ++i) {
      AbstractResultSheet * w=qobject_cast<AbstractResultSheet *>(windows.at(i));
      if(w && w->valueTitle()==valueTitle()) {
        QList<CurveResults *> resList;
        for(QList<AbstractResultWidget *>::iterator it=_results.begin();it!=_results.end();it++) {
          resList.append(static_cast<CurveResults *>((*it)->values()));
        }
        curves.insert(w->windowTitle(), resList);
      }
    }
    d->init(curves);

    Settings::getWidget(d);
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d);
      const CurveResults * r1=d->firstCurve();
      const CurveResults * r2=d->secondCurve();
      if(r1 && r2) {
        if(r1==r2) {
          Message::warning(MSG_ID, tr("Student test"), tr("Identical stations selected."));
        } else {
          if(r1->average().sameSampling(r2->average())) {
            StatisticResults::studentTest(this, r1, r2, d->significance(), d->makeUp());
          } else {
            Message::warning(MSG_ID, tr("Student test"), tr("Selected curves have not a compatible frequency sampling."));
          }
        }
      } else {
        Message::warning(MSG_ID, tr("Student test"), tr("Missing at least one station."));
      }
    }
    delete d;
  }

  void CurveResultSheet::filterWindows()
  {
    TRACE;
    if(_stations.isEmpty()) return;
    static const QString title=tr("Filter windows");
    if(!selectAll(title)) return;

    FilterTimeWindows * d=new FilterTimeWindows(this);
    Settings::getWidget(d);
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d);
      QList<AbstractResultWidget *> res=selectedResults();
      for(QList<AbstractResultWidget *>::iterator it=res.begin(); it!=res.end(); it++) {
        AbstractResultWidget * w=*it;
        CurveResults * r=static_cast<CurveResults *>(w->values());
        r->blackWindows(Rect(d->minFrequency->value(), d->minAmplitude->value(),
                        d->maxFrequency->value(), d->maxAmplitude->value()));
        w->deepUpdate();
      }
    }
    delete d;
  }

} // namespace HVGui

