/***************************************************************************
**
**  This file is part of GeopsyGui.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2005-09-13
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "WindowingParameterWidget.h"

#include "FilterParameterWidget.h"

namespace GeopsyGui {

/*
 *  Constructs a WindowingParameterWidget as a child of 'parent', with the
 *  name 'name' and widget flags set to 'f'.
 */
WindowingParameterWidget::WindowingParameterWidget(QWidget *parent, Qt::WindowFlags f)
    : QWidget(parent, f)
{
  TRACE;
  setupUi(this);
  _nComponents=0;

  // signals and slots connections
  connect(badSampleThreshold, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(badSampleTol, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(badSampleGap, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(overlapValue, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );

  connect(filtApplyList, SIGNAL(itemChanged( QListWidgetItem * )), this, SIGNAL(parametersChanged()) );
  connect(filtStaLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(filtLtaLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(filtMinStaLta, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(filtMaxStaLta, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );

  connect(rawApplyList, SIGNAL(itemChanged( QListWidgetItem * )), this, SIGNAL(parametersChanged()) );
  connect(staLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(ltaLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(minStaLta, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(maxStaLta, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );

  connect(windowLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(windowMaxLength, SIGNAL(valueChanged( const QString& )), this, SIGNAL(parametersChanged()) );
  connect(filterWidget, SIGNAL(parametersChanged()), this, SIGNAL(parametersChanged()));

  windowLength->setRange(0.0, std::numeric_limits<double>::infinity());
  windowMaxLength->setRange(0.0, std::numeric_limits<double>::infinity());
  badSampleTol->setRange(0.0, std::numeric_limits<double>::infinity());
  badSampleGap->setRange(0.0, std::numeric_limits<double>::infinity());
  badSampleThreshold->setRange(0.0, std::numeric_limits<double>::infinity());

  connect(seismicEventDelay, SIGNAL(valueChanged(const QString&)), this, SIGNAL(parametersChanged()) );
}

void WindowingParameterWidget::createSelectMenu(QToolButton * select)
{
  // Init the select windows menu
  QAction * a;
  QMenu * m=new QMenu(this);
  m->setTitle(tr("Select windows"));
  m->setTearOffEnabled (true);
  connect(select, SIGNAL(clicked()), this, SIGNAL(autoWindows()));
  select->setMenu(m);

  a=new QAction(tr("Auto"), this);
  a->setWhatsThis(tr("Select time windows automatically using all parameters."));
  connect(a, SIGNAL(triggered()), this, SIGNAL(autoWindows()));
  m->addAction(a);

  _addWindowsAction=new QAction(tr("Add"), this);
  _addWindowsAction->setCheckable(true);
  _addWindowsAction->setWhatsThis(tr("If switched on, you can select windows with the mouse by clicking and dragging on the wanted signal. "
                      "Only the \"length\" and the \"overlap\" parameters are considered."));
  connect(_addWindowsAction, SIGNAL(triggered()), this, SLOT(toggleAddWindows()));
  m->addAction(_addWindowsAction);

  _removeWindowsAction=new QAction(tr("Remove"), this);
  _removeWindowsAction->setCheckable(true);
  _removeWindowsAction->setWhatsThis(tr("If switched on you can remove windows with the mouse by clicking and dragging on the existing windows."));
  connect(_removeWindowsAction, SIGNAL(triggered()), this, SLOT(toggleRemoveWindows()));
  m->addAction(_removeWindowsAction);

  a=new QAction(tr("Inverse"), this);
  a->setWhatsThis(tr("Select time windows inside the time ranges not included in the current time window list. "
                     "Only the \"length\" and the \"overlap\" parameters are considered."));
  connect(a, SIGNAL(triggered()), this, SIGNAL(inverseWindows()));
  m->addAction(a);

  a=new QAction(tr("Clear"), this);
  a->setWhatsThis(tr("Remove windows"));
  connect(a, SIGNAL(triggered()), this, SIGNAL(clearWindows()));
  m->addAction(a);

  a=new QAction(tr("Load"), this);
  a->setWhatsThis(tr("Add time windows defined in a text file. The .log file of previous processings can be used. "
                     "You can build your own file using .log files as a template. Section \"### Time Windows ###\" is mandatory."));
  connect(a, SIGNAL(triggered()), this, SIGNAL(loadWindows()));
  m->addAction(a);
}

void WindowingParameterWidget::toggleAddWindows()
{
  TRACE;
  bool b=_addWindowsAction->isChecked();
  if(b) {
    if(_removeWindowsAction->isChecked()) {
      emit endRemoveWindowsManually();
      _removeWindowsAction->setChecked(false);
    }
  }
  emit beginAddWindowsManually();
}

void WindowingParameterWidget::toggleRemoveWindows()
{
  TRACE;
  bool b=_removeWindowsAction->isChecked();
  if(b) {
    if(_addWindowsAction->isChecked()) {
      emit endAddWindowsManually();
      _addWindowsAction->setChecked(false);
    }
  }
  emit beginRemoveWindowsManually();
}

void WindowingParameterWidget::clear()
{
  TRACE;
  _nComponents=0;
  rawApplyList->clear();
  if(isFiltered) {
    filtApplyList->clear();
  }
}

/*!
  Add component selectors in apply list, this function cannot be called after addStation()
*/
void WindowingParameterWidget::addComponent(QString name)
{
  TRACE;
  QListWidgetItem * item;
  item=new QListWidgetItem(name);
  item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
  item->setCheckState(Qt::Checked);
  rawApplyList->addItem(item);
  if(isFiltered) {
    item=new QListWidgetItem(name);
    item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    item->setCheckState(Qt::Checked);
    filtApplyList->addItem(item);
  }
  _nComponents++;
}

/*!
  Add component selectors in apply list, this function cannot be called before addComponent()
*/
void WindowingParameterWidget::addStation(QString name)
{
  TRACE;
  ASSERT(_nComponents>0);
  QListWidgetItem * item;
  if(_nComponents==rawApplyList->count()) {
    item=new QListWidgetItem( "---" );
    item->setFlags(nullptr);
    rawApplyList->addItem(item);
    if(isFiltered) {
      item=new QListWidgetItem( "---" );
      item->setFlags(nullptr);
      filtApplyList->addItem(item);
    }
  }
  item=new QListWidgetItem(name);
  item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
  item->setCheckState(Qt::Checked);
  rawApplyList->addItem(item);
  if(isFiltered) {
    item=new QListWidgetItem(name);
    item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
    item->setCheckState(Qt::Checked);
    filtApplyList->addItem(item);
  }
}

void WindowingParameterWidget::removeFilterOption()
{
  TRACE;
  tabs->removeTab(2);
  delete tabs->widget(2);
  tabs->removeTab(3);
  delete tabs->widget(3);
  delete isFiltered;
  isFiltered=nullptr;
}

void WindowingParameterWidget::removeFreqDepOption()
{
  TRACE;
  windowLengthType->removeItem(2);
}

void WindowingParameterWidget::updateAllFields()
{
  TRACE;
  on_isFiltered_toggled();
  on_isTolerance_toggled();
  on_isGap_toggled();
  on_isOverlap_toggled();
  on_isBadSample_toggled();
  on_badSampleRelative_currentIndexChanged(0);
  filterWidget->updateAllFields();
  on_windowLengthType_currentIndexChanged(0);
  on_isSeismicEventTrigger_toggled();
}

void WindowingParameterWidget::on_isRaw_toggled()
{
  TRACE;
  bool b=isRaw->isChecked();
  staLength->setEnabled(b);
  ltaLength->setEnabled(b);
  minStaLta->setEnabled(b);
  maxStaLta->setEnabled(b);
  setRawEnabled(isBadSample->isChecked() || b);
  isRawTab->setChecked(b);
  if(b) { // by default select all components
    int n=rawApplyList->count();
    for(int i=0;i < n;i++ ) {
      if(i!=_nComponents)
        rawApplyList->item(i) ->setCheckState(Qt::Checked);
    }
  }
  emit parametersChanged();
}

void WindowingParameterWidget::on_isFiltered_toggled()
{
  TRACE;
  if( !isFiltered) return ;
  bool b=isFiltered->isChecked();
  filtStaLength->setEnabled(b);
  filtLtaLength->setEnabled(b);
  filtMinStaLta->setEnabled(b);
  filtMaxStaLta->setEnabled(b);
  setFilteredEnabled(isBadSample->isChecked() || b);
  isFilteredTab->setChecked(b);
  if(b) { // by default select all components
    int n=filtApplyList->count();
    for(int i=0;i < n;i++ ) {
      if(i!=_nComponents)
        filtApplyList->item(i) ->setCheckState(Qt::Checked);
    }
  }
  emit parametersChanged();
}

void WindowingParameterWidget::setRawEnabled(bool b)
{
  TRACE;
  rawApplyList->setEnabled(b);
}

void WindowingParameterWidget::setFilteredEnabled(bool b)
{
  TRACE;
  if( !isFiltered) return ;
  filtApplyList->setEnabled(b);
  tabs->widget(2) ->setEnabled(b);
}

void WindowingParameterWidget::on_isRawTab_toggled()
{
  TRACE;
  isRaw->setChecked(isRawTab->isChecked());
}

void WindowingParameterWidget::on_isFilteredTab_toggled()
{
  TRACE;
  if( !isFiltered) return ;
  isFiltered->setChecked(isFilteredTab->isChecked());
}

void WindowingParameterWidget::on_isTolerance_toggled()
{
  TRACE;
  if(isTolerance->isChecked()) {
    badSampleTol->setEnabled(true);
    isGap->setChecked(false);
  } else {
    badSampleTol->setEnabled(false);
  }
  emit parametersChanged();
}

void WindowingParameterWidget::on_isGap_toggled()
{
  TRACE;
  if(isGap->isChecked()) {
    badSampleGap->setEnabled(true);
    isTolerance->setChecked(false);
  } else {
    badSampleGap->setEnabled(false);
  }
  emit parametersChanged();
}

void WindowingParameterWidget::on_isOverlap_toggled()
{
  TRACE;
  overlapValue->setEnabled(isOverlap->isChecked());
  emit parametersChanged();
}

void WindowingParameterWidget::on_isBadSample_toggled()
{
  TRACE;
  bool b=isBadSample->isChecked();
  badSampleThreshold->setEnabled(b);
  setRawEnabled(isRaw->isChecked() || b);
  if(isFiltered) {
    setFilteredEnabled(isFiltered->isChecked() || b);
    if(b) { // by default unselect all components
      int n=filtApplyList->count();
      for(int i=0;i < n;i++ )
        filtApplyList->item(i) ->setCheckState(Qt::Checked);
    }
  }
  emit parametersChanged();
}

void WindowingParameterWidget::on_isSeismicEventTrigger_toggled()
{
  TRACE;
  bool b=isSeismicEventTrigger->isChecked();
  seismicEventDelay->setEnabled(b);
  emit parametersChanged();
}

void WindowingParameterWidget::on_badSampleRelative_currentIndexChanged(int)
{
  TRACE;
  if(badSampleRelative->currentIndex()==0) {
    badSampleThreshold->setSuffix(" %");
    badSampleThreshold->setMaximum(100.0);
  } else {
    badSampleThreshold->setSuffix(QString());
    badSampleThreshold->setMaximum(std::numeric_limits<double>::infinity());
  }
  emit parametersChanged();
}

void WindowingParameterWidget::on_windowLengthType_currentIndexChanged(int)
{
  TRACE;
  switch (windowLengthType->currentIndex()) {
  case 0:
    windowLength->show();
    windowLengthLabel->hide();
    windowMaxLength->hide();
    windowLength->setSuffix(tr( " s." ));
    break;
  case 1:
    windowLength->show();
    windowLengthLabel->setText( "to" );
    windowLengthLabel->show();
    windowMaxLength->show();
    windowLength->setSuffix(tr( " s." ));
    windowMaxLength->setSuffix(tr( " s." ));
    break;
  case 2:
    windowLength->hide();
    windowLengthLabel->show();
    windowLengthLabel->setText(tr( "include" ));
    windowMaxLength->show();
    windowLength->setSuffix(tr( " s." ));
    windowMaxLength->setSuffix(tr( " T" ));
    break;
  }
  emit parametersChanged();
}

void WindowingParameterWidget::setLength(WindowingParameters::LengthType t, double min, double max)
{
  TRACE;
  if(max==0.0 && t==WindowingParameters::AtLeast) {
    t=WindowingParameters::Exactly;
  }
  switch (t) {
  case WindowingParameters::Exactly:
    windowLengthType->setCurrentIndex(0);
    windowLength->setValue(min);
    break;
  case WindowingParameters::AtLeast:
    windowLengthType->setCurrentIndex(1);
    windowLength->setValue(min);
    windowLength->setValue(max);
    break;
  case WindowingParameters::FrequencyDependent:
    windowLengthType->setCurrentIndex(2);
    windowLength->setValue(min);
    break;
  }
}

void WindowingParameterWidget::getParameters(WindowingParameters& param) const
{
  TRACE;
  switch(windowLengthType->currentIndex()) {
  case 0:
    param.setLength(windowLength->value());
    break;
  case 1:
    param.setLength(windowLength->value(),
                     windowMaxLength->value());
    break;
  case 2:
    param.setPeriodCount(windowMaxLength->value());
    break;
  }

  if(isOverlap->isChecked()) {
    param.setOverlap(overlapValue->value());
  } else {
    param.setOverlap(0.0);
  }

  if(isTolerance->isChecked()) {
    param.setBadSampleTolerance(badSampleTol->value());
  } else {
    param.setBadSampleTolerance(0.0);
  }

  if(isGap->isChecked()) {
    param.setBadSampleGap(badSampleGap->value());
  } else {
    param.setBadSampleGap(0.0);
  }

  if(isBadSample->isChecked()) {
    if(badSampleRelative->currentIndex()==0) {
      param.setBadSampleThresholdType(WindowingParameters::RelativeSampleThreshold);
      param.setBadSampleThreshold(badSampleThreshold->value()*0.01);
    } else {
      param.setBadSampleThresholdType(WindowingParameters::AbsoluteSampleThreshold);
      param.setBadSampleThreshold(badSampleThreshold->value());
    }
  } else {
    param.setBadSampleThresholdType(WindowingParameters::NoSampleThreshold);
  }

  int deltaComponents=_nComponents > 0 ? (_nComponents+1) : 0;
  param.setComponentCount(_nComponents);
  int nStations=rawApplyList->count()-deltaComponents;
  param.setStationCount(nStations);

  WindowingParameters::ShortLongTermAverage slta;
  for(int i=0; i<_nComponents; i++) {
    param.setRawComponent(i, rawApplyList->item(i) ->checkState()==Qt::Checked);
  }
  for(int i=0; i<nStations; i++) {
    param.setRawStation(i, rawApplyList->item(i+deltaComponents)->checkState()==Qt::Checked);
  }
  slta.enabled=isRaw->isChecked();
  slta.shortLength=staLength->value();
  slta.longLength=ltaLength->value();
  slta.minimumThreshold=minStaLta->value();
  slta.maximumThreshold=maxStaLta->value();
  param.setRawSignalAntiTrigger(slta);

  if(isFiltered) {
    ASSERT(filtApplyList->count()==nStations+deltaComponents);
    for(int i=0; i<_nComponents; i++) {
      param.setFilterComponent(i, filtApplyList->item(i) ->checkState()==Qt::Checked);
    }
    int n=filtApplyList->count()-deltaComponents;
    for(int i=0; i<n; i++) {
      param.setFilterStation(i, filtApplyList->item(i+deltaComponents)->checkState()==Qt::Checked);
    }
    slta.enabled=isFiltered->isChecked();
    slta.shortLength=filtStaLength->value();
    slta.longLength=filtLtaLength->value();
    slta.minimumThreshold=filtMinStaLta->value();
    slta.maximumThreshold=filtMaxStaLta->value();
    param.setFilterSignalAntiTrigger(slta);
    filterWidget->getParameters(param.filter());
  } else {
    for(int i=0; i<_nComponents; i++) {
      param.setFilterComponent(i, false);
    }
    for(int i=0; i<nStations; i++) {
      param.setFilterStation(i, false);
    }
    slta.enabled=false;
    param.setFilterSignalAntiTrigger(slta);
  }

  param.setSeismicEventTrigger(isSeismicEventTrigger->isChecked());
  param.setSeismicEventDelay(seismicEventDelay->value());
}

void WindowingParameterWidget::setParameters(const WindowingParameters& param)
{
  TRACE;
  switch(param.lengthType()) {
  case 0:
    windowLengthType->setCurrentIndex(0);
    windowLength->setValue(param.minimumLength(0.0));
    break;
  case 1:
    windowLengthType->setCurrentIndex(1);
    windowLength->setValue(param.minimumLength(0.0));
    windowMaxLength->setValue(param.maximumLength(0.0));
    break;
  case 2:
    windowLengthType->setCurrentIndex(2);
    windowMaxLength->setValue(param.periodCount());
    break;
  }

  isOverlap->setChecked(param.overlap()>0.0);
  overlapValue->setValue(param.overlap());

  isTolerance->setChecked(param.badSampleTolerance()>0.0);
  badSampleTol->setValue(param.badSampleTolerance());

  isGap->setChecked(param.badSampleGap()>0.0);
  badSampleGap->setValue(param.badSampleGap());

  isBadSample->setChecked(param.badSampleThresholdType()!=WindowingParameters::NoSampleThreshold);
  badSampleRelative->setCurrentIndex(param.badSampleThresholdType()==WindowingParameters::RelativeSampleThreshold ? 0 : 1);
  badSampleThreshold->setValue(param.badSampleThreshold());

  int deltaComponents=_nComponents > 0 ? (_nComponents+1) : 0;
  int nStations=rawApplyList->count()-deltaComponents;
  for(int i=0; i<_nComponents; i++) {
    rawApplyList->item(i)->setCheckState(param.rawComponent(i) ? Qt::Checked : Qt::Unchecked);
  }
  for(int i=0; i<nStations; i++) {
    rawApplyList->item(i+deltaComponents)->setCheckState(param.rawStation(i) ? Qt::Checked : Qt::Unchecked);
  }
  isRaw->setChecked(param.rawSignalAntiTrigger().enabled);
  staLength->setValue(param.rawSignalAntiTrigger().shortLength);
  ltaLength->setValue(param.rawSignalAntiTrigger().longLength);
  minStaLta->setValue(param.rawSignalAntiTrigger().minimumThreshold);
  maxStaLta->setValue(param.rawSignalAntiTrigger().maximumThreshold);

  if(isFiltered) {
    ASSERT(filtApplyList->count()==nStations+deltaComponents);
    for(int i=0; i<_nComponents; i++) {
      filtApplyList->item(i)->setCheckState(param.filterComponent(i) ? Qt::Checked : Qt::Unchecked);
    }
    int n=filtApplyList->count()-deltaComponents;
    for(int i=0; i<n; i++) {
      filtApplyList->item(i+deltaComponents)->setCheckState(param.filterStation(i) ? Qt::Checked : Qt::Unchecked);
    }
    isFiltered->setChecked(param.filterSignalAntiTrigger().enabled);
    filtStaLength->setValue(param.filterSignalAntiTrigger().shortLength);
    filtLtaLength->setValue(param.filterSignalAntiTrigger().longLength);
    filtMinStaLta->setValue(param.filterSignalAntiTrigger().minimumThreshold);
    filtMaxStaLta->setValue(param.filterSignalAntiTrigger().maximumThreshold);
    filterWidget->setParameters(param.filter());
  }

  isSeismicEventTrigger->setChecked(param.seismicEventTrigger());
  seismicEventDelay->setValue(param.seismicEventDelay());
}

} // namespace GeopsyGui
