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

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

/***************************************************************************
 *                                                                         *
 *  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 "ExpDecayDialog.h"
#include "Graph.h"
#include "ColorButton.h"
#include "ApplicationWindow.h"
#include "Fit.h"
#include "ExponentialFit.h"

#include <QMessageBox>
#include <QLayout>
#include <QGroupBox>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QComboBox>
#include <QCloseEvent>

	ExpDecayDialog::ExpDecayDialog(int type, QWidget* parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
{
	slopes = type;

	setWindowTitle(tr("Verify initial guesses"));

	QGroupBox *gb1 = new QGroupBox();
	QGridLayout *gl1 = new QGridLayout();
	gl1->addWidget(new QLabel(tr("Exponential Fit of")), 0, 0);

	boxName = new QComboBox();
	connect( boxName, SIGNAL( activated(const QString&) ), this, SLOT( activateCurve(const QString&) ) );
	gl1->addWidget(boxName, 0, 1);

	if (type < 0)
		dampingLabel = new QLabel( tr("Growth time"));
	else if (type == 1)
		dampingLabel = new QLabel( tr("Decay time"));
	else
		dampingLabel = new QLabel( tr("First decay time (t1)"));
	gl1->addWidget(dampingLabel, 1, 0);

	boxFirst = new QLineEdit();
	boxFirst->setText(tr("1"));
	gl1->addWidget(boxFirst, 1, 1);

	if (type > 1)
	{
		gl1->addWidget(new QLabel( tr("Second decay time (t2)")), 2, 0);

		boxSecond = new QLineEdit();
		boxSecond->setText(tr("1"));
		gl1->addWidget(boxSecond, 2, 1);

		thirdLabel = new QLabel( tr("Third decay time (t3)"));
		gl1->addWidget(thirdLabel, 3, 0);

		boxThird = new QLineEdit();
		boxThird->setText(tr("1"));
		gl1->addWidget(boxThird, 3, 1);

		if (type < 3)
		{
			thirdLabel->hide();
			boxThird->hide();
		}
	}

	if (type <= 1)
	{
		gl1->addWidget(new QLabel(tr("Amplitude")), 2, 0);
		boxAmplitude = new QLineEdit();
		boxAmplitude->setText(tr("1"));
		gl1->addWidget(boxAmplitude, 2, 1);
	}

	gl1->addWidget(new QLabel(tr("Y Offset")), 4, 0 );
	boxYOffset = new QLineEdit();
	boxYOffset->setText(tr("0"));
	gl1->addWidget(boxYOffset, 4, 1);

    gl1->addWidget(new QLabel(tr("Initial time")), 5, 0);

	boxStart = new QLineEdit();
	boxStart->setText(tr("0"));
	gl1->addWidget(boxStart, 5, 1);

	gl1->addWidget(new QLabel(tr("Color")), 6, 0 );
	btnColor = new ColorButton();
	btnColor->setColor(QColor(Qt::red));
	gl1->addWidget(btnColor, 6, 1);

	gb1->setLayout(gl1);

	buttonFit = new QPushButton(tr("&Fit"));
	buttonFit->setDefault(true);

	buttonCancel = new QPushButton(tr("&Close"));

	QBoxLayout *bl1 = new QBoxLayout (QBoxLayout::TopToBottom);
	bl1->addWidget(buttonFit);
	bl1->addWidget(buttonCancel);
	bl1->addStretch();

	QHBoxLayout* hlayout = new QHBoxLayout();
	hlayout->addWidget(gb1);
	hlayout->addLayout(bl1);
	setLayout(hlayout);

	// signals and slots connections
	connect( buttonFit, SIGNAL( clicked() ), this, SLOT(fit()));
	connect( buttonCancel, SIGNAL( clicked() ), this, SLOT(close()));
}

void ExpDecayDialog::setGraph(Graph *g)
{
	if (!g)
		return;

    fitter = 0;
	graph = g;

	boxName->addItems(graph->analysableCurvesList());

    QString selectedCurve = g->selectedCurveTitle();
	if (!selectedCurve.isEmpty())
	{
	    int index = boxName->findText (selectedCurve);
		boxName->setCurrentIndex(index);
	}
    activateCurve(boxName->currentText());

	connect (graph, SIGNAL(closedGraph()), this, SLOT(close()));
    connect (graph, SIGNAL(dataRangeChanged()), this, SLOT(changeDataRange()));
};

void ExpDecayDialog::activateCurve(const QString& curveName)
{
	QwtPlotCurve *c = graph->curve(curveName);
	if (!c)
		return;

    ApplicationWindow *app = (ApplicationWindow *)this->parent();
	if (!app)
        return;

	int precision = app->fit_output_precision;
	double start, end;
	graph->range(graph->curveIndex(curveName), &start, &end);
	boxStart->setText(QString::number(qMin(start, end)));
	boxYOffset->setText(QString::number(c->minYValue(), 'g', precision));
	if (slopes < 2)
        boxAmplitude->setText(QString::number(c->maxYValue() - c->minYValue(), 'g', precision));

};

void ExpDecayDialog::changeDataRange()
{
double start = graph->selectedXStartValue();
double end = graph->selectedXEndValue();
boxStart->setText(QString::number(qMin(start, end), 'g', 15));
}

void ExpDecayDialog::fit()
{
	QString curve = boxName->currentText();
	QwtPlotCurve *c = graph->curve(curve);
	QStringList curvesList = graph->analysableCurvesList();
	if (!c || !curvesList.contains(curve))
	{
		QMessageBox::critical(this,tr("Warning"),
				tr("The curve <b> %1 </b> doesn't exist anymore! Operation aborted!").arg(curve));
		boxName->clear();
		boxName->addItems(curvesList);
		return;
	}

	ApplicationWindow *app = (ApplicationWindow *)this->parent();
	if (!app)
        return;

	int precision = app->fit_output_precision;

	if (fitter)
        delete fitter;

	if (slopes == 3)
	{
		double x_init[7] = {1.0, boxFirst->text().toDouble(), 1.0, boxSecond->text().toDouble(),
			1.0, boxThird->text().toDouble(), boxYOffset->text().toDouble()};
		fitter = new ThreeExpFit(app, graph);
		fitter->setInitialGuesses(x_init);
	}
	else if (slopes == 2)
	{
		double x_init[5] = {1.0, boxFirst->text().toDouble(), 1.0, boxSecond->text().toDouble(),
			boxYOffset->text().toDouble()};
		fitter = new TwoExpFit(app, graph);
		fitter->setInitialGuesses(x_init);
	}
	else if (slopes == 1 || slopes == -1)
	{
		double x_init[3] = {boxAmplitude->text().toDouble(), slopes/boxFirst->text().toDouble(), boxYOffset->text().toDouble()};
		fitter = new ExponentialFit(app, graph, slopes == -1);
		fitter->setInitialGuesses(x_init);
	}

  	if (fitter->setDataFromCurve(boxName->currentText(), boxStart->text().toDouble(), c->maxXValue()))
	{
		fitter->setColor(btnColor->color());
		fitter->scaleErrors(app->fit_scale_errors);
        fitter->setOutputPrecision(app->fit_output_precision);
		fitter->generateFunction(app->generateUniformFitPoints, app->fitPoints);
		fitter->fit();

		auto& results = fitter->results();
		boxFirst->setText(QString::number(results[1], 'g', precision));
		if (slopes < 2)
		{
            boxAmplitude->setText(QString::number(results[0], 'g', precision));
            boxYOffset->setText(QString::number(results[2], 'g', precision));
        }
        else if (slopes == 2)
        {
            boxSecond->setText(QString::number(results[3], 'g', precision));
            boxYOffset->setText(QString::number(results[4], 'g', precision));
        }
        else if (slopes == 3)
        {
            boxSecond->setText(QString::number(results[3], 'g', precision));
            boxThird->setText(QString::number(results[5], 'g', precision));
            boxYOffset->setText(QString::number(results[6], 'g', precision));
        }
	}
}

void ExpDecayDialog::closeEvent (QCloseEvent * e )
{
	if(fitter)
	{
        ApplicationWindow *app = (ApplicationWindow *)this->parent();
        if (app && app->pasteFitResultsToPlot)
            fitter->showLegend();

        delete fitter;
	}

	e->accept();
}
