/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QDebug>
#include <QScriptEngine>


/////////////////////// Local includes
#include <minexpert/nongui/MzIntegrationParamsJsPrototype.hpp>

#include <globals/globals.hpp>

namespace msXpSmineXpert
{


/*/js/ Class: MzIntegrationParams
 */

MzIntegrationParamsJsPrototype::MzIntegrationParamsJsPrototype(QObject *parent)
  : QObject(parent)
{
}


MzIntegrationParamsJsPrototype::~MzIntegrationParamsJsPrototype()
{
}


MzIntegrationParams *
MzIntegrationParamsJsPrototype::thisMzIntegrationParams() const
{
  MzIntegrationParams *mziParams =
    qscriptvalue_cast<MzIntegrationParams *>(thisObject().data());

  if(mziParams == Q_NULLPTR)
    qFatal(
      "Fatal error at %s@%d -- %s. "
      "thisMzIntegrationParams() would return Q_NULLPTR."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  return mziParams;
}


void
MzIntegrationParamsJsPrototype::initialize()
{
  // Depending on the various parameters, we will choose the proper
  // initializing function.

  QScriptContext *ctx = context();
  int argCount        = ctx->argumentCount();

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
           << "argCount: " << argCount;

  if(argCount == 1)
    {
      return initializeOneArgument();
    }
  else if(argCount == 5)
    {
      return initializeFiveArguments();
    }

  return;
}


void
MzIntegrationParamsJsPrototype::initializeOneArgument()
{
  QScriptContext *ctx = context();
  int argCount        = ctx->argumentCount();

  if(argCount != 1)
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "Programming error."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  // There is only one argument. We need to check the type of that argument
  // and depending on that type choose the proper initializator.

  QScriptValue arg = ctx->argument(0);

  // The other single-arg initializations are with Trace or MzIntegrationParams.

  QVariant variant = arg.data().toVariant();

  if(!variant.isValid())
    {
      variant = arg.toVariant();

      qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
               << "QVariant user type: " << variant.userType();
      ;

      if(!variant.isValid())
        {
          qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
                   << "Not going to do anything.";
        }
      else
        {

          // Let's check if the object passed as parameter in the script
          // environment is actually a SavGolParams object.

          QScriptValue nLValue = arg.property("nL", QScriptValue::ResolveLocal);

          if(nLValue.isValid())
            {

              // Finally, we can take the constructor parameter as a
              // SavGolParams argument.
              SavGolParams params = qscriptvalue_cast<SavGolParams>(arg);

              // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
              //<< "We are constructing from SavGolParams.";

              /*/js/
               * MzIntegrationParams.initialize(other)
               *
               * Initialize this MzIntegrationParams object with a
               * <SavGolParams> object
               *
               * Ex:
               *
               * var sgp1 = new SavGolParams(10, 10, 6, 0, true);
               * var mzip1 = new MzIntegrationParams();
               *
               * mzip1.savGolParams.nL; // --> 15
               * mzip1.savGolParams.nR; // --> 15
               * mzip1.savGolParams.m; // --> 4
               * mzip1.savGolParams.lD; // --> 0
               *
               * mzip1.initialize(sgp1);
               *
               * mzip1.savGolParams.nL; // --> 10
               * mzip1.savGolParams.nR; // --> 10
               * mzip1.savGolParams.m; // --> 6
               * mzip1.savGolParams.lD; // --> 0
               *
               * other: <SavGolParams> object
               */

              thisMzIntegrationParams()->initializeSavGolParams(params);

              return;
            }
        }
    }
  else
    {
      if(variant.userType() ==
         QMetaType::type("msXpSmineXpert::MzIntegrationParams"))
        {
          MzIntegrationParams params(
            qscriptvalue_cast<MzIntegrationParams>(arg));

          // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
          //<< "We are constructing from MzIntegrationParams."
          //<< "with params:" << params.asText();

          /*/js/
           * MzIntegrationParams.initialize(other)
           *
           * Initialize this MzIntegrationParams object with a
           * <MzIntegrationParams> object
           *
           * Ex:
           *
           * var sgp1 = new SavGolParams(10, 10, 6, 0, true);
           * var mzip1 = new MzIntegrationParams(sgp1);
           * var mzip2 = new MzIntegrationParams(mzip1);
           *
           * mzip2.savGolParams.nL; // --> 10
           * mzip2.savGolParams.nR; // --> 10
           * mzip2.savGolParams.m; // --> 6
           * mzip2.savGolParams.lD; // --> 0
           *
           * other: <MzIntegrationParams> object
           */

          thisMzIntegrationParams()->initialize(params);

          return;
        }
    }

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
           << "Not going to do anything.";

  return;
}


void
MzIntegrationParamsJsPrototype::initializeFiveArguments()
{
  QScriptContext *ctx = context();
  int argCount        = ctx->argumentCount();

  if(argCount != 5)
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "Programming error."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  QScriptValue arg1 = ctx->argument(0);
  QScriptValue arg2 = ctx->argument(1);
  QScriptValue arg3 = ctx->argument(2);
  QScriptValue arg4 = ctx->argument(3);
  QScriptValue arg5 = ctx->argument(4);

  if(!arg1.isNumber() || !arg2.isNumber() || !arg3.isNumber() ||
     !arg4.isNumber() || !arg5.isBoolean())
    {
      qDebug()
        << __FILE__ << __LINE__ << __FUNCTION__ << "()"
        << "One or more arguments is/are not of the Number or Boolean type.";

      ctx->throwError(
        "Syntax error: expected Savitzky-Golay parameters, nL, nR, m, lD and "
        "convolveWithNr.");

      return;
    }

  int nL              = qscriptvalue_cast<int>(arg1);
  int nR              = qscriptvalue_cast<int>(arg2);
  int m               = qscriptvalue_cast<int>(arg3);
  int lD              = qscriptvalue_cast<int>(arg4);
  bool convolveWithNr = qscriptvalue_cast<bool>(arg4);

  /*/js/
   * MzIntegrationParams.initialize(nL, nR, m, lD, convolveWithNr)
   *
   * Initialize this MzIntegrationParams object with the individual
   * <SavGolParams> parameters
   *
   * nL: number of data points on the left of the point being filtered
   *
   * nR: number of data points on the right of the point being filtered
   *
   * m: order of the polynomial to use in the regression analysis
   * leading to the Savitzky-Golay coefficients (typicall [2-6])
   *
   * lD: order of the derivative to extract from the Savitzky-Golay
   * smoothing algorithm (for regular smoothing, use 0);
   *
   * convolveWithNr: set to false for best results
   *
   * Ex:
   *
   * var mzip1 = new MzIntegrationParams();
   * mzip1.initialize(10, 10, 6, 0, false);
   *
   * mzip1.savGolParams.nL; // --> 10
   * mzip1.savGolParams.nR; // --> 10
   * mzip1.savGolParams.m; // --> 6
   * mzip1.savGolParams.lD; // --> 0
   */

  return thisMzIntegrationParams()->initializeSavGolParams(
    nL, nR, m, lD, convolveWithNr);
}


/*/js/
 * MzIntegrationParams.asText()
 *
 * Return a <String> object containing a textual representation of all the
 * member data of this MzIntegrationParams object.
 */
QString
MzIntegrationParamsJsPrototype::asText()
{
  /*/js/
   * MzIntegrationParams.asText()
   *
   * Return a textual representation of this <MzIntegrationParams> object.
   *
   * Ex:
   *
   *  var mzip1 = new MzIntegrationParams();
   *  mzip1.binningType = 2;
   *  mzip1.binningTypeString; // --> ARBITRARY
   *  mzip1.binSize = 0.0055;
   *  mzip1.binSizeTypeString = "MZ";
   *  mzip1.applyMzShift = true;
   *  mzip1.applySavGolFilter= true;
   *  mzip1.asText(); // returns this:
   *
   * m/z integration parameters:
   *     Binning type: ARBITRARY
   *     Bin nominal size: 0.005500
   *     Bin size type: MZ
   *     Apply m/z shift: true
   *     Remove 0-val data points: false
   *     Savitzky-Golay parameters
   *         nL = 15 ; nR = 15 ; m = 4 ; lD = 0 ; convolveWithNr = false
   */

  return thisMzIntegrationParams()->asText();
}


QScriptValue
MzIntegrationParamsJsPrototype::valueOf() const
{
  return thisObject().data();
}


} // namespace msXpSmineXpert
