/***************************************************************************
**
**  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: 2002-11-22
**  Copyright: 2002-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>

#include <QGpCoreTools.h>

#include "GeopsyCoreEngine.h"
#include "GeopsyPreferences.h"
#include "GeopsyPlugins.h"
#include "SignalDatabase.h"
#include "KeepSignal.h"
#include "SubSignalPool.h"
#include "CustomFileFormats.h"
#include "Comments.h"
#include "FFTW.h"

namespace GeopsyCore {

GeopsyCoreEngine * GeopsyCoreEngine::_self=nullptr;

GeopsyCoreEngine::GeopsyCoreEngine(bool plugins, bool debug)
{
  TRACE;
  // Make sure geopsyCore is NULL, stop if two GeopsyCoreEngine objects are created
  ASSERT(!_self);
  _self=this;

  _progress=nullptr;
  _plugins=nullptr;
  _preferences=nullptr;
  _cache=nullptr;
  _fileContentKey=true;
  new FFTW;

  _customFileFormats=new CustomFileFormats;
  // Plugins required before preferences
  if(plugins) {
    _plugins=new GeopsyPlugins(debug);
  } else {
    _plugins=nullptr;
  }
  _preferences=new GeopsyPreferences;
  // Cache requires preferences
  _cache=new Cache;
  _cache->setSize(_preferences->cacheSize());
  _cache->setSwapDir(_preferences->cacheSwapDir());

  if(_plugins) {
    _plugins->polish(); // post initialization of plugins
  }
}

GeopsyCoreEngine::~GeopsyCoreEngine()
{
  TRACE;
  CoreApplication::instance()->removeGlobalObjects("FFTWCache");
  qDeleteAll(_databases);
  delete _cache;
  delete _preferences;
  delete _progress;
  delete _plugins;
  delete _customFileFormats;
  delete FastFourierTransform::instance();
}

/*!
 Create or delete (if \a s is null) a console progess.
*/
void GeopsyCoreEngine::setConsoleProgress(bool s)
{
  TRACE;
  if(s) {
    if(!_progress) {
      _progress=new ConsoleProgress;
    }
  } else {
    delete _progress;
    _progress=nullptr;
  }
}

/*!
  \fn GeopsyCoreEngine::useProgress()

  Return true if a progress monitor is available.
*/

/*!
  Set maximum value of progress for database \a db.
*/
void GeopsyCoreEngine::setProgressMaximum(SignalDatabase * , int val)
{
  TRACE;
  if(_progress) {
    _progress->setMaximum(val);
  }
}

/*!
  \fn GeopsyCoreEngine::progressMaximum(SignalDatabase *)
  Return maximum value of progress for database \a db.
  \a db can be null.
*/

/*!
  \fn GeopsyCoreEngine::setProgressValue(SignalDatabase *, int val)
  Set value of progress for database \a db.
  \a db can be null.
*/

/*!
  \fn GeopsyCoreEngine::progressValue(SignalDatabase *)
  Return value of progress for database \a db.
  \a db can be null.
*/

/*!
  \fn GeopsyCoreEngine::increaseProgressValue(SignalDatabase *)
  Increase value of progress for database \a db.
  \a db can be null.
*/

/*!
  Show message \a s for database \a db.
  \a db can be null.
*/
void GeopsyCoreEngine::showMessage(SignalDatabase * , QString s)
{
  TRACE;
  if(_progress) {
    _progress->setCaption(s.toLatin1());
    _progress->increaseValue(0);
  }
}

/*!
  Ask for file format through terminal
 
  The value returned is the same as the one returned by the GUI implementation (see Combo Box order!).
  This function re-implemented in GeopsyCoreEngineGui with a dialog box
*/
SignalFileFormat GeopsyCoreEngine::askLoadFormat(const QString&)
{
  // Build the list of possible formats (alphabetically sorted)
  QList< QPair<QString, SignalFileFormat> > formats=SignalFileFormat::importList();

  QTextStream mesg(stdout);
  QTextStream rep(stdin);
  QSettings& reg=CoreApplication::instance()->settings();

  reg.beginGroup("DialogOptions");
  reg.beginGroup("LoadFormat");
  int formatIndex=reg.value("fileFormat").toInt();
  if(formatIndex>=formats.count() || formatIndex<0) {
    formatIndex=0;
  }
  SignalFileFormat format=formats.at(formatIndex).second;
  if(reg.value("again").toBool()) {
    CoreApplication::instance()->debugUserInterrupts(false);
    mesg << tr("File Format\n");
    mesg << tr("Possible file formats:\n");
    for(int i=0; i<formats.count(); i++) {
      mesg << QString("%1.  ").arg(i) << formats.at(i).first << endl;
    }
    mesg << tr("File format (0...%1)? [default=%2] ").arg(formats.count()-1).arg(formatIndex) << flush;
    bool ok;
    int formatIndex=rep.readLine().toInt(&ok);
    if(ok) {
      if(formatIndex>=formats.count() || formatIndex<0) formatIndex=0;
      reg.setValue("fileFormat", formatIndex);
      format=formats.at(formatIndex).second;
      mesg << tr("Ask for file format next time? [y]/n ") << flush;
      if(rep.readLine().toLower()=="n") {
        reg.setValue("again", false);
      }
    }
    CoreApplication::instance()->debugUserInterrupts(true);
  }
  return format;
}

bool GeopsyCoreEngine::askRD3LogParameter(int& nSamples, double& sampFreq, double& t0)
{
  TRACE;
  CoreApplication::instance()->debugUserInterrupts(false);
  QTextStream mesg(stdout);
  QTextStream rep(stdin);
  QSettings& reg=CoreApplication::instance()->settings();
  reg.beginGroup("DialogOptions");
  reg.beginGroup("RD3LogParameter");

  mesg << tr("RD3 Default log parameters\n");
  bool ok;

  int defI=reg.value("numSamp", 1024).toInt();
  mesg << tr("Number of samples ? [default=%1] ").arg(defI) << endl;
  int repI=rep.readLine().toInt(&ok);
  if(ok) {
    nSamples=repI;
    reg.setValue("numSamp", repI);
  }

  double defD=reg.value("sampFreq", 578).toDouble();
  mesg << tr("Sampling frequency ? [default=%1] ").arg(defD) << endl;
  double repD=rep.readLine().toDouble(&ok);
  if(ok) {
    sampFreq=repD;
    reg.setValue("sampFreq", repD);
  }

  defD=reg.value("t0", 1024).toInt();
  mesg << tr("T0 ? [default=%1] ").arg(defD) << endl;
  repD=rep.readLine().toDouble(&ok);
  if(ok) {
    t0=repD;
    reg.setValue("t0", repD);
  }
  CoreApplication::instance()->debugUserInterrupts(true);
  return true;
}

/*!
  Return the database saved in file \a name. If none is found in already open databases, it tries to open if from disk.

  Only used when loading signals in geopsyfigs plugin (figue).
*/
SignalDatabase * GeopsyCoreEngine::database(QString name)
{
  TRACE;
  // Before searching for an already open database we must make sure that its path was translated or not
  // Before 20120613, this check was after searching all open databases leading to multiple re-opening of
  // databases with translated paths.
  QFileInfo fi(name);
  if (!fi.exists()) {
    PathTranslatorOptions options;
    options.setTitle(tr("Opening database... [GeopsyCoreEngine]"));
    options.setFileType(tr("geopsy database (%1)"));
    name=CoreApplication::instance()->translatePath(name, options);
    if(name.isEmpty()) {
      return nullptr;
    }
    fi.setFile(name);
    if(!fi.exists()) {
      return nullptr;
    }
  }
  for(QList<SignalDatabase *>::iterator it=_databases.begin(); it!=_databases.end(); ++it) {
    if(name==(*it)->name()) return *it;
  }
  // The requested database is not opened, so try to find it
  SignalDatabase * db=new SignalDatabase;

  if(db->open(fi.absoluteFilePath())) {
    _databases.append(db);
    return db;
  } else {
    delete db;
    return nullptr;
  }
}

/*!
  Return the first database if only one is available. Else construct a default one.

  Used when loading signals in geopsyfigs plugin (figue) or when running Core engine only (without GUI).
*/
SignalDatabase * GeopsyCoreEngine::defaultDatabase()
{
  TRACE;
  if(_databases.count()==1) {
    return _databases.first();
  } else {
    SignalDatabase * db=new SignalDatabase;
    _databases.append(db);
    return db;
  }
}

} // namespace GeopsyCore
