/***************************************************************************
**
**  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-13
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include <DinverDCCore.h>

#include "SPACResults.h"

namespace ArrayCore {

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

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  SPACResults::SPACResults()
    : AbstractArrayResults()
  {
    TRACE;
    _target=nullptr;
    _stats=nullptr;
    for(int i=0; i<3; i++) {
      _rejected[i]=nullptr;
    }

    _nFrequencies=0;
    _nRings=0;
    _nComponents=0;
  }

  /*!
    Description of destructor still missing
  */
  SPACResults::~SPACResults()
  {
    TRACE;
    delete [] _stats;
    for(int i=0; i<3; i++) {
      delete [] _rejected[i];
    }
    delete _target;
  }

  void SPACResults::setArray(const ArraySelection * array)
  {
    AbstractArrayResults::setArray(array);
    switch (_array->array()->components()) {
    case StationSignals::AllComponent:
      _nComponents=3;
      break;
    case StationSignals::VerticalComponent:
      _nComponents=1;
      break;
    }
  }

  /*!
    Description of destructor still missing
  */
  void SPACResults::clear()
  {
    TRACE;
  }

  void SPACResults::setParameters(const SPACParameters& param)
  {
    ASSERT(_nComponents>0);
    _nRings=param.ringCount();
    _nFrequencies=param.frequencySampling().count();
    delete _target;
    _target=new AutocorrCurves;
    delete [] _stats;
    _stats=new SPACStatistics[_nFrequencies];
    for(int i=0; i<_nFrequencies; i++) {
      _stats[i].setParameters(_nRings, _nComponents);
    }
    for(int i=0; i<_nComponents; i++) {
      _rejected[i]=new int[_nFrequencies];
      for(int iFreq=0; iFreq<_nFrequencies; iFreq++) {
        _rejected[i][iFreq]=0;
      }
    }

    // Set the correct number of sample directly
    ModalCurve refCurve(param.frequencySampling().count());
    for(int iFreq=0; iFreq<_nFrequencies; iFreq++) {
      refCurve[iFreq].setX(param.frequencySampling().value(iFreq));
    }
    for(int iRing=0; iRing<_nRings; iRing++) {
      _target->addRing(param.ring(iRing) );
      if(_nComponents==3) {
        _target->addCurve(refCurve);
        _target->lastCurve().modes().last()=Mode(Mode::Vertical, iRing, 0);
        _target->addCurve(refCurve);
        _target->lastCurve().modes().last()=Mode(Mode::Radial, iRing, 0);
        _target->addCurve(refCurve);
        _target->lastCurve().modes().last()=Mode(Mode::Transverse, iRing, 0);
      } else {
        _target->addCurve(refCurve);
        _target->lastCurve().modes().last()=Mode(Mode::Vertical, iRing, 0);
      }
    }
  }

  void SPACResults::addValue(int frequency, int ring, int component, double value)
  {
    _addLock.lock();
    _stats[frequency].addValue(ring, component, value);
    _addLock.unlock();
  }

  void SPACResults::addRejected(int frequency, int * rejected)
  {
    _addLock.lock();
    for(int i=0; i<_nComponents; i++) {
      _rejected[i][frequency]+=rejected[i];
    }
    _addLock.unlock();
  }

  void SPACResults::commitStatistics()
  {
    QList<ModalCurve>& curves=_target->curves();
    for(int iFreq=0; iFreq<_nFrequencies; iFreq++) {
      for(int iRing=0; iRing<_nRings; iRing++) {
        int ringOffset=iRing*_nComponents;
        for(int iComp=0; iComp<_nComponents; iComp++){
          const Statistics& s=_stats[iFreq].result(iRing, iComp);
          ModalCurve& curve=curves[ringOffset+iComp];
          FactoryPoint& p=curve[iFreq];
          p.setMean(s.mean());
          p.setStddev(s.stddev());
          p.setWeight(s.count());
        }
      }
    }
  }

  void SPACResults::reportRejects()
  {
    if(_target->curves().isEmpty() ||
       static_cast<const SPACParameters *>(_parameters)->maximumImaginary()>=1.0) {
      return;
    }
    int nVal, nRej;
    ModalCurve& curve=_target->curves().first();
    for(int iFreq=0; iFreq<_nFrequencies; iFreq++) {
      if(_nComponents==3) {
        nVal=_stats[iFreq].count(1);
        nRej=_rejected[1][iFreq];
        App::log(tr("%1 values, %2 rejected (%3 %) at %4 Hz for radial component.\n")
                 .arg(nVal)
                 .arg(nRej)
                 .arg((100.0*nRej)/(nRej+nVal))
                 .arg(curve.at(iFreq).x()));
        nVal=_stats[iFreq].count(2);
        nRej=_rejected[2][iFreq];
        App::log(tr("%1 values, %2 rejected (%3 %) at %4 Hz for transverse component.\n")
                 .arg(nVal)
                 .arg(nRej)
                 .arg((100.0*nRej)/(nRej+nVal))
                 .arg(curve.at(iFreq).x()));

      }
      nVal=_stats[iFreq].count(0);
      nRej=_rejected[0][iFreq];
      App::log(tr("%1 values, %2 rejected (%3 %) at %4 Hz for vertical component.\n")
               .arg(nVal)
               .arg(nRej)
               .arg((100.0*nRej)/(nRej+nVal))
               .arg(curve.at(iFreq).x()));
    }
  }

  void SPACResults::save(QString fileName)
  {
    TRACE;
    if(_target) {
      if(!fileName.endsWith(".target")) {
        fileName+=".target";
      }
      TargetList tl;
      tl.autocorrTarget().setCurves( *_target);
      XMLHeader hdr(&tl);
      hdr.xml_saveFile(fileName);
      App::log(tr("results saved in '%1'.\n").arg(fileName));
    }
  }

#if 0
  // Export to stmap to be implemented in AutocorrCurves
  if(_loop->outputStmap(iComp)) {
    _loop->lockOutputStmap(iComp);
    const RingPairs& ring=_crossSpectrum->parameters()->ring(iRing);
    QTextStream * s=new QTextStream(_loop->outputStmap(iComp));
    (*s) << iRing << " "
         << index << " "
         << f << " "
         << tsum << " "
         << sqrt(tsum2) << " "
         << ring.minRadius()*0.001 << " "
         << ring.maxRadius()*0.001 << "\n";
    delete s;
    _loop->unlockOutputStmap(iComp);
  }
#endif

} // namespace ArrayCore

