/***************************************************************************
**
**  This file is part of hvtfa.
**
**  hvtfa 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.
**
**  hvtfa 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: 2008-02-07
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**    Miriam Kristekova (Geophysical Institute, Academy of Sciences, Bratislava, Slovak Republic)
**
***************************************************************************/

#include <GeopsyCore.h>
#include "HVTFALoop.h"
#include "HVTFAParameters.h"

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

  Full description of class still missing
*/

LoopWorker * HVTFALoop::newWorker()
{
  HVTFALoopWorker * t=new HVTFALoopWorker;
  t->setLoop(this);
  return t;
}

/*!
  Opens .max file in WriteOnly or Append.
*/
bool HVTFALoop::openMaxFile(bool createNew)
{
  TRACE;
  if(createNew) {
    if(_fdotmax.open(QIODevice::WriteOnly)) {
      QTextStream s(&_fdotmax);
      s << "# File generated by Geopsy, H/V TFA processing\n"
           "# seconds from start | cfreq | H/V | AmpZ | AmpH | Delay\n";
      return true;
    } else {
      return false;
    }
  } else {
    return _fdotmax.open(QIODevice::Append);
  }
}

/*!
  Return the index of the next maximum even a local one starting from \a iStart. Return -1 is it reach the end of signal
  without finding a maximum. \a iEnd is the last usable sample index, generally the number of samples -1 - coneOfInfluence
*/
inline int HVTFALoopWorker::nextMaximum(const Curve<Point2D>& curve, int iStart, int iEnd)
{
  TRACE;
  int i;
  for(i=iStart+1; i<iEnd && curve.at(i).y()<curve.at(i-1).y(); i++);
  for(i++; i<iEnd && curve.at(i).y()>curve.at(i-1).y(); i++);
  if(i>=iEnd) return -1; else return i-1;
}

void HVTFALoopWorker::run(int index)
{
  TRACE;
  const HVTFAParameters& tfaParam=*_loop->_param;
  SignalDatabase * db=_loop->_database;
  GeopsyCoreEngine::instance()->increaseProgressValue(db);
  double currentFrequency=tfaParam.frequencySampling().value(index);
  double currentPeriod=1.0/currentFrequency;
  MorletParameters wavelet=tfaParam.wavelet();
  wavelet.setFi(currentFrequency);
  int rayleighDelay=qRound(0.25*currentPeriod*_loop->_sampFreq);
  // According to Miriam Kristekova, the cone of influence is better defined with 3*DeltaT
  int iConeOfInfluence=(int)floor(3.0*wavelet.deltaT()*_loop->_sampFreq + 0.5);
  ComplexSignal * components[3];
  for(int i=0; i<3; i++) {
    GeopsyCoreEngine::instance()->showMessage(db, tr("Station %1 : Morlet wavelet convolution at %2 Hz for component %3")
                            .arg(_loop->_stationName).arg(currentFrequency).arg(i));
    components[i]=_loop->_signals[i]->morletWavelet(wavelet);
  }
  // Combine horizontal components
  double df=1.0/_loop->_signals[0]->duration();
  Curve<Point2D> verticalComponent=components[0]->abs(df);
  components[1]->square();
  components[2]->square();
  components[1]->add(components[2]);
  components[1]->sqrt();
  Curve<Point2D> horizontalComponent=components[1]->abs(df);
  if(terminated()) {
    for(int i=0; i<3; i++ ) {
      delete components[i];
    }
    return;
  }
  // TODO: stack is not used... remains of some aborted developments
  // Probably worth to have a look at this stack... more or less like Raydec?
  ComplexSignal * verticalStack=new ComplexSignal(200);
  ComplexSignal * horizontalStack=new ComplexSignal(200);
  verticalStack->initValues(Complex(), 0, 199);
  horizontalStack->initValues(Complex(), 0, 199);
  // Scan for all maxima even the local ones on the vertical component
  // Exclude all peak pick on the signal edges. The cone of influence is related to the time resolution
  // given by 4*deltaTi where deltaTi=w0/(2*pi*fi)*sqrt(m)
  int iVMax=iConeOfInfluence;
  if(iVMax<rayleighDelay) iVMax=rayleighDelay;
  int iEnd=verticalComponent.count()-1-iConeOfInfluence;
  double vMax, hMaxPos, hMaxNeg, tMax;
  //printf("Frequency %lf\n",currentFrequency);
  while((iVMax=nextMaximum(verticalComponent, iVMax, iEnd))>-1) {
    if(iVMax>100 && iVMax<verticalComponent.count()-100) {
      verticalStack->add(components[0], iVMax-100, 0, 200);
      horizontalStack->add(components[1], iVMax-100, 0, 200);
      //verticalStack->abs()->debugPrint();
      //horizontalStack->abs()->debugPrint();
    }
    vMax=verticalComponent.at(iVMax).y();
    // For each maximum on vertical component, seek for a maximum on the horizontal one
    hMaxNeg=horizontalComponent.at(iVMax-rayleighDelay).y();
    hMaxPos=horizontalComponent.at(iVMax+rayleighDelay).y();
    tMax=iVMax*_loop->_samplingPeriod;
    _loop->_dotmaxMutex.lock();
    QTextStream * s=new QTextStream(&_loop->_fdotmax);
    (*s) << tMax << " "
         << currentFrequency << " "
         << hMaxNeg/vMax << " "
         << vMax << " "
         << hMaxNeg << "\n"
         << tMax << " "
         << currentFrequency << " "
         << hMaxPos/vMax << " "
         << vMax << " "
         << hMaxPos << "\n";
    delete s;
    _loop->_dotmaxMutex.unlock();
  }
  //verticalStack->abs()->debugPrint();
  //horizontalStack->abs()->debugPrint();
  delete verticalStack;
  delete horizontalStack;
  for(int i=0; i<3; i++) {
    delete components[i];
  }
  _loop->_dotmaxMutex.lock();
  _loop->_fdotmax.flush();
  _loop->_dotmaxMutex.unlock();
}
