/***************************************************************************
**
**  This file is part of waran.
**
**  waran 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.
**
**  waran 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-07-13
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <ArrayCore.h>

#include "RealTimeArrayManager.h"

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
RealTimeArrayManager::RealTimeArrayManager(ArrayStations * array)
{
  printf("RealTimeArrayManager %p\n",this);
  // Create as many processes as the number of processors
  //int maximumThreads=Thread::idealThreadCount();
  int maximumThreads=1;
  if(maximumThreads<1) maximumThreads=1;
  // _threads is never modified in thread, only in main thread, hence no need for mutex
  for(int i=0; i<maximumThreads; i++) {
    RealTimeArrayProcess * t=new RealTimeArrayProcess(this, array);
    t->setObjectName(QString("arrayprocess%1").arg(i+1));
    _threads.append(t);
    t->setFrequencyBands(&_bands);
    t->start();
  }

  _array=array;
  // Get the minimum starting time
  DateTime minTime=DateTime::minimumTime;
  for(ArrayStations::iterator it=_array->begin();it!=_array->end();it++) {
    const DateTime& t=(*it)->minTime();
    if(t>minTime) minTime=t;
  }
  _globalRange.setStart(minTime);
  _globalRange.setEnd(minTime);
  _active=false;
}

/*!
  Description of destructor still missing
*/
RealTimeArrayManager::~RealTimeArrayManager()
{
  //clearTasks();
  for(QList<RealTimeArrayProcess *>::iterator it=_threads.begin(); it!=_threads.end(); it++) {
    (*it)->terminate();
  }
  _event.wakeAll();
  for(QList<RealTimeArrayProcess *>::iterator it=_threads.begin(); it!=_threads.end(); it++) {
    (*it)->wait();
  }
  printf("~RealTimeArrayManager %p\n",this);
}

#if 0
void RealTimeArrayManager::clearTasks()
{
  _mutex.lock();
  qDeleteAll(_tasks);
  _tasks.clear();
  _mutex.unlock();
  QList<RealTimeArrayProcess *>::iterator it;
  do {
    for(it=_threads.begin(); it!=_threads.end(); it++) {
      if(!(*it)->isWaiting()) break;
    }
  } while(it!=_threads.end());
}
#endif

void RealTimeArrayManager::start()
{
  // Clean result grids
  _fkDispersion.clear();
  _hrfkDispersion.clear();
  _spacDispersion.clear();
  // Reset starting times for each frequency band
  for(int i=_lastFrequencyTimes.count()-1;i>=0;i--) {
    _lastFrequencyTimes[i]=_globalRange.start();
  }
  // Cache frequency bands for each process
  int n=_samplingParameters.count();
  for(int i=0; i<n; i++) {
    FrequencyBand fb;
    fb.setRelativeWidth(_frequencyBandwidth);
    fb.setCenter(_samplingParameters.value(i));
    // TODO
    //_bands[i].calculate(fb, _windowingParameters.periodCount()/fb.center());
  }
  _active=true;
  createTasks();
}

void RealTimeArrayManager::stop()
{
  if(_active) {
    _active=false;
    //clearTasks();
  }
}

void RealTimeArrayManager::setKmin(double k)
{
  bool wasActive=_active;
  //if(wasActive) clearTasks();
  for(QList<RealTimeArrayProcess *>::iterator it=_threads.begin(); it!=_threads.end(); it++) {
    (*it)->setKmin(k);
  }
  if(wasActive) start();
}

void RealTimeArrayManager::setKmax(double k)
{
  bool wasActive=_active;
  //if(wasActive) clearTasks();
  for(QList<RealTimeArrayProcess *>::iterator it=_threads.begin(); it!=_threads.end(); it++) {
    (*it)->setKmax(k);
  }
  if(wasActive) start();
}

void RealTimeArrayManager::setMaximumTime(const DateTime& t)
{
  qDebug() << "Maximum time " << t.toString();
  if(t>_globalRange.end())
    _globalRange.setEnd(t);
}

void RealTimeArrayManager::setSamplingParameters(const SamplingParameters& p)
{
  bool wasActive=_active;
  //if(wasActive) clearTasks();
  _samplingParameters=p;
  _lastFrequencyTimes.resize(_samplingParameters.count());
  _fkDispersion.init(p, 200, 0.0005, 0.01, LinearScale);
  _hrfkDispersion.init(p, 200, 0.0005, 0.01, LinearScale);
  _spacDispersion.init(p, 200, 0.0005, 0.01, LinearScale);
  _bands.resize(_samplingParameters.count());
  if(wasActive) start();
}

void RealTimeArrayManager::setfrequencyBandwidth(double fbw)
{
  bool wasActive=_active;
  //if(wasActive) clearTasks();
  _frequencyBandwidth=fbw;
  if(wasActive) start();
}

void RealTimeArrayManager::setWindowLenght(double l)
{
  bool wasActive=_active;
  //if(wasActive) clearTasks();
  _windowingParameters.setPeriodCount(l);
  if(wasActive) start();
}

/*!
*/
void RealTimeArrayManager::createTasks()
{
  if( !_active || _lastFrequencyTimes.isEmpty()) return;
  // Get minimum lastFrequency time
  DateTime minLastFrequencyTime=_globalRange.end();
  for(int i=_lastFrequencyTimes.count()-1;i>=0;i--) {
    if(_lastFrequencyTimes[i]<minLastFrequencyTime) {
      minLastFrequencyTime=_lastFrequencyTimes[i];
    }
  }
  // Get the minimum end time (to skip gap computation at the end of signals)
  DateTime minEndTime=_globalRange.end();
  for(ArrayStations::iterator it=_array->begin();it!=_array->end();it++) {
    const DateTime& t=(*it)->maxTime();
    if(t<minEndTime) minEndTime=t;
  }
  // Get gaps between minLastFrequencyTime and minEndTime
  // Horizontal components are ignored for now
  TimeRange globalProcessRange(minLastFrequencyTime, minEndTime);
  SparseTimeRange globalSparseRange(globalProcessRange);
  //printf("############################################\n");
  //printf("**** global\n");
  //printf("From %lf to %lf\n",minLastFrequencyTime, globalProcessRange.end());
  for(int i=_array->count()-1;i>=0;i--) {
    globalSparseRange=globalSparseRange.intersection(_array->at(i)->timeRange(globalProcessRange));
  }
  globalSparseRange.printDebug();
  SparseKeepSignal keep(globalSparseRange);
  // Adjust range taking sampling of all stations into account
  for(int i=_array->count()-1;i>=0;i--) {
    _array->at(i)->setSampling(&keep);
  }
  // Set values of keep
  keep.initValues(1);
  for(int i=_array->count()-1;i>=0;i--) {
    _array->at(i)->setKeep(&keep, _windowingParameters, i);
  }
  // Calculate time windows for each frequency
  FrequencyBand fb;
  fb.setRelativeWidth(_frequencyBandwidth);
  for(int i=_lastFrequencyTimes.count()-1;i>=0;i--) {
    TimeRangeList wList;
    fb.setCenter(_samplingParameters.value(i));
    wList.add(fb.center(), _windowingParameters, keep, TimeRange(_lastFrequencyTimes[i], _globalRange.end()));
    if(!wList.isEmpty()) {
      //printf("Enqueue...\n");
      /*for(TimeRangeList::iterator it=wList.begin();it!=wList.end();it++) {
        addTask(new ArrayTask(i, fb, **it) );
      }*/
      _lastFrequencyTimes[i]=wList.timeRange().end();
    }
  }
}

#if 0
void RealTimeArrayManager::addTask(ArrayTask * t)
{
  TRACE;
  /*printf("Enqueue task for %lf Hz between %lf s and %lf s\n", t->frequency().center(),
                                                              t->range().start(), t->range().end());*/
  _mutex.lock();
  _tasks.enqueue(t);
  queueChanged(_tasks.count());
  _mutex.unlock();
  _event.wakeOne();
}

/*!
  Normaly called from a process. Mutex is assumed to be already locked. It is unlock on exit.
  Returned pointer must be deleted.
*/
ArrayTask * RealTimeArrayManager::takeTask()
{
  ArrayTask * t=_tasks.dequeue();
  _mutex.unlock();
  return t;
}
#endif
