/***************************************************************************
**
**  This file is part of geopsyarray.
**
**  geopsyarray 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.
**
**  geopsyarray 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: 2004-09-09
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <DinverDCCore.h>
#include <GeopsyGui.h>
#include <ArrayCore.h>
#include <QGpCoreWave.h>

#include "SPACToolWidget.h"

#define MinCol 0
#define MaxCol 1
#define PairsNumCol 2
#define ColorCol 3

#define _arrayMap static_cast<ArrayMap*>(_childrenList[0])
#define _coArrayMap static_cast<ArrayMap*>(_childrenList[1])

SPACToolWidget::SPACToolWidget(QWidget * parent) :
    AbstractToolWidget(parent, 2)
{
  TRACE;
  setupUi(this);

  delete _tool;
  _tool=new SPACTool;
  connect(tool(), SIGNAL(finished()), this, SLOT(finish()));

  freqSamp->setUnit(tr(" Hz"));
  freqSamp->setPrecision(2);
  freqSamp->setSingleStep(0.25);
  freqSamp->setAdmissibleRange(1e-99, std::numeric_limits<double>::infinity());
  winParam->removeFilterOption();
  winParam->setLength(WindowingParameters::FrequencyDependent, 50.0);

  connect(startBut, SIGNAL(clicked()), this, SLOT(start()) );
  connect(stopBut, SIGNAL(clicked()), this, SLOT(stop()) );
  connect(testBut, SIGNAL(clicked()), this, SLOT(parametersChanged()));
  connect(loadParam, SIGNAL(clicked()), this, SLOT(loadLogParameters()));
  connect(freqSamp, SIGNAL(parametersChanged()), this, SLOT(numFreqChanged()));

  connect(timeLimits, SIGNAL(parametersChanged()), this, SLOT(parametersChanged()));
  connect(selectMinimumDurationEdit, SIGNAL(valueChanged(const QString&)), this, SLOT(parametersChanged()));
  connect(winParam, SIGNAL(parametersChanged()), this, SLOT(parametersChanged()));
  connect(testFrequency, SIGNAL(valueChanged(const QString&)), this, SLOT(parametersChanged()));

  connect(blockAveraging, SIGNAL(parametersChanged()), this, SLOT(parametersChanged()));

  connect(freqBandwidthEdit, SIGNAL(valueChanged(const QString&)), this, SLOT(parametersChanged()));
  connect(oversamplingEdit, SIGNAL(valueChanged(const QString&)), this, SLOT(parametersChanged()));

  _outputFileChecked=false;
}

SPACToolWidget::~SPACToolWidget()
{
  TRACE;
}

bool SPACToolWidget::setSubPool(SubSignalPool * subPool)
{
  TRACE;
  if(AbstractToolWidget::setSubPool(subPool)) {
    timeLimits->setSubPool(subPool);
    if(!_arrayMap || !_coArrayMap) {
      _childrenList[0]=new ArrayMap;
      _childrenList[1]=new ArrayMap;
      _arrayMap->QWidget::resize(300, 300); // default size
      _arrayMap->setObjectName("arrayMap");
      _arrayMap->setWindowTitle(tr("%1 - array map").arg(subPool->name()));
      Settings::getRect(_arrayMap);
      _coArrayMap->QWidget::resize(300, 300); // default size
      _coArrayMap->setObjectName("coArrayMap");
      _coArrayMap->setWindowTitle(tr("%1 - co-array map").arg(subPool->name()));
      _coArrayMap->mapLayer()->setShowNames(false);
      Settings::getRect(_coArrayMap);
      ringEdit->setCoArrayGraph(_coArrayMap);
      ArraySelection array(tool()->array());
      ringEdit->setArray(&array);
      GeopsyGuiEngine::instance()->addSubWindow(this, _arrayMap)->setUserClosable(false);
      GeopsyGuiEngine::instance()->addSubWindow(this, _coArrayMap)->setUserClosable(false);
    }
    setArrayMap();
    setCoArrayMap();
    setTimeWindowLayer(&_winList);
    return true;
  } else {
    return false;
  }
}

void SPACToolWidget::updateAllFields()
{
  TRACE;
  winParam->updateAllFields();
  timeLimits->updateAllFields();
  blockAveraging->updateAllFields();
  freqSamp->updateAllFields();
  numFreqChanged();
  on_freqScroll_valueChanged(freqScroll->value());
  if(ringEdit->ringCount()==0) {
    ringEdit->autoRings();
  }
}

/*!
  Just computes and shows time window selection.
  TODO: plot autocorr for active block, real and imaginary
*/
void SPACToolWidget::parametersChanged()
{
  if(timeWindowLayer() && testBut->isChecked()) {
    SPACTaskManager manager(tool()->array());
    ArraySelection array(tool()->array());
    ArrayTimeWindows timeWindows(&array, manager.keep(),
                                 static_cast<SPACParameters *>(parameters()));
    LayerLocker ll(timeWindowLayer());
    timeWindows.setFrequency(testFrequency->value());
    _winList=timeWindows.list();
    timeWindowLayer()->deepUpdate();
  }
}


void SPACToolWidget::setArrayMap()
{
  TRACE;
  int n=tool()->array()->count();
  NameLineLayer * plot=_arrayMap->mapLayer();
  LayerLocker ll(plot);
  NameLine * l=static_cast<NameLine *>(_arrayMap->line(0));
  Curve<NamedPoint>& c=l->curve();
  c.resize(n);
  for(int i=0; i<n; i++) {
    NamedPoint& p=c[ i ];
    const StationSignals * stat=tool()->array()->at(i);
    p=stat->coordinates();
    p.setName(stat->name());
  }
  _arrayMap->setLimitsArray();
}

void SPACToolWidget::setCoArrayMap()
{
  TRACE;
  ArraySelection array(tool()->array());
  SPACCrossSpectrum crossSpectrum(&array, static_cast<SPACParameters *>(parameters()));

  NameLineLayer * plot=_coArrayMap->mapLayer();
  LayerLocker ll(plot);
  NameLine * l=static_cast<NameLine *>(_coArrayMap->line(0));
  Curve<NamedPoint>& c=l->curve();
  c.resize(crossSpectrum.stationPairCount());
  for(int i=c.count()-1; i>=0; i--) {
    c[i]=crossSpectrum.stationPair(i).coArray();
  }
  _coArrayMap->setLimitsCoArray();
}

/*!
  Add all stations to time window layer
*/
void SPACToolWidget::setTimeWindowLayer(TimeWindowList * winList)
{
  TRACE;
  if(timeWindowLayer()) {
    const ArraySelection& array=tool()->array();
    int nComp=array.array()->nComponents();
    int n=array.count();
    timeWindowLayer()->clearTimeWindows();
    for(int i=0; i<n; i++) {
      for(int iComp=0; iComp<nComp; iComp++ ) {
        const SubSignalPool subPool=array.at(i)->originals(iComp);
        for(SubSignalPool::const_iterator itSig=subPool.begin(); itSig!=subPool.end(); itSig++) {
          timeWindowLayer()->addTimeWindows(*itSig, winList);
        }
      }
    }
  }
}

/*!
*/
void SPACToolWidget::start()
{
  TRACE;
  if(subPoolLocked()) {
    return;
  }
  // Get parameters from dialog box
  SPACParameters * param=static_cast<SPACParameters *>(parameters());
  if(!param) {
    return;
  }
  param->setOutputBaseName(outputFileName());
  if(!param->outputBaseName().isEmpty()) {
    lockSubPool();
    if(tool()->setParameters(*param)) {
      detailedStatus->setLoop(tool()->loop());
      setRunning(true);
      tool()->start();
    } else {
      unlockSubPool();
    }
  }
  delete param;

}

/*!
  Same as in FKToolWidget
*/
void SPACToolWidget::finish()
{
  unlockSubPool();
  SPACParameters * param=static_cast<SPACParameters *>(parameters());
  tool()->results()->commitStatistics();
  tool()->results()->reportRejects();
  tool()->results()->save(param->outputBaseName());
  delete param;
  setRunning(false);
}

/*!
  Same as in FKToolWidget
*/
void SPACToolWidget::stop()
{
  TRACE;
  tool()->stop();
}

/*!
*/
AbstractParameters * SPACToolWidget::parameters(AbstractParameters * param) const
{
  TRACE;
  if(!param) {
    param=new SPACParameters;
  }
  getParameters(*static_cast<SPACParameters *>(param));
  return param;
}

void SPACToolWidget::setParameters(const AbstractParameters * param)
{
  TRACE;
  setParameters(*static_cast<const SPACParameters *>(param));
}

void SPACToolWidget::getParameters(SPACParameters& param) const
{
  TRACE;
  timeLimits->getParameters(param.timeLimits());
  param.setSelectDurationFactor(0.01*selectMinimumDurationEdit->value());
  winParam->getParameters(param.windowing());

  int nRings=ringEdit->rings->rowCount();
  for(int i=0; i<nRings; i++) {
    param.addRing(ringEdit->rings->item(i, RingEditor_MinCol)->text().toDouble(),
                  ringEdit->rings->item(i, RingEditor_MaxCol)->text().toDouble());
  }

  freqSamp->getParameters(param.frequencySampling());
  blockAveraging->getParameters(param.blockAveraging());

  param.setfrequencyBandwidth(freqBandwidthEdit->value());
  param.setOversamplingFactor(oversamplingEdit->value());

  param.setMaximumImaginary(maxImaginaryPart->value());

  param.setOutputBaseName(outputFileNameEdit->text());
}

void SPACToolWidget::setParameters(const SPACParameters& param)
{
  TRACE;
  timeLimits->setParameters(param.timeLimits());
  selectMinimumDurationEdit->setValue(100.0*param.selectDurationFactor());
  winParam->setParameters(param.windowing());

  ringEdit->clear();
  for(int i=0; i<param.ringCount(); i++) {
    const AutocorrRing& r=param.ring(i);
    ringEdit->addRing(r.minRadius(), r.maxRadius());
  }

  freqSamp->setParameters(param.frequencySampling());
  blockAveraging->setParameters(param.blockAveraging());

  freqBandwidthEdit->setValue(param.frequencyBandwidth());
  oversamplingEdit->setValue(param.oversamplingFactor());

  maxImaginaryPart->setValue(param.maximumImaginary());

  outputFileNameEdit->setText(param.outputBaseName());
}

void SPACToolWidget::on_outputFileNameBrowse_clicked()
{
  TRACE;
  QString fileName=Message::getSaveFileName(tr("Output autocorr file"),
                                            tr("SPAC file (*.target)" ),
                                            outputFileNameEdit->text());
  if(!fileName.isEmpty()) {
    outputFileNameEdit->setText(fileName);
    _outputFileChecked=true;
  }
}

void SPACToolWidget::on_outputFileNameEdit_textChanged(QString)
{
  TRACE;
  _outputFileChecked=false;
}

QString SPACToolWidget::outputFileName()
{
  TRACE;
  if(outputFileNameEdit->text().isEmpty()) {
    on_outputFileNameBrowse_clicked();
    if(outputFileNameEdit->text().isEmpty()) {
      return QString();
    }
  }
  if(!_outputFileChecked) {
    QFileInfo fi(outputFileNameEdit->text());
    if(fi.exists()) {
      if(Message::question(MSG_ID, tr("Output autocorr file"),
                           tr("File '%1' already exist. Do you want to overwrite?").arg(fi.fileName()),
                           Message::no(), Message::yes())==Message::Answer0) {
        return QString();
      }
    }
  }
  return outputFileNameEdit->text();
}

/*!
*/
void SPACToolWidget::numFreqChanged()
{
  TRACE;
  SamplingParameters param;
  freqSamp->getParameters(param);
  freqScroll->setMaximum(param.count()-1);
}

/*!
  Same as in FKToolWidget
*/
void SPACToolWidget::on_freqScroll_valueChanged(int)
{
  TRACE;
  SamplingParameters param;
  freqSamp->getParameters(param);
  testFrequency->setValue(param.value(freqScroll->value()));
}

/*!
  Same as in FKToolWidget
*/
void SPACToolWidget::setRunning(bool r, const QString& message)
{
  TRACE;
  startBut->setEnabled(!r);
  stopBut->setEnabled(r);
  if(r) {
    if(message.isEmpty()) {
      mainStatus->setText(tr("Running..."));
    } else {
      mainStatus->setText(message);
    }
  } else {
    mainStatus->setText(tr("Not running"));
  }
}
