/*************************************************************************************
 *  Copyright (C) 2007-2009 by Aleix Pol <aleixpol@kde.org>                          *
 *                                                                                   *
 *  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 "functionedit.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>

#include <KColorScheme>

#include <analitzagui/expressionedit.h>
#include <analitza/analyzer.h>
#include <analitza/expression.h>
#include <analitza/variables.h>
#include <analitza/value.h>
#include <analitzagui/algebrahighlighter.h>
#include <analitzagui/plotsview2d.h>
#include <analitzaplot/planecurve.h>
#include <analitzaplot/plotsmodel.h>
#include <analitzaplot/plotsfactory.h>
#include <klocalizedstring.h>

using namespace Analitza;

namespace {
	static const int resolution = 200;
}

FunctionEdit::FunctionEdit(QWidget *parent)
	: QWidget(parent), m_calcUplimit(0), m_calcDownlimit(0)
{
	setWindowTitle(i18n("Add/Edit a function"));
	
	QVBoxLayout *topLayout = new QVBoxLayout(this);
	topLayout->setMargin(2);
	topLayout->setSpacing(5);
	
	m_name = new QLineEdit(this);
	
	m_func = new ExpressionEdit(this);
    m_func->setExamples(PlotsFactory::self()->examples(Dim2D));
	m_func->setAns("x");
	connect(m_func, SIGNAL(textChanged()), this, SLOT(edit()));
	connect(m_func, SIGNAL(returnPressed()), this, SLOT(ok()));
	
	m_valid = new QLabel(this);
	m_valid->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
	
	QPalette p=palette();
	p.setColor(QPalette::Active, QPalette::Base, Qt::white);
	m_valid->setPalette(p);
	
	m_validIcon = new QLabel(this);
	m_validIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
	QLayout* validLayout=new QHBoxLayout;
	validLayout->addWidget(m_validIcon);
	validLayout->addWidget(m_valid);
	
	m_color = new KColorCombo(this);
	m_color->setColor(QColor(0,150,0));
	connect(m_color, SIGNAL(currentIndexChanged(int)), this, SLOT(colorChange(int)));
	
	m_funcsModel=new PlotsModel(this);
    m_funcsModel->setResolution(resolution);
	
	m_viewTabs=new QTabWidget(this);
	
	m_graph = new PlotsView2D(m_viewTabs);
	m_graph->setModel(m_funcsModel);
	m_graph->setViewport(QRectF(QPointF(-10.0, 10.0), QSizeF(20.0, -20.0)));
	m_graph->setFocusPolicy(Qt::NoFocus);
	m_graph->setMouseTracking(false);
	m_graph->setFramed(true);
	m_graph->setReadOnly(true);
	m_graph->setTicksShown(Qt::Orientation(0));
	
	m_viewTabs->addTab(m_graph, QIcon::fromTheme("document-preview"), i18n("Preview"));
	QWidget *options=new QWidget(m_viewTabs);
	options->setLayout(new QVBoxLayout);
	m_uplimit=new ExpressionEdit(options);
	m_downlimit=new ExpressionEdit(options);
	m_uplimit->setText("2*pi");
	m_downlimit->setText("0");
	options->layout()->addWidget(new QLabel(i18n("From:"), options));
	options->layout()->addWidget(m_downlimit);
	options->layout()->addWidget(new QLabel(i18n("To:"), options));
	options->layout()->addWidget(m_uplimit);
	options->layout()->addItem(new QSpacerItem(0,0, QSizePolicy::Expanding, QSizePolicy::Expanding));
	m_viewTabs->addTab(options, QIcon::fromTheme("configure"), i18n("Options"));
	connect(m_uplimit, SIGNAL(textChanged()), this, SLOT(updateUplimit()));
	connect(m_downlimit, SIGNAL(textChanged()), this, SLOT(updateDownlimit()));
	
	QHBoxLayout *m_butts = new QHBoxLayout;
	m_ok = new QPushButton(i18n("OK"), this);
	m_ok->setIcon(QIcon::fromTheme("dialog-ok"));
	m_remove = new QPushButton(i18nc("@action:button", "Remove"), this);
	m_remove->setIcon(QIcon::fromTheme("list-remove"));
	connect(m_ok, SIGNAL(clicked()), this, SLOT(ok()));
	connect(m_remove, SIGNAL(clicked()), this, SIGNAL(removeEditingPlot()));
	
	topLayout->addWidget(m_name);
	topLayout->addWidget(m_func);
	topLayout->addWidget(m_color);
	topLayout->addLayout(validLayout);
	topLayout->addWidget(m_viewTabs);
	topLayout->addLayout(m_butts);
	
	m_name->hide(); //FIXME: Remove this when the name has any sense
	
	m_butts->addWidget(m_ok);
	m_butts->addWidget(m_remove);
	
	m_func->setFocus();
	m_ok->setEnabled(false);
	
	QFont errorFont=m_valid->font();
	errorFont.setBold(true);
	m_valid->setFont(errorFont);
}

FunctionEdit::~FunctionEdit()
{}

void FunctionEdit::clear()
{
	m_func->setText(QString());
	m_funcsModel->clear();
	edit();
}

void FunctionEdit::setFunction(const QString &newText)
{
	m_func->setText(newText);
	m_func->document()->setModified(true);
}

void FunctionEdit::setColor(const QColor &newColor)
{
	m_color->setColor(newColor);
	if(m_funcsModel->rowCount()>0)
		  m_funcsModel->setData(m_funcsModel->index(0), newColor);
}

void FunctionEdit::colorChange(int)
{
	setColor(m_color->color());
}

static double calcExp(const Analitza::Expression& exp, Analitza::Variables* v, bool* corr)
{
	Q_ASSERT(exp.isCorrect());
	Analitza::Analyzer d(v);
	d.setExpression(exp);
	Analitza::Expression r=d.calculate();
	
	*corr=r.isCorrect() && r.isReal();
	
	if(*corr)
		return r.toReal().value();
	else
		return 0.;
}

void FunctionEdit::updateUplimit()
{
	bool corr = m_uplimit->isCorrect();
	if(corr) {
		Analitza::Expression e=m_uplimit->expression();
		m_calcUplimit=calcExp(e, m_vars, &corr);
		m_uplimit->setCorrect(corr);
		if(corr)
			edit();
	}
}

void FunctionEdit::updateDownlimit()
{
	bool corr = m_downlimit->isCorrect();
	if(corr) {
		Analitza::Expression e=m_downlimit->expression();
		m_calcDownlimit=calcExp(e, m_vars, &corr);
		m_downlimit->setCorrect(corr);
		if(corr)
			edit();
	}
}

void FunctionEdit::setState(const QString& text, bool negative)
{
	QFontMetrics fm(m_valid->font());
	m_valid->setText(fm.elidedText(text, Qt::ElideRight, m_valid->width()));
	m_valid->setToolTip(text);
	
	KColorScheme scheme(QPalette::Normal);
	KColorScheme::ForegroundRole role = negative? KColorScheme::NegativeText : KColorScheme::PositiveText;
	
	QPalette p=m_valid->palette();
	p.setColor(foregroundRole(), scheme.foreground(role).color());
	m_valid->setPalette(p);
	
	if(negative)
		m_validIcon->setPixmap(QIcon::fromTheme("flag-red").pixmap(QSize(16,16)));
	else
		m_validIcon->setPixmap(QIcon::fromTheme("flag-green").pixmap(QSize(16,16)));
}

///Let's see if the exp is correct
void FunctionEdit::edit()
{
	if(m_func->text().isEmpty()) {
		m_func->setCorrect(true);
		m_ok->setEnabled(false);
		m_valid->clear();
		m_valid->setToolTip(QString());
		m_validIcon->setPixmap(QIcon::fromTheme("flag-yellow").pixmap(QSize(16,16)));
		
		m_funcsModel->clear();
		m_graph->forceRepaint();
		return;
	}
	
	if(!m_uplimit->isCorrect() || !m_downlimit->isCorrect()) {
		setState(i18n("The options you specified are not correct"), true);
		return;
	}
	
	if(m_calcDownlimit>m_calcUplimit) {
		setState(i18n("Downlimit cannot be greater than uplimit"), true);
		return;
	}
	bool added = false;
	
	PlaneCurve* f = 0;
	PlotBuilder req = PlotsFactory::self()->requestPlot(expression(), Dim2D, m_vars);
	if(req.canDraw())
		f = createFunction();
	
	if(f && f->isCorrect())
		f->update(QRect(-10, 10, 20, -20));
	
	m_funcsModel->clear();
	if(f && f->isCorrect()) {
		m_funcsModel->addPlot(f);
		added=true;
		setState(QString("%1:=%2")
			.arg(m_name->text()).arg(f->expression().toString()), false);
	} else {
		QStringList errors = req.errors();
		if(f)
			errors = f->errors();
		Q_ASSERT(!errors.isEmpty());
		
		setState(errors.first(), true);
		m_valid->setToolTip(errors.join("<br />"));
		delete f;
	}
	m_func->setCorrect(added);
	m_ok->setEnabled(added);
}

void FunctionEdit::ok()
{
	if(m_ok->isEnabled())
		emit accept();
}

void FunctionEdit::focusInEvent(QFocusEvent *)
{
	m_func->setFocus();
}

PlaneCurve* FunctionEdit::createFunction() const
{
	PlotBuilder req = PlotsFactory::self()->requestPlot(expression(), Dim2D, m_vars);
	PlaneCurve* curve = static_cast<PlaneCurve*>(req.create(color(), name()));
	curve->setResolution(resolution);
	if(m_calcUplimit != m_calcDownlimit) {
		foreach(const QString& var, curve->parameters())
			curve->setInterval(var, m_calcUplimit, m_calcDownlimit);
	}
	return curve;
}

Analitza::Expression FunctionEdit::expression() const
{
	return m_func->expression();
}

void FunctionEdit::setOptionsShown(bool shown)
{
	m_viewTabs->setVisible(shown);
}

void FunctionEdit::resizeEvent(QResizeEvent*)
{
	QFontMetrics fm(m_valid->font());
	m_valid->setText(fm.elidedText(m_valid->toolTip(), Qt::ElideRight, m_valid->width()));
}

void FunctionEdit::setEditing(bool m)
{
	m_modmode=m;
	m_remove->setVisible(m);
}
