/***************************************************************************
 *   Copyright (C) 2005 by Niklas Knutsson                                 *
 *   nq@altern.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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "qalculateresultdisplay.h"

#include "qalculate_tde_utils.h"
#include "kqalculate.h"
#include <vector>
#include <tdepopupmenu.h>
#include <kxmlguifactory.h>
#include <kxmlguiclient.h>
#include <tdeaction.h>
#include <tdemessagebox.h>
#include <tqwhatsthis.h>
#include <tqtooltip.h>
#include <tdelocale.h>
#include <tdeapplication.h>
#include <tqsimplerichtext.h>

extern std::vector<mode_struct> modes;

extern TQString parsed_text;
extern KQalculate *mainWin;
extern MathStructure *mstruct;
extern PrintOptions printops;
extern EvaluationOptions evalops;

TQString linkInfo(const MathStructure *m, TQWidget *w, bool full_length = false) {
	TQString str;
	if(m) {
		if(m->isSymbolic()) {
			str = "\"";
			str += m->symbol().c_str();
			str += "\"";
		} else if(m->isVector()) {
			if(m->isMatrix()) {
				str = i18n("a matrix");
			} else {
				str = i18n("a vector");
			}
			str += "\n";
			str += i18n("double-click to view/edit");
		} else if(m->isVariable()) {
			if(m->variable()->subtype() == SUBTYPE_UNKNOWN_VARIABLE) {
				str = i18n("Unknown variable");
			} else {
				str = i18n("Variable");
			}
			str += ": ";
			str += m->variable()->title(true).c_str();
		} else if(m->isUnit()) {
			str = i18n("Unit");
			str += ": ";
			str += m->unit()->title(true).c_str();
			if(m->prefix() && m->prefix() != CALCULATOR->decimal_null_prefix && m->prefix() != CALCULATOR->binary_null_prefix) {
				str += "\n";
				str += i18n("Prefix");
				str += ": ";
				TQString pstr;
				switch(m->prefix()->type()) {
					case PREFIX_DECIMAL: {
						TQTextOStream(&pstr) << m->prefix()->name(false, printops.use_unicode_signs, &can_display_unicode_string_function, (void*) w).c_str() << " (10e" << ((DecimalPrefix*) m->prefix())->exponent() << ")";
						break;
					}
					case PREFIX_BINARY: {
						TQTextOStream(&pstr) << m->prefix()->name(false, printops.use_unicode_signs, &can_display_unicode_string_function, (void*) w).c_str() << " (2e" << ((BinaryPrefix*) m->prefix())->exponent() << ")";
						break;
					}
					case PREFIX_NUMBER: {
						TQTextOStream(&pstr) << m->prefix()->name(false, printops.use_unicode_signs, &can_display_unicode_string_function, (void*) w).c_str();
						break;
					}			
				}
				str += pstr;
			}
		} else if(m->isFunction()) {
			str = i18n("Function");
			str += ": ";
			str += m->function()->title(true).c_str();
			Argument default_arg;
			MathFunction *f = m->function();
			str += "<br>";
			const ExpressionName *ename = &f->preferredName(false, printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) w);
			str += "<i><b>";
			str += ename->name.c_str();
			str += "</b>";
			int iargs = f->maxargs();
			if(iargs < 0) {
				iargs = f->minargs() + 1;
			}
			str += "(";
			if(iargs != 0) {
				for(int i2 = 1; i2 <= iargs; i2++) {
					if(i2 > f->minargs()) {
						str += "[";
					}
					if(i2 > 1) {
						str += CALCULATOR->getComma().c_str();
						str += " ";
					}
					Argument *arg = f->getArgumentDefinition(i2);
					if(arg && !arg->name().empty()) {
						str += arg->name().c_str();
					} else {
						str += i18n("argument");
						str += " ";
						str += TQString::number(i2);
					}
					if(i2 > f->minargs()) {
						str += "]";
					}
				}
				if(f->maxargs() < 0) {
					str += CALCULATOR->getComma().c_str();
					str += " ...";
				}
			}
			str += ")";
			str += "</i>";
			if(full_length) {
				str += "<i>";
				for(size_t i2 = 1; i2 <= f->countNames(); i2++) {
					if(&f->getName(i2) != ename) {
						str += "<br>";
						str += f->getName(i2).name.c_str();
					}
				}
				str += "</i>";
				if(f->subtype() == SUBTYPE_DATA_SET) {
					str += "<br>";
					str += "<br>";
					TQString str2;
					str2.sprintf(i18n("Retrieves data from the %s data set for a given object and property. If \"info\" is typed as property, a dialog window will pop up with all properties of the object.").utf8(), f->title().c_str());
					str2.replace("<", "&lt;");
					str2.replace(">", "&gt;");
					str += str2;
				}
				if(!f->description().empty()) {
					str += "<br>";
					str += "<br>";
					TQString str2 = f->description().c_str();
					str2.replace("<", "&lt;");
					str2.replace(">", "&gt;");
					str += str2;
				}
			}
		}
	}
	return str;
}


TQString linkInfo(const TQString &link, TQWidget *w, bool full_length = false) {
	if(!link.isEmpty() && link[0] == '+') {
		TQString str = link;
		str.remove(0, 1);
		return str;
	} else {
		return linkInfo(mainWin->getResultPart(link.toInt()), w, full_length);
	}
}

class QalculateExpressionToolTip : public TQToolTip {

public:

	QalculateResultDisplay *qrd;

	QalculateExpressionToolTip(QalculateResultDisplay *parent, TQToolTipGroup *group = 0) : TQToolTip(parent->viewport(), group) {	
		qrd = parent;
	}
	virtual ~QalculateExpressionToolTip() {}

protected:

	void maybeTip(const TQPoint &p) {
		int cx = qrd->contentsX();
		int cy = qrd->contentsY();
		TQString str = qrd->anchorAt(qrd->viewportToContents(p), TQt::AnchorName);
		if(str.isEmpty()) return;
		int w = 5;
		while(p.x() + w < qrd->visibleWidth() && qrd->anchorAt(TQPoint(p.x() + cx + w, p.y() + cy), TQt::AnchorName) == str) {
			w += 5;
		}
		int x = p.x() - 5;
		w += 5;
		while(x > 0 && qrd->anchorAt(TQPoint(x + cx, p.y() + cy), TQt::AnchorName) == str) {
			x -= 5;
			w += 5;
		}
		if(x < 0) x = 0;
		if(w + x > qrd->visibleWidth()) w = qrd->visibleWidth() - x;
		int h = 5;
		while(p.y() + h < qrd->visibleHeight() && qrd->anchorAt(TQPoint(p.x() + cx, p.y() + cy + h), TQt::AnchorName) == str) {
			h += 5;
		}
		int y = p.y() - 5;
		h += 5;
		while(y > 0 && qrd->anchorAt(TQPoint(p.x() + cx, y + cy), TQt::AnchorName) == str) {
			y -= 5;
			h += 5;
		}
		if(y < 0) y = 0;
		if(h + y > qrd->visibleHeight()) h = qrd->visibleHeight() - y;
		tip(TQRect(x, y, w, h), linkInfo(str, qrd, true));
	}
	
};

QalculateResultDisplay::QalculateResultDisplay(TQWidget *parent, const char *name): TQTextBrowser(parent, name) {

	setResizePolicy(AutoOneFit);
	setVScrollBarMode(Auto);
	setHScrollBarMode(Auto);
	setTextFormat(TQt::RichText);
	setFrameStyle(TQFrame::NoFrame);
	//setFocusPolicy(TQWidget::TabFocus);
	paletteChanged();
	m_under_pointer = NULL;

	TQToolTipGroup *tgroup = new TQToolTipGroup(viewport());
	tooltip = new QalculateExpressionToolTip(this, tgroup);
	
	//connect(this, TQ_SIGNAL(anchorClicked(const TQString&, const TQString&)), this, TQ_SLOT(onAnchorClicked(const TQString&, const TQString&)));
	connect(this, TQ_SIGNAL(doubleClicked(int, int)), this, TQ_SLOT(onDoubleClicked(int, int)));
	if(kapp) {
		connect(kapp, TQ_SIGNAL(tdedisplayPaletteChanged()), this, TQ_SLOT(paletteChanged()));
	}

}

QalculateResultDisplay::~QalculateResultDisplay() {}

void QalculateResultDisplay::setRightMargin(int i_margin) {
	setMargins(0, 0, i_margin, 0);
}

void QalculateResultDisplay::onAnchorClicked(const TQString&, const TQString&) {
	/*if(name.isEmpty()) return;
	TQWhatsThis::display(linkInfo(name, this, true));*/
}

TQPopupMenu *QalculateResultDisplay::createPopupMenu(const TQPoint &pos) {

	m_under_pointer = NULL;

	TQPopupMenu *menu = TQTextBrowser::createPopupMenu(pos);
	menu->insertSeparator();
	mainWin->ActionStoreResult->plug(menu);
	mainWin->ActionSaveAsImage->plug(menu);
	menu->insertSeparator();
	if(mstruct && mstruct->containsType(STRUCT_ADDITION)) {
		if(evalops.structuring != STRUCTURING_FACTORIZE) {
			mainWin->ActionFactorize->plug(menu);
		}
		if(evalops.structuring != STRUCTURING_SIMPLIFY) {
			mainWin->ActionSimplify->plug(menu);
		}
	} else if(mstruct && mstruct->isNumber() && mstruct->number().isInteger() && !mstruct->number().isZero()) {
		mainWin->ActionFactorize->plug(menu);
		menu->insertSeparator();
	}
	if(mstruct && mstruct->containsUnknowns()) {
		mainWin->ActionSetUnknowns->plug(menu);
	}
	if(mstruct && (mstruct->containsType(STRUCT_ADDITION) || mstruct->containsUnknowns())) {
		menu->insertSeparator();
	}
	if(mstruct && mstruct->containsDivision()) {
		mainWin->ActionNonZeroDenominators->plug(menu);
		menu->insertSeparator();
	}
	int id = menu->insertItem(i18n("Normal"));
	if(printops.min_exp == EXP_PRECISION) menu->setItemChecked(id, true);
	else menu->connectItem(id, this, TQ_SLOT(displayNormal()));
	id = menu->insertItem(i18n("Engineering"));
	if(printops.min_exp == EXP_BASE_3) menu->setItemChecked(id, true);
	else menu->connectItem(id, this, TQ_SLOT(displayEngineering()));
	id = menu->insertItem(i18n("Scientific"));
	if(printops.min_exp == EXP_SCIENTIFIC) menu->setItemChecked(id, true);
	else menu->connectItem(id, this, TQ_SLOT(displayScientific()));
	/*mainWin->ActionNumericalDisplayNormal->plug(menu);
	mainWin->ActionNumericalDisplayEngineering->plug(menu);
	mainWin->ActionNumericalDisplayScientific->plug(menu);*/
	if(!mstruct || !mstruct->containsType(STRUCT_UNIT)) {
		menu->insertSeparator();
		mainWin->ActionNumberBaseBinary->plug(menu);
		mainWin->ActionNumberBaseOctal->plug(menu);
		mainWin->ActionNumberBaseDecimal->plug(menu);
		mainWin->ActionNumberBaseHexadecimal->plug(menu);
		menu->insertSeparator();
		mainWin->ActionFractionalDisplayDecimal->plug(menu);
		mainWin->ActionFractionalDisplayDecimalTryExact->plug(menu);
		mainWin->ActionFractionalDisplayFraction->plug(menu);
		mainWin->ActionFractionalDisplayCombined->plug(menu);
	}
	menu->insertSeparator();
	mainWin->ActionAbbreviateNames->plug(menu);
	if(mstruct && mstruct->containsType(STRUCT_UNIT)) {
		menu->insertSeparator();
		mainWin->ActionConvertToUnitExpression->plug(menu);
		mainWin->ActionConvertToBaseUnits->plug(menu);
		mainWin->ActionConvertToBestUnit->plug(menu);
		menu->insertSeparator();
		mainWin->ActionEnablePrefixes->plug(menu);
		mainWin->ActionEnableUseOfAllPrefixes->plug(menu);
		mainWin->ActionEnableDenominatorPrefixes->plug(menu);
	}
	menu->insertSeparator();
	TQPopupMenu *modes_menu = new TQPopupMenu(menu);
	TQObject::connect(modes_menu, TQ_SIGNAL(activated(int)), mainWin, TQ_SLOT(loadMode(int)));
	for(size_t i = 0; i < modes.size(); i++) {
		modes_menu->insertItem(modes[i].name, i, i);
	}
	modes_menu->insertSeparator();
	mainWin->ActionSaveModeAs->plug(modes_menu);
	mainWin->ActionDeleteMode->plug(modes_menu);
	menu->insertItem(i18n("Meta Modes"), modes_menu);
	if(parsed_text.isEmpty()) return menu;
	menu->insertSeparator();
	menu->insertItem(i18n("Show Parsed Expression"), this, TQ_SLOT(showParsedExpression()));
	TQString str = anchorAt(pos, TQt::AnchorName);
	if(!str.isEmpty()) {
		if(str[0] == '+') {
			name_under_pointer = str;
			name_under_pointer.remove(0, 1);
			menu->insertItem(i18n("Show Object Info"), this, TQ_SLOT(showInfo()));
		} else {
			m_under_pointer = mainWin->getResultPart(str.toInt());
			if(m_under_pointer) {
				if(m_under_pointer->isVector()) {
					if(m_under_pointer->isMatrix()) {
						menu->insertItem(i18n("View/Edit Matrix"), this, TQ_SLOT(editMatrix()));
					} else {
						menu->insertItem(i18n("View/Edit Vector"), this, TQ_SLOT(editVector()));
					}
				} else {
					menu->insertItem(i18n("Show Object Info"), this, TQ_SLOT(showInfo()));
				}
			}
		}
	}
	curpos = viewport()->mapToGlobal(pos);
	return menu;

}

void QalculateResultDisplay::displayNormal() {
	printops.negative_exponents = false;
	printops.sort_options.minus_last = true;
	mainWin->ActionNumericalDisplayNormal->activate();
	mainWin->ActionNegativeExponents->setChecked(false);
	mainWin->ActionSortMinusLast->setChecked(true);
}
void QalculateResultDisplay::displayEngineering() {
	mainWin->ActionNumericalDisplayEngineering->activate();
}
void QalculateResultDisplay::displayScientific() {
	printops.negative_exponents = true;
	printops.sort_options.minus_last = false;
	mainWin->ActionNumericalDisplayScientific->activate();
	mainWin->ActionNegativeExponents->setChecked(true);
	mainWin->ActionSortMinusLast->setChecked(false);
}

void QalculateResultDisplay::showParsedExpression() {
	TQString str = i18n("Parsed expression:");
	str += "\n";
	str += parsed_text;
	TQWhatsThis::display(str, curpos);
}
void QalculateResultDisplay::showInfo() {
	if(m_under_pointer) {
		TQWhatsThis::display(linkInfo(m_under_pointer, this, true), curpos);
	} else {
		TQWhatsThis::display(linkInfo(name_under_pointer, this, true), curpos);
	}
}
void QalculateResultDisplay::editVector() {
	mainWin->insertMatrixVector(m_under_pointer, true, false, true);
}
void QalculateResultDisplay::editMatrix() {
	mainWin->insertMatrixVector(m_under_pointer, false, false, true);
}

void QalculateResultDisplay::paletteChanged() {
	TQPalette p = kapp ? kapp->palette() : palette();
	p.setBrush(TQColorGroup::Base, p.brush(TQPalette::Normal, TQColorGroup::Background));
	p.setColor(TQColorGroup::Text, p.color(TQPalette::Normal, TQColorGroup::Foreground));
	setPalette(p);
}

void QalculateResultDisplay::virtual_hook(int, void*) {
}

void QalculateResultDisplay::focusInEvent(TQFocusEvent* fe) {
	TQTextBrowser::focusInEvent(fe);
	if(fe->reason() == TQFocusEvent::Tab || fe->reason() == TQFocusEvent::Backtab)
		selectAll(true);
}

void QalculateResultDisplay::focusOutEvent(TQFocusEvent* fe) {
	TQTextBrowser::focusOutEvent(fe);
	if(fe->reason() == TQFocusEvent::Tab || fe->reason() == TQFocusEvent::Backtab)
		selectAll(false);
}

void QalculateResultDisplay::onDoubleClicked(int, int) {
	curpos = TQCursor::pos();
	TQString str = anchorAt(viewportToContents(viewport()->mapFromGlobal(curpos)), TQt::AnchorName);
	if(!str.isEmpty()) {
		if(str[0] == '+') {
			selectAll(false);
			name_under_pointer = str;
			name_under_pointer.remove(0, 1);
			showInfo();
		} else {
			m_under_pointer = mainWin->getResultPart(str.toInt());
			if(m_under_pointer) {
				selectAll(false);
				if(m_under_pointer->isVector()) {
					if(m_under_pointer->isMatrix()) {
						editMatrix();
					} else {
						editVector();
					}
				} else {
					showInfo();
				}
			}
		}
	}
}

void QalculateResultDisplay::keyPressEvent(TQKeyEvent *e) {

	switch (e->key()) {
		case Key_Down:
		case Key_Up:
		case Key_Left:
		case Key_Right:
			// jump over TQTextEdit's key navigation breakage.
			// we're not interested in keyboard navigation within the text
			TQWidget::keyPressEvent(e);
			break;
		default:
			TQTextBrowser::keyPressEvent(e);
	}
	
}

TQSize QalculateResultDisplay::minimumSizeHint() const {

	TQSize ms = minimumSize();
	if((ms.width() > 0) && (ms.height() > 0))
		return ms;

	int w = 100;
	if(ms.width() > 0)
		w = ms.width();

	TQSimpleRichText rt("<font size=6>X</font>", font());
	rt.setWidth(100);
	TQSimpleRichText rt2("<font size=2>X</font>", font());
	rt2.setWidth(100);
	int h2 = (int) (rt2.height() / 1.5);
	h2 += h2 % 2;
	int h = rt.height() * 2 + h2 + 2 * frameWidth();
	if(h < ms.height())
		h = ms.height();

	return TQSize(w, h);
}

TQSize QalculateResultDisplay::sizeHint() const {
	return minimumSizeHint();
}


#include "qalculateresultdisplay.moc"
