/***************************************************************************
    File                 : NonLinearFit.cpp
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2006 by Ion Vasilief, Tilman Benkert
    Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net
    Description          : NonLinearFit class

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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 2 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, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#include "NonLinearFit.h"
#include "MyParser.h"
#include "fit_gsl.h"

#include <QMessageBox>
using namespace std;

NonLinearFit::NonLinearFit(ApplicationWindow *parent, Graph *g) : Fit(parent, g)
{
    init();
}

NonLinearFit::NonLinearFit(ApplicationWindow *parent, Graph *g, const QString &curveTitle)
    : Fit(parent, g)
{
    init();
    setDataFromCurve(curveTitle);
}

NonLinearFit::NonLinearFit(ApplicationWindow *parent, Graph *g, const QString &curveTitle,
                           double start, double end)
    : Fit(parent, g)
{
    init();
    setDataFromCurve(curveTitle, start, end);
}

void NonLinearFit::init()
{
    setObjectName(tr("NonLinear"));
    d_formula = QString();
    d_f = user_f;
    d_df = user_df;
    d_fdf = user_fdf;
    d_fsimplex = user_d;
    d_explanation = tr("Non-linear");
    d_init_err = false;
}

void NonLinearFit::setFormula(const QString &s)
{
    d_formula = s;
}

void NonLinearFit::setParametersList(const QStringList &lst)
{
    if ((int)lst.count() < 1) {
        QMessageBox::critical((ApplicationWindow *)parent(), tr("Fit Error"),
                              tr("You must provide a list containing at least one parameter for "
                                 "this type of fit. Operation aborted!"));
        d_init_err = true;
        return;
    }

    d_init_err = false;
    d_param_names = lst;

    if (d_p > 0) { // free previously allocated memory
        gsl_vector_free(d_param_init);
        gsl_matrix_free(covar);
    }

    d_p = (int)lst.count();
    d_min_points = d_p;
    d_param_init = gsl_vector_alloc(d_p);
    gsl_vector_set_all(d_param_init, 1.0);

    covar = gsl_matrix_alloc(d_p, d_p);
    d_results.resize(d_p);

    for (unsigned i = 0; i < d_p; i++)
        d_param_explain << "";
}

bool NonLinearFit::calculateFitCurveData(const vector<double> &par, std::vector<double> &X,
                                         std::vector<double> &Y)
{
    for (unsigned i = 0; i < d_p; i++)
        if (!d_script->setDouble(par[i], d_param_names[i].toUtf8()))
            return false;

    bool ok = false;
    generateX(X);

    for (int i = 0; i < d_points; i++) {
        if (!d_script->setDouble(X[i], "x"))
            return false;
        auto y = d_script->eval();
        if (y.isNull())
            return false;
        Y[i] = y.toDouble(&ok);
        if (!ok)
            return false;
    }
    return true;
}
