#include "../headers/IndexWindow.h"

using namespace std;
using namespace mu;

IndexWindow::IndexWindow(QTableWidget* t = NULL, QWidget *parent = NULL) : QDialog(parent),table(t), m_vect_values(NULL){}

//~ IndexWindow::IndexWindow(QMap<QString, QVector<double> >& map_ind_indexres, QMap<QString, QString> & map_formula, QTableWidget* t = NULL, QWidget *parent = NULL) : QDialog(parent),table(t), m_vect_values(NULL){
IndexWindow::IndexWindow(QMap<QString, QVector<double> >& map_ind_indexres, QMap<QString, QString> & map_formula, const QMap <QString, QStringList>& map_scores, QTableWidget* t = NULL, QWidget *parent = NULL) : QDialog(parent),table(t), m_vect_values(NULL){
	ui.setupUi(this);
	
	m_map_formula = &map_formula;
	m_map_ind_indexres = &map_ind_indexres;
	m_map_scores = &map_scores;
	initWindow();
	
	connect(ui.onePushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.twoPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.threePushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.fourPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.fivePushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.sixPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.sevenPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.eightPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.ninePushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.zeroPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	
	connect(ui.pointPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.lBracketPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.rBracketPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.dividePushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.multiplyPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.minusPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.plusPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.powerPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.sqrtPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.absPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.expPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.lnPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	connect(ui.logPushButton, SIGNAL(clicked()), this, SLOT(onCalcButtonClicked()));
	
	//connect(ui.delPushButton, SIGNAL(clicked()), this, SLOT(delLastInput()));
	
	connect(ui.itemsTableWidget, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(onItemClicked(QTableWidgetItem *)));
	
	// Action buttons
	connect(ui.savePushButton, SIGNAL(clicked()), this, SLOT(saveFormula()));
	connect(ui.loadPushButton, SIGNAL(clicked()), this, SLOT(loadFormula()));
	
	connect(ui.buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(resetFormula()));
	connect(ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyFormula()));
	connect(ui.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(close()));
}


IndexWindow::~IndexWindow() { if(m_vect_values!=NULL) free(m_vect_values); m_vect_values=NULL; }

QString IndexWindow::getFormulaName() const {return m_name;}

/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: instantiate the QTLs table items.
	-----------------------------------------------------------------------------
*/

void IndexWindow::initWindow(){
	
	int ncol = table->columnCount();
	m_map_ind_indexres->clear();

	//~ int COL_GROUP = ColumnsIndexes::v(ICOL_GROUP);
	//~ int COL_LAST_PMS = ColumnsIndexes::v(ICOL_LAST_PMS);
	int COL_MS = ColumnsIndexes::v(ICOL_MS);
	int COL_UC = ColumnsIndexes::v(ICOL_UC);
	int COL_FIRST_QTL = ColumnsIndexes::v(ICOL_FIRST_QTL);
	//int COL_FIRST_PHENO = ColumnsIndexes::v(ICOL_FIRST_PHENO);
		//if (COL_FIRST_PHENO != -1) ncol = COL_FIRST_PHENO;
	
	int items_nb = 2 + ncol - COL_FIRST_QTL; // MS, UC, QTLs, PMSs and Phenos
	//~ if(COL_LAST_PMS != -1) items_nb += COL_LAST_PMS - COL_GROUP;
	//~ m_vect_values.resize(items_nb);
	m_vect_values = (double*)calloc(items_nb, sizeof(double));
	
	ui.itemsTableWidget->setRowCount(items_nb);
	ui.itemsTableWidget->setColumnCount(1);
	ui.itemsTableWidget->setHorizontalHeaderLabels(QStringList()<<"items");

	int ii=0;
	QString var;
	
	// MS
	ui.itemsTableWidget->setItem( ii, 0, new QTableWidgetItem( *(table->horizontalHeaderItem(COL_MS)) ) );
	ui.itemsTableWidget->item(ii, 0)->setFlags(Qt::ItemIsEnabled);
	var = (table->horizontalHeaderItem(COL_MS))->text();
	p.DefineVar(var.toStdString(), m_vect_values+ii); ii++;
	// UC
	ui.itemsTableWidget->setItem( ii, 0, new QTableWidgetItem( *(table->horizontalHeaderItem(COL_UC)) ) );
	ui.itemsTableWidget->item(ii, 0)->setFlags(Qt::ItemIsEnabled);
	var = (table->horizontalHeaderItem(COL_UC))->text();
	p.DefineVar(var.toStdString(), m_vect_values+ii); ii++;
	// QTLs + PMS(s) + pheno(s)
	for(int i=COL_FIRST_QTL; i<ncol; i++){
		ui.itemsTableWidget->setItem( ii, 0, new QTableWidgetItem( *(table->horizontalHeaderItem(i)) ) );
		ui.itemsTableWidget->item(ii, 0)->setFlags(Qt::ItemIsEnabled);
		var = (table->horizontalHeaderItem(i))->text();
		p.DefineVar(var.toStdString(), m_vect_values+ii); ii++;
	}
	//qDebug()<<ii<<" "<<items_nb;
	ui.itemsTableWidget->resizeRowsToContents();
	ui.itemsTableWidget->resizeColumnsToContents();
}


/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: set each selected QTL to the selected weight.
	-----------------------------------------------------------------------------
*/

void IndexWindow::onCalcButtonClicked(){
	
	QPushButton *button = (QPushButton*)sender(); // the button that send the signal clicked();
	QString buttonText = button->text();
	int pos = ui.formulaLineEdit->cursorPosition();
	m_formula = ui.formulaLineEdit->text();
	m_formula.insert(pos, buttonText);
	ui.formulaLineEdit->setText(m_formula);
	ui.formulaLineEdit->setCursorPosition(pos+buttonText.size());
	ui.formulaLineEdit->setFocus();
}


/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: select all QTL items.
	-----------------------------------------------------------------------------
*/

void IndexWindow::onItemClicked(QTableWidgetItem * item){
	QString itemText = item->text();
	int pos = ui.formulaLineEdit->cursorPosition();
	m_formula = ui.formulaLineEdit->text();
	m_formula.insert(pos, itemText);
	ui.formulaLineEdit->setText(m_formula);
	ui.formulaLineEdit->setCursorPosition(pos+itemText.size());
	ui.formulaLineEdit->setFocus();
}

/*
void IndexWindow::delLastInput(){
	int pos = ui.formulaLineEdit->cursorPosition();
	m_formula = ui.formulaLineEdit->text();
	m_formula.remove(1);
	ui.formulaLineEdit->setText(m_formula);
}
*/

/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: Save the formula, currently displayed in formulaLineEdit,
	into m_map_formula.
	N.B.: All saved formula are also saved in the working directory (saved_formulas.txt)
	once the index window is closed.
	-----------------------------------------------------------------------------
*/

void IndexWindow::saveFormula(){
	
	bool ok = true;
	
	QString name, expr;
	QString local_formula = ui.formulaLineEdit->text();
	
	if(local_formula.isEmpty()) { QMessageBox::information(this, tr("Save formula"), tr("Formula literal expression is empty !")); return; }
	
	QStringList name_expr = local_formula.split(QRegExp(" *= *"));
	
	if(name_expr.size() == 1) expr = name_expr[0];
	else if (name_expr.size() > 2) { QMessageBox::information(this, tr("Save formula"), tr("Formula literal expression is not conform !")); return; }
	else {	
		name = name_expr[0];
		expr = name_expr[1];
	}
	if(expr.isEmpty()) { QMessageBox::information(this, tr("Save formula"), tr("Formula literal expression is empty !")); return; }
	
	if(name.isEmpty()) name = QInputDialog::getText(this, tr("Save formula"), tr("Formula name:"), QLineEdit::Normal, "", &ok);
	if (!ok) return; // cancel pressed
	
	if(name.isEmpty()) { QMessageBox::information(this, tr("Save formula"), tr("Formula name is required !")); return; }
	
	// here we have a name and an literal expression :-)
	if(m_map_formula->find(name) != m_map_formula->end()){
		int answer = QMessageBox::question(this, tr("Save formula"), tr("A Formula with this name already exists,\ndo you want to overwrite it ?"), QMessageBox::Yes | QMessageBox::No);
		if( answer == QMessageBox::No ) return;
	}
	m_map_formula->insert(name, expr);
	ui.formulaLineEdit->setText(name+"="+expr);
	//~ qDebug()<<*m_map_formula;
}


/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: Load a formula previously saved.
	The formula is displayed in formulaLineEdit.
	-----------------------------------------------------------------------------
*/

void IndexWindow::loadFormula(){
	QStringList names = m_map_formula->keys();
	
	if(names.isEmpty()) { QMessageBox::information(this, tr("Load formula"), tr("There is no saved formula !")); return; }
	
	bool ok;
	QString name = QInputDialog::getItem(this, tr("Load formula"), tr("Choose a Formula:"), names, 0, false, &ok);
	if (!ok) return; // cancel pressed
	
	if(name.isEmpty()) return; // should not happen.
	
	ui.formulaLineEdit->setText(name+"="+m_map_formula->value(name));
}


/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: clear the formula.
	-----------------------------------------------------------------------------
*/

void IndexWindow::resetFormula(){
	
	m_formula = "";
	ui.formulaLineEdit->clear();
	
}


/**-----------------------------------------------------------------------------
	## PARAMETERS:
	## RETURN:
	## SPECIFICATION: hide/show rows (individual) and columns (QTLs) of table,
	according to their corresponding check state in the TableViewWindow.
	-----------------------------------------------------------------------------
*/

void IndexWindow::applyFormula(){
	
	int COL_ID = ColumnsIndexes::v(ICOL_ID);
	//~ int COL_GROUP = ColumnsIndexes::v(ICOL_GROUP);
	//~ int COL_LAST_PMS = ColumnsIndexes::v(ICOL_LAST_PMS);
	int COL_MS = ColumnsIndexes::v(ICOL_MS);
	int COL_UC = ColumnsIndexes::v(ICOL_UC);
	int COL_FIRST_QTL = ColumnsIndexes::v(ICOL_FIRST_QTL);

	m_formula = ui.formulaLineEdit->text();
	QString expr;
	
	QStringList name_expr = m_formula.split(QRegExp(" *= *"));
	if(name_expr.size() == 1) {
		expr = name_expr[0];
		m_name = "Index";
	}
	else{
		expr = name_expr[1];
		m_name = name_expr[0];
		if (name_expr[0].isEmpty())	m_name = "Index";
	}
	
	int ii = 0;
	QMap <QString, QStringList>::const_iterator score_it;
	int nrow = table->rowCount();
	int ncol = table->columnCount();
	p.SetExpr(expr.toStdString());

	// Pick the values of PMS, MS, UC, QTL1 etc ... from each individual
	// to fill m_vect_values (in which each element is linked to a particular string "MS" or "UC" or "QTL1" etc)
	// then evaluate the expression (expr) with p.Eval()  
	for(int r=0; r<nrow; r++){

		QString ind = ((QTableWidgetItem *)table->item(r, COL_ID))->text();
		score_it = m_map_scores->find(ind);
		const QStringList & ind_data = score_it.value();
		
		ii = 0;
		
		//~ m_vect_values[ii++] = (table->item(r,COL_MS))->text().toDouble(); // MS
		//~ m_vect_values[ii++] = (table->item(r,COL_UC))->text().toDouble(); // UC
		m_vect_values[ii++] = ind_data[COL_MS-1].toDouble(); // MS
		m_vect_values[ii++] = ind_data[COL_UC-1].toDouble(); // UC
		for(int i=COL_FIRST_QTL; i<ncol; i++){ // QTLs + PMS + pheno(s)
			//~ m_vect_values[ii++] = (table->item(r,i))->text().toDouble();
			m_vect_values[ii++] = ind_data[i-1].toDouble();
		}
		try{
			//cerr<<ind.toStdString()<<": "<<p.Eval()<<endl;
			m_map_ind_indexres->insert( ind, QVector<double>(1, p.Eval()) );
		}
		catch (Parser::exception_type &e){
			QMessageBox::critical(this, "Formula Error", QString::fromStdString(e.GetMsg()));
			m_map_ind_indexres->clear();
			return;
		}
	}
	//~ qDebug()<<"name: "<<m_name;
	accept();
}
