/***************************************************************************
**
**  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-05-05
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QJSEngine>

#include "SignalProcessScript.h"
#include "SignalProcessObject.h"

namespace GeopsyCore {

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
SignalProcessScript::SignalProcessScript(SignalProcess * process)
{
  TRACE;
  _process=process;
  _engine=new QJSEngine;
  ScriptContext::add(_engine);

  SignalProcessObject * obj=new SignalProcessObject(_process, this);
  QJSValue extension=_engine->newQObject(obj);

  _engine->globalObject().setProperty("saveStep", extension.property("saveStep"));
  _engine->globalObject().setProperty("restoreStep", extension.property("restoreStep"));

  _engine->globalObject().setProperty("fastFourierTransfrom", extension.property("fastFourierTransfrom"));
  _engine->globalObject().setProperty("exportFile", extension.property("exportFile"));
  _engine->globalObject().setProperty("header", extension.property("header"));
  _engine->globalObject().setProperty("setHeader", extension.property("setHeader"));
  _engine->globalObject().setProperty("signalCount", extension.property("signalCount"));
  _engine->globalObject().setProperty("selectSignals", extension.property("selectSignals"));

  _engine->globalObject().setProperty("removeTrend", extension.property("removeTrend"));
  _engine->globalObject().setProperty("subtractValue", extension.property("subtractValue"));
  _engine->globalObject().setProperty("subtractSignal", extension.property("subtractSignal"));
  _engine->globalObject().setProperty("subtractSignals", extension.property("subtractSignals"));
  _engine->globalObject().setProperty("multiply", extension.property("multiply"));
  _engine->globalObject().setProperty("filter", extension.property("filter"));
  _engine->globalObject().setProperty("agc", extension.property("agc"));
  _engine->globalObject().setProperty("whiten", extension.property("whiten"));
  _engine->globalObject().setProperty("stddevClip", extension.property("stddevClip"));
  _engine->globalObject().setProperty("shift", extension.property("shift"));
  _engine->globalObject().setProperty("overSample", extension.property("overSample"));
  _engine->globalObject().setProperty("taper", extension.property("taper"));
  _engine->globalObject().setProperty("cut", extension.property("cut"));
  _engine->globalObject().setProperty("merge", extension.property("merge"));
  _engine->globalObject().setProperty("mergeStations", extension.property("mergeStations"));
  _engine->globalObject().setProperty("decimateAmplitude", extension.property("decimateAmplitude"));
  _engine->globalObject().setProperty("decimateTime", extension.property("decimateTime"));

  _engine->globalObject().setProperty("waveletTransform", extension.property("waveletTransform"));
  _engine->globalObject().setProperty("stalta", extension.property("stalta"));
  _engine->globalObject().setProperty("discreteFourierTransform", extension.property("discreteFourierTransform"));

  _engine->globalObject().setProperty("rotateComponents", extension.property("rotateComponents"));
  _engine->globalObject().setProperty("correlations", extension.property("correlations"));
  _engine->globalObject().setProperty("normalizedCorrelations", extension.property("normalizedCorrelations"));
  _engine->globalObject().setProperty("convolution", extension.property("convolution"));
  _engine->globalObject().setProperty("removeInstrumentalResponse", extension.property("removeInstrumentalResponse"));
}

SignalProcessScript::~SignalProcessScript()
{
  TRACE;
  delete _engine;
}

bool SignalProcessScript::run(const QString& script)
{
  TRACE;
  QJSValue result=_engine->evaluate(script);
  if(result.isError()) {
    _process->addToHistory(tr("// Last line(s) ended with error(s), see log for details\n"));
    App::log(tr("Uncaught exception at line %1: %2\n")
                     .arg(result.property("lineNumber").toInt())
                     .arg(result.toString()));
    return false;
  } else {
    return true;
  }
}

#if 0

TODO: in SignalProcessObject there are still some functions that do not have standard types
      for arguments

QScriptValue SignalProcessScript::waveletTransformScript(QScriptContext *context, QScriptEngine *engine)
{
  TRACE;
  if(context->argumentCount()!=2) {
    return context->throwError(QScriptContext::SyntaxError, tr("1 argument required: number m, number fi"));
  }
  QScriptValue m=context->argument(0);
  if(!m.isNumber() || m.toNumber()<=0) {
    return context->throwError(QScriptContext::SyntaxError, tr("Argument 1 must be a number (>0)"));
  }
  QScriptValue fi=context->argument(1);
  if(!fi.isNumber() || fi.toNumber()<=0) {
    return context->throwError(QScriptContext::SyntaxError, tr("Argument 1 must be a number (>0)"));
  }
  SignalProcessScript * p=static_cast<SignalProcessScript *>(engine->parent());
  MorletParameters param;
  param.setM(m.toNumber());
  param.setFi(fi.toNumber());
  p->waveletTransformCore(param);
  return QScriptValue(true);
}

QScriptValue SignalProcessScript::removeInstrumentalResponseScript(QScriptContext *context, QScriptEngine *engine)
{
  TRACE;
  InstrumentalResponse sensor;
  if(context->argumentCount()<3) {
    return context->throwError(QScriptContext::SyntaxError, tr("At least 3 arguments are required: nPoles, nZeros, noise"));
  }
  int argIndex=0;
  // Reading poles
  QScriptValue npS=context->argument(argIndex++);
  if(!npS.isNumber()) {
    return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
  }
  int np=npS.toInt32();
  if(context->argumentCount()<3+np*2) {
    return context->throwError(QScriptContext::SyntaxError, tr("At least %1 arguments are required: nPoles, [re and im of %2 poles], nZeros, noise").arg(2*np+3).arg(np));
  }
  for(int i=0; i<np; i++) {
    QScriptValue reS=context->argument(argIndex++);
    if(!reS.isNumber()) {
      return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
    }
    QScriptValue imS=context->argument(argIndex++);
    if(!imS.isNumber()) {
      return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
    }
    sensor.addPole(Complex(reS.toNumber(), imS.toNumber()));
  }
  // Reading zeros
  QScriptValue nzS=context->argument(argIndex++);
  if(!nzS.isNumber()) {
    return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
  }
  int nz=nzS.toInt32();
  if(context->argumentCount()<3+np*2+nz*2) {
    return context->throwError(QScriptContext::SyntaxError, tr("At least %1 arguments are required: nPoles, [poles], nZeros, [re and im of %2 zeros], noise").arg(2*np+2*nz+3).arg(nz));
  }
  for(int i=0; i<nz; i++) {
    QScriptValue reS=context->argument(argIndex++);
    if(!reS.isNumber()) {
      return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
    }
    QScriptValue imS=context->argument(argIndex++);
    if(!imS.isNumber()) {
      return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
    }
    sensor.addZero(Complex(reS.toNumber(), imS.toNumber()));
  }
  // Reading noise
  QScriptValue noiseS=context->argument(argIndex++);
  if(!noiseS.isNumber()) {
    return context->throwError(QScriptContext::SyntaxError, tr("Argument %1 must be a number").arg(argIndex));
  }
  sensor.setNoiseLevel(noiseS.toNumber());
  SignalProcessScript * p=static_cast<SignalProcessScript *>(engine->parent());
  p->removeInstrumentalResponseCore(sensor);
  return QScriptValue(true);
}

#endif

} // namespace GeopsyCore
