/***************************************************************************
**
**  This file is part of ArrayCore.
**
**  ArrayCore 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.
**
**  ArrayCore 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: 2018-04-10
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "AsyncArrayProcess.h"
#include "ArrayStationSignals.h"

namespace ArrayCore {
#if 0
  /*!
    \class AsyncArrayProcess AsyncArrayProcess.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  AsyncArrayProcess::AsyncArrayProcess(const ArraySelection * array)
    : FKCrossSpectrum(array)
  {
    TRACE;
    _mode=array->array()->mode();
    _verticalIndex=0;
    _northIndex=0;
    _eastIndex=0;
    _referenceStationIndex=0;
    _referenceStation=nullptr;
    _timeRanges=nullptr;
  }

  AsyncArrayProcess::~AsyncArrayProcess()
  {
    TRACE;
    qDeleteAll(_couples);
    delete [] _timeRanges;
  }

  /*!
    Look for the station with the longuest recording time during globbal range,
    assumed to be the reference station. The recording time excludes gaps.
  */
  void AsyncArrayProcess::setReferenceStation()
  {
    double maxLength=0.0;
    int n=array().count();
    for(int i=0; i<n; i++) {
      const StationSignals& stat=*array().at(i);
      SparseTimeRange r=stat.timeRange(_globalRange);
      double length=r.totalLength();
      if(length>maxLength) {
        _referenceStationIndex=i;
        maxLength=length;
      }
    }
    _referenceStation=static_cast<ArrayStationSignals *>(_stations.at(_referenceStationIndex));
  }

  void AsyncArrayProcess::setTimeRanges()
  {
    int n=array().count();
    _timeRanges=new SparseTimeRange[n];
    SparseTimeRange ref=_referenceStation->originalSignals()->timeRange(_globalRange);
    for(int i=0; i<n; i++) {
      const StationSignals& stat=*array().at(i);
      _timeRanges[i]=ref.intersection(stat.timeRange(_globalRange));
    }
  }

  bool AsyncArrayProcess::setParameters(const ArrayParameters * param)
  {
    TRACE;
    if(FKCrossSpectrum::setParameters(param)) {
      _globalRange=parameters()->timeLimits().absoluteRange(array().at(0));
      switch(_mode) {
      case ArrayStations::Vertical:
        break;
      case ArrayStations::Horizontal:
        _northIndex=0;
        _eastIndex=1;
        break;
      case ArrayStations::ThreeComponents:
        _verticalIndex=0;
        _northIndex=1;
        _eastIndex=2;
        break;
      }
      setReferenceStation();
      setTimeRanges();
      return true;
    } else {
      return false;
    }
  }

  void AsyncArrayProcess::setTimeRangeList(const AsyncStationPair& stations)
  {
    TRACE;
    SparseTimeRange r=_timeRanges[stations.index1()].intersection(_timeRanges[stations.index2()]);
    SparseKeepSignal * keep=new SparseKeepSignal(r);
    _referenceStation->originalSignals()->setSampling(keep);
    stations.station1()->originalSignals()->setSampling(keep);
    stations.station2()->originalSignals()->setSampling(keep);
    keep->initValues(1);
    _referenceStation->originalSignals()->setKeep(keep, parameters()->windowing(), _referenceStationIndex);
    stations.station1()->originalSignals()->setKeep(keep, parameters()->windowing(), stations.index1());
    stations.station2()->originalSignals()->setKeep(keep, parameters()->windowing(), stations.index2());
    _timeRangeList.clear();
    _timeRangeList.add(frequency().center(), parameters()->windowing(), *keep, keep->timeRange().range());
  }

  bool AsyncArrayProcess::lockTimeWindow(const TimeRange &win, const AsyncStationPair& stations)
  {
    TRACE;
    if(!stations.station1()->setProcessed(win, &frequency()) ||
       !stations.station1()->lockSamples()) {
      return false;
    }
    if(!stations.station2()->setProcessed(win, &frequency()) ||
       !stations.station2()->lockSamples()) {
      stations.station1()->unlockSamples();
      return false;
    }
    if(!_referenceStation->setProcessed(win, &frequency()) ||
       !_referenceStation->lockSamples()) {
      stations.station1()->unlockSamples();
      stations.station2()->unlockSamples();
      return false;
    }
    return true;
  }

  void AsyncArrayProcess::unlockTimeWindow(const AsyncStationPair& stations)
  {
    TRACE;
    _referenceStation->unlockSamples();
    stations.station1()->unlockSamples();
    stations.station2()->unlockSamples();
  }

  void AsyncArrayProcess::crossSpectrum()
  {
    TRACE;
    int stationCount=_stations.count();
    // Initialize to the number of pairs including self pairs
    _couples.resize(stationCount*(stationCount+1)/2);
    for(int i=_couples.count()-1; i>=0; i--) {
      _couples[i]=new AsyncStationPair;
    }
    int k=0;
    for(int is1=0; is1<stationCount; is1++) {
      for(int is2=is1; is2<stationCount; is2++) {
        AsyncStationPair& c=*_couples.at(k++);
        c.setStation1(is1, static_cast<ArrayStationSignals *>(stations().at(is1)));
        c.setStation2(is2, static_cast<ArrayStationSignals *>(stations().at(is2)));
        setTimeRangeList(c);
        crossSpectrum(c);
      }
    }
  }

  void AsyncArrayProcess::crossSpectrum(AsyncStationPair& stations)
  {
    TRACE;
    QString debugPrefix=QString("%1").arg(frequency().center());
    const AsyncParameters * param=static_cast<const AsyncParameters *>(parameters());

    int nWin=_timeRangeList.count();
    int iWin=0;
    Complex z;
    while(iWin<nWin) {
      QVector<int> blocks=param->blockAveraging().list(iWin, _stations.count(), _timeRangeList);
      iWin+=param->blockAveraging().increment(_timeRangeList, blocks);
      if(blocks.isEmpty()) {
        break;
      }
      for(QVector<int>::const_iterator it=blocks.begin(); it!=blocks.end(); it++) {
        if(lockTimeWindow(_timeRangeList.at(*it), stations)) {
          //stations.addCrossSpectrum(_frequencyFilter, debugPrefix);
          unlockTimeWindow(stations);
        }
      }
    }
  }
#endif
} // namespace ArrayCore

