/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore 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.
**
**  GeopsyCore 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: 2009-06-02
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "SparseKeepSignal.h"
#include "KeepSignal.h"

namespace GeopsyCore {

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

  Full description of class still missing
*/

SparseKeepSignal::SparseKeepSignal()
    : SparseTimeRange()
{
  TRACE;
}

/*!
  Description of constructor still missing
*/
SparseKeepSignal::SparseKeepSignal(const SparseTimeRange& r)
    : SparseTimeRange(r)
{
  TRACE;
  const ::QVector<TimeRange>& rs=ranges();
  int n=rs.count();
  _keeps.resize(n);
  for(int i=0; i<n; i++) {
    _keeps[i]=0;
  }
}

/*!
  Copy properties of keep signals, not their values. You must call initValues() or copySamplesFrom()
*/
SparseKeepSignal::SparseKeepSignal(const SparseKeepSignal& o)
    : SparseTimeRange(o)
{
  TRACE;
  int n=o._keeps.count();
  _keeps.resize(n);
  for(int i=0; i<n; i++) {
    _keeps[i]=new KeepSignal(*o._keeps[i]);
  }
}

/*!
  Description of destructor still missing
*/
SparseKeepSignal::~SparseKeepSignal()
{
  TRACE;
  qDeleteAll(_keeps);
}

void SparseKeepSignal::copySamplesFrom(const SparseKeepSignal& o)
{
  TRACE;
  int n=_keeps.count();
  ASSERT(n==o._keeps.count());
  for(int i=0; i<n; i++) {
    _keeps[i]->copySamplesFrom(o._keeps[i]);
  }
}

/*!
  Set T0 and samplingPeriod for all continuous blocks hit by \a sigRange.
  T0 is set to match \a r with a multiple of samplingPeriod().
  The samples are not affected by this function.

  Returns false if T0 is already set but not compatible with \a sigRange. Tests also \a samplingPeriod.
*/
bool SparseKeepSignal::setSampling(const TimeRange& sigRange, double samplingPeriod)
{
  TRACE;
  if(_keeps.isEmpty()) return true;
  int i=index(sigRange.start());
  double samplingFrequency=1.0/samplingPeriod;
  const ::QVector<TimeRange>& keepRanges=ranges();
  for(;i<keepRanges.count();i++) {
    KeepSignal * k=_keeps[i];
    const TimeRange& keepRange=keepRanges.at(i);
    if(keepRange.start()>=sigRange.end()) {
      break;
    }
    if(k) {
      if(k->samplingPeriod()!=samplingPeriod) {
        return false;
      }
      double dt=fabs(sigRange.start().secondsTo(k->startTime()));
      double dt0=qRound(dt*samplingFrequency)*samplingPeriod;
      if(fabs(dt0-dt)*samplingFrequency>0.001) { // 1/1000 sampling period
        return false;
      }
    } else {
      //printf("Arbitrary limits %lf to %lf\n",r.start(), r.end());
      DateTime startTime=sigRange.start();
      startTime.addSeconds(-floor((keepRange.start().secondsTo(sigRange.start())*samplingFrequency)*samplingPeriod));
      // +1 is there because the time end cuts the last time step which beginning
      // is still inside time range
      KeepSignal * k=new KeepSignal(qFloor(startTime.secondsTo(keepRange.end())*samplingFrequency)+1);
      k->setSamplingPeriod(samplingPeriod);
      k->setStartTime(startTime);
      _keeps[i]=k;
    }
  }
  return true;
}

SparseKeepSignal * SparseKeepSignal::intersection(const TimeRange& r) const
{
  TRACE;
  int iFirst=index(r.start());
  SparseKeepSignal * res=new SparseKeepSignal(SparseTimeRange::intersection(iFirst, r));
  int n=res->ranges().count();
  res->_keeps.resize(n);
  if(n>0) {
    for(int i=n-2; i>0; i--) {
      res->_keeps[i]=new KeepSignal(*_keeps.at(iFirst+i));
    }
    const KeepSignal * kStart=_keeps.at(iFirst+n-1);
    if(r.start()>kStart->startTime()) {
      res->_keeps[0]=cutStart(kStart, r.start());
    } else {
      res->_keeps[0]=new KeepSignal(*kStart);
    }
    const KeepSignal * kEnd=_keeps.at(iFirst+n-1);
    if(r.end()<kEnd->endTime()) {
      res->_keeps[n-1]=cutEnd(kEnd, r.end());;
    } else {
      res->_keeps[n-1]=new KeepSignal(*kEnd);
    }
  }
  return res;
}

void SparseKeepSignal::remove(const TimeRange& r)
{
  TRACE;
  int iFirst=index(r.start());
  KeepSignal * kStart=_keeps.at(iFirst);
  if(r.start()>kStart->startTime() && r.start()<kStart->endTime()) {
    _keeps[iFirst]=cutEnd(kStart, r.start());
    delete kStart;
  }
  int nRemoved=SparseTimeRange::remove(iFirst, r);
  for(int i=nRemoved-1; i>=0; i--) {
    delete _keeps[iFirst+i];
  }
  _keeps.remove(iFirst, nRemoved);
  if(iFirst<ranges().count()) {
    KeepSignal * kEnd=_keeps.at(iFirst);
    if(r.end()>kEnd->startTime() && r.end()<kEnd->endTime()) {
      _keeps[iFirst]=cutStart(kEnd, r.end());
      delete kEnd;
    }
  }
}

KeepSignal * SparseKeepSignal::cutStart(const KeepSignal * keep, const DateTime& t)
{
  int iSample=qCeil(keep->startTime().secondsTo(t)/keep->samplingPeriod()+0.001);
  KeepSignal * newKeep=new KeepSignal(keep->nSamples()-iSample);
  newKeep->setSamplingPeriod(keep->samplingPeriod());
  DateTime st(keep->startTime());
  st.addSeconds(iSample*keep->samplingPeriod());
  newKeep->setStartTime(st);
  newKeep->copySamplesFrom(keep, iSample, 0, newKeep->nSamples());
  return newKeep;
}

KeepSignal * SparseKeepSignal::cutEnd(const KeepSignal * keep, const DateTime& t)
{
  int nSamples=qFloor(keep->startTime().secondsTo(t)/keep->samplingPeriod()-0.001);
  KeepSignal * newKeep=new KeepSignal(nSamples);
  newKeep->setSamplingPeriod(keep->samplingPeriod());
  newKeep->setStartTime(keep->startTime());
  newKeep->copySamplesFrom(keep, 0, 0, newKeep->nSamples());
  return newKeep;
}

void SparseKeepSignal::initValues(int value, const DateTime& start, const DateTime& end)
{
  TRACE;
  int n=_keeps.count();
  for(int i=0;i<n;i++) {
    KeepSignal * keep=_keeps[i];
    double invSamplingPeriod=1.0/keep->samplingPeriod();
    int iStart;
    if(start<keep->startTime()) {
      iStart=0;
    } else {
      iStart=qRound(keep->startTime().secondsTo(start)*invSamplingPeriod);
      if(iStart>=keep->nSamples()) continue;
    }
    int iEnd;
    if(end>keep->startTime()+keep->nSamples()*keep->samplingPeriod()) iEnd=keep->nSamples()-1;
    else {
      iEnd=qRound(keep->startTime().secondsTo(end)*invSamplingPeriod);
      if(iEnd<0) continue;
    }
    keep->initValues(value, iStart, iEnd-iStart+1);
  }
}

/*!
  Returns all keeps hit by \a r
*/
QVector<KeepSignal *> SparseKeepSignal::keeps(const TimeRange& r) const
{
  TRACE;
  ::QVector<KeepSignal *> res;
  if(isNull()) return res;
  int i=index(r.start());
  const ::QVector<TimeRange>& rs=ranges();
  int n=rs.count();
  for(;i<n && rs.at(i).end()<r.end();i++) {
    res.append(_keeps.at(i));
  }
  // Add the last block that had its end after r.end() but can have a start() before.
  if(i<n && rs.at(i).start()<r.end()) {
    res.append(_keeps.at(i));
  }
  return res;
}

} // namespace GeopsyCore
