/***************************************************************************
 *   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 "qalculateunitsdialog.h"
#include "qalculate_tde_utils.h"
#include "qalculateeditunitdialog.h"
#include <vector>
#include <string>
#include <kpushbutton.h>
#include <tqsplitter.h>
#include <tqvbox.h>
#include <tqhbox.h>
#include <tdelistview.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <tqlayout.h>
#include <kcombobox.h>
#include <tqlabel.h>
#include <tqgroupbox.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <tdeapplication.h>
#include <kstdguiitem.h>

extern tree_struct unit_cats;
extern std::vector<void*> ia_units;
extern PrintOptions printops;
extern EvaluationOptions evalops;

QalculateUnitsDialog::QalculateUnitsDialog(TQWidget *parent, const char *name) : KDialog(parent, name, false) {

	unit_edit_dialog = NULL;
	selected_category = "";
	selected_unit = NULL;
	selected_to_unit = NULL;
	block_unit_convert = true;

	TQHBoxLayout *layout = new TQHBoxLayout(this, marginHint(), spacingHint());
	
	setCaption(i18n("Units"));

	TQVBoxLayout *leftLayout = new TQVBoxLayout(layout, spacingHint());

	TQSplitter *splitter = new TQSplitter(TQt::Horizontal, this);
	splitter->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding);
	leftLayout->addWidget(splitter);

	categoryView = new TDEListView(splitter);
	categoryView->addColumn(i18n("Category"));
	categoryView->setRootIsDecorated(false);

	unitView = new TDEListView(splitter);
	unitView->addColumn(i18n("Unit"));
	unitView->addColumn(i18n("Names"));
	unitView->addColumn(i18n("Base"));
	unitView->setRootIsDecorated(false);

	convertGroup = new TQGroupBox(3, TQt::Horizontal, i18n("Conversion"), this);
	convertGroup->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Minimum);
	convertGroup->setInsideSpacing(spacingHint());
	convertGroup->setInsideMargin(spacingHint());
	convertButton = new KPushButton(TQIconSet(SmallIcon("reload")), "", convertGroup);
	fromEdit = new KLineEdit("1", convertGroup);
	fromEdit->setAlignment(TQt::AlignRight);
	fromEdit->setMinimumWidth(100);
	fromLabel = new TQLabel(convertGroup);
	equalsLabel = new TQLabel("=", convertGroup);
	equalsLabel->setAlignment(TQt::AlignRight);
	toEdit = new KLineEdit("1", convertGroup);
	toEdit->setAlignment(TQt::AlignRight);
	toEdit->setMinimumWidth(100);
	toCombo = new KComboBox(convertGroup);
	leftLayout->addWidget(convertGroup);

	TQVBoxLayout *buttonLayout = new TQVBoxLayout(layout, spacingHint());

	newButton = new TQPushButton(i18n("New"), this);
	buttonLayout->addWidget(newButton);
	editButton = new TQPushButton(i18n("Edit"), this);
	editButton->setEnabled(false);
	buttonLayout->addWidget(editButton);
	deleteButton = new TQPushButton(i18n("Delete"), this);
	deleteButton->setEnabled(false);
	buttonLayout->addWidget(deleteButton);
	deactivateButton = new TQPushButton(i18n("Deactivate"), this);
	deactivateButton->setEnabled(false);
	buttonLayout->addWidget(deactivateButton);
	insertButton = new TQPushButton(i18n("Insert"), this);
	insertButton->setEnabled(false);
	buttonLayout->addWidget(insertButton);
	convertResultButton = new TQPushButton(i18n("Convert Result"), this);
	convertResultButton->setEnabled(false);
	buttonLayout->addWidget(convertResultButton);
	buttonLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Minimum, TQSizePolicy::Expanding));
	helpButton = new KPushButton(KStdGuiItem::help(), this);
	buttonLayout->addWidget(helpButton);
	buttonClose = new KPushButton(KStdGuiItem::close(), this);
	buttonLayout->addWidget(buttonClose);
	
	fromEdit->setFocus();

	resize(TQSize(675, 425).expandedTo(size()));

	connect(buttonClose, TQ_SIGNAL(clicked()), this, TQ_SLOT(close()));
	connect(newButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(newUnit()));
	connect(editButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(editUnit()));
	connect(deleteButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(deleteUnit()));
	connect(deactivateButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(deactivateUnit()));
	connect(insertButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(insertUnit()));
	connect(convertResultButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(convertResult()));
	connect(unitView, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(unitSelected()));
	connect(unitView, TQ_SIGNAL(doubleClicked(TQListViewItem*)), this, TQ_SLOT(unitDoubleClicked(TQListViewItem*)));
	connect(categoryView, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(categorySelected()));
	connect(toCombo, TQ_SIGNAL(activated(int)), this, TQ_SLOT(toUnitSelected(int)));
	connect(convertButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(convertClicked()));
	connect(fromEdit, TQ_SIGNAL(returnPressed()), this, TQ_SLOT(convert()));
	connect(toEdit, TQ_SIGNAL(returnPressed()), this, TQ_SLOT(convertReversed()));
	connect(fromEdit, TQ_SIGNAL(lostFocus()), this, TQ_SLOT(convert()));
	connect(toEdit, TQ_SIGNAL(lostFocus()), this, TQ_SLOT(convertReversed()));
	connect(helpButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotHelp()));

}

QalculateUnitsDialog::~QalculateUnitsDialog() {
}

void QalculateUnitsDialog::slotHelp() {
	TDEApplication::kApplication()->invokeHelp("qalculate-managers");
}

void QalculateUnitsDialog::updateUnitTree() {
	unitItems.clear();
	comboItems.clear();
	categoryItems.clear();
	categoryView->clear();
	TQListViewItem *i = new TDEListViewItem(categoryView, i18n("All")), *i2;
	categoryItems[i] = i18n("All");
	i->setOpen(true);
	TQString str;
	tree_struct *item, *item2;
	unit_cats.it = unit_cats.items.begin();
	if(unit_cats.it != unit_cats.items.end()) {
		item = &*unit_cats.it;
		++unit_cats.it;
		item->it = item->items.begin();
	} else {
		item = NULL;
	}
	str = "";
	i2 = i;
	while(item) {
		str += "/";
		str += item->item.c_str();
		i = new TDEListViewItem(i2, item->item.c_str());
		i->setOpen(false);
		categoryItems[i] = str;
		if(str == selected_category) {
			categoryView->ensureItemVisible(i);
			categoryView->setSelected(i, true);
		}
		while(item && item->it == item->items.end()) {
			int str_i = str.findRev("/");
			if(str_i < 0) {
				str = "";
			} else {
				str.truncate(str_i);
			}
			item = item->parent;
			i = i->parent();
			i2 = i;
		}
		if(item) {
			item2 = &*item->it;
			if(item->it == item->items.begin())
				i2 = i;
			++item->it;
			item = item2;
			item->it = item->items.begin();
		}
	}
	if(!unit_cats.objects.empty()) {
		//add "Uncategorized" category if there are units without category
		i = new TDEListViewItem(categoryView, i18n("Uncategorized"));
		categoryItems[i] = i18n("Uncategorized");
		if(selected_category == i18n("Uncategorized")) {
			categoryView->ensureItemVisible(i);
			categoryView->setSelected(i, true);
		}
	}
	if(!ia_units.empty()) {
		//add "Inactive" category if there are inactive units
		i = new TDEListViewItem(categoryView, i18n("Inactive"));
		categoryItems[i] = i18n("Inactive");
		if(selected_category == i18n("Inactive")) {
			categoryView->ensureItemVisible(i);
			categoryView->setSelected(i, true);
		}
	}
	if(!categoryView->selectedItem()) {
		//if no category has been selected (previously selected has been renamed/deleted), select "All"
		selected_category = i18n("All");
		TQListViewItemIterator it(categoryView);
		if(it.current())
			categoryView->setSelected(it.current(), true);
	}
}

#define UPDATE_SELECTED_UNIT		TQListViewItem *i = unitView->selectedItem(); if(!i) return;	selected_unit = unitItems[i]; if(!selected_unit) return;
#define CHECK_IF_UNIT_STILL_THERE			if(!CALCULATOR->stillHasUnit(selected_unit)) {KMessageBox::error(this, i18n("Unit does not exist anymore.")); emit unitsChanged(); return;}

void QalculateUnitsDialog::insertUnit() {
	UPDATE_SELECTED_UNIT
	CHECK_IF_UNIT_STILL_THERE
	emit insertRequest(selected_unit);
}

void QalculateUnitsDialog::convertResult() {
	UPDATE_SELECTED_UNIT
	CHECK_IF_UNIT_STILL_THERE
	emit convertRequest(selected_unit);
}

void QalculateUnitsDialog::deactivateUnit() {
	UPDATE_SELECTED_UNIT
	CHECK_IF_UNIT_STILL_THERE
	selected_unit->setActive(!selected_unit->isActive());
	emit unitsChanged();
}

void QalculateUnitsDialog::deleteUnit() {
	UPDATE_SELECTED_UNIT
	CHECK_IF_UNIT_STILL_THERE
	if(selected_unit->isLocal()) {
		if(selected_unit->isUsedByOtherUnits()) {
			//do not delete units that are used by other units
			KMessageBox::error(this, i18n("Cannot delete unit as it is needed by other units."));
			return;
		}
		//ensure that all references are removed in Calculator
		selected_unit->destroy();
		//update menus and trees
		emit unitsChanged();
	}
}


void QalculateUnitsDialog::editUnit() {
	UPDATE_SELECTED_UNIT
	CHECK_IF_UNIT_STILL_THERE
	if(!unit_edit_dialog) {
		unit_edit_dialog = new QalculateEditUnitDialog(this);
	}
	Unit *u = unit_edit_dialog->editUnit(TQString::null, selected_unit);
	if(u) {
		selected_unit = u;
		if(!u->isActive()) {
			selected_category = i18n("Inactive");
		} else if(u->category().empty()) {
			selected_category = i18n("Uncategorized");
		} else {
			selected_category = "/";
			selected_category += u->category().c_str();
		}
		emit unitsChanged();
	}
}

void QalculateUnitsDialog::newUnit() {
	if(!unit_edit_dialog) {
		unit_edit_dialog = new QalculateEditUnitDialog(this);
	}
	Unit *u = NULL;
	if(selected_category.isEmpty() || selected_category[0] != '/') {
		u = unit_edit_dialog->editUnit();
	} else {
		TQString str = selected_category;
		str.remove(0, 1);
		u = unit_edit_dialog->editUnit(str);
	}
	if(u) {
		selected_unit = u;
		if(!u->isActive()) {
			selected_category = i18n("Inactive");
		} else if(u->category().empty()) {
			selected_category = i18n("Uncategorized");
		} else {
			selected_category = "/";
			selected_category += u->category().c_str();
		}
		emit unitsChanged();
	}
}

void QalculateUnitsDialog::unitDoubleClicked(TQListViewItem*i) {
	selected_unit = unitItems[i];
	if(!selected_unit)
		return;
	CHECK_IF_UNIT_STILL_THERE
	if(!unit_edit_dialog) {
		unit_edit_dialog = new QalculateEditUnitDialog(this);
	}
	Unit *u = unit_edit_dialog->editUnit(TQString::null, selected_unit);
	if(u) {
		selected_unit = u;
		if(!u->isActive()) {
			selected_category = i18n("Inactive");
		} else if(u->category().empty()) {
			selected_category = i18n("Uncategorized");
		} else {
			selected_category = "/";
			selected_category += u->category().c_str();
		}
		emit unitsChanged();
	}
}


void QalculateUnitsDialog::unitSelected() {
	TQListViewItem *selected = unitView->selectedItem();
	if(selected) {
		Unit *u = unitItems[selected];
		if(!CALCULATOR->stillHasUnit(u)) {
			KMessageBox::error(this, i18n("Unit does not exist anymore."));
			selected_unit = NULL;
			emit unitsChanged();
			return;
		}
		//remember selection
		selected_unit = u;
		editButton->setEnabled(true);
		insertButton->setEnabled(u->isActive());
		convertResultButton->setEnabled(true);
		deleteButton->setEnabled(u->isLocal());
		deactivateButton->setEnabled(true);
		convertGroup->setEnabled(true);
		fromLabel->setText(u->print(true, printops.abbreviate_names, printops.use_unicode_signs, &can_display_unicode_string_function, (void*) fromLabel).c_str());
		if(u->isActive())
			deactivateButton->setText(i18n("Deactivate"));
		else
			deactivateButton->setText(i18n("Activate"));
	} else {
		editButton->setEnabled(false);
		insertButton->setEnabled(false);
		deleteButton->setEnabled(false);
		deactivateButton->setEnabled(false);
		convertResultButton->setEnabled(false);
		convertGroup->setEnabled(false);
		selected_unit = NULL;
	}
	if(!block_unit_convert) convert();
}

void QalculateUnitsDialog::addUnitTreeItem(Unit *u) {
	TQString snames, sbase;
	//display name, plural name and short name in the second column
	AliasUnit *au;
	for(size_t i = 1; i <= u->countNames(); i++) {
		if(i > 1) snames += " / ";
		snames += u->getName(i).name.c_str();
	}
	//depending on unit type display relation to base unit(s)
	switch(u->subtype()) {
		case SUBTYPE_COMPOSITE_UNIT: {
			snames = "";
			sbase = ((CompositeUnit*) u)->print(false, true, printops.use_unicode_signs, &can_display_unicode_string_function, (void*) unitView).c_str();
			break;
		}
		case SUBTYPE_ALIAS_UNIT: {
			au = (AliasUnit*) u;
			sbase = au->firstBaseUnit()->preferredDisplayName(printops.abbreviate_names, printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) unitView).name.c_str();
			if(au->firstBaseExponent() != 1) {
				sbase += POWER;
				sbase += TQString::number(au->firstBaseExponent());
			}
			break;
		}
		case SUBTYPE_BASE_UNIT: {
			sbase = "";
			break;
		}
	}
	TQListViewItem *i = new TDEListViewItem(unitView, u->title(true).c_str(), snames, sbase);
	unitItems[i] = u;
	if(u == selected_unit) {
		unitView->setSelected(i, true);
	}
}


void QalculateUnitsDialog::categorySelected() {
	block_unit_convert = true;
	TQListViewItem *selected = categoryView->selectedItem();
	bool no_cat = false, b_all = false, b_inactive = false;
	unitView->clear();
	unitItems.clear();
	if(!selected) {
		selected_category = "";
		unitSelected();
		return;
	}
	selected_category = categoryItems[selected];
	if(selected_category == i18n("All")) {
		b_all = true;
	} else if(selected_category == i18n("Uncategorized")) {
		no_cat = true;
	} else if(selected_category == i18n("Inactive")) {
		b_inactive = true;
	}
	if(!b_all && !no_cat && !b_inactive && selected_category[0] == '/') {
		std::string str = selected_category.ascii();
		str.erase(str.begin());
		for(size_t i = 0; i < CALCULATOR->units.size(); i++) {
			if(CALCULATOR->units[i]->isActive() && CALCULATOR->units[i]->category().substr(0, selected_category.length() - 1) == str) {
				addUnitTreeItem(CALCULATOR->units[i]);
			}
		}
	} else {
		std::string str = selected_category.ascii();
		for(size_t i = 0; i < CALCULATOR->units.size(); i++) {
			if((b_inactive && !CALCULATOR->units[i]->isActive()) || (CALCULATOR->units[i]->isActive() && (b_all || (no_cat && CALCULATOR->units[i]->category().empty()) || (!b_inactive && CALCULATOR->units[i]->category() == str)))) {
				addUnitTreeItem(CALCULATOR->units[i]);
			}
		}
	}
	if(!selected_unit || !unitView->selectedItem()) {
		TQListViewItemIterator it(unitView);
		if(it.current())
			unitView->setSelected(it.current(), true);
	}
	//generate convert to menu
	toCombo->blockSignals(true);
	toCombo->clear();
	comboItems.clear();
	int i = 0, h = -1;
	//add all units in units tree to menu
	TQListViewItemIterator it(unitView);
	Unit *u;
	while(it.current()) {
		u = unitItems[it.current()];
		if(!selected_to_unit) {
			selected_to_unit = u;	
		}
		if(u) {
			toCombo->insertItem(it.current()->text(0));
			comboItems.push_back(u);
			if(selected_to_unit == u)
				h = i;
		}
		i++;
		++it;
	}

	//if no items were added to the menu, reset selected unit
	if(i == 0) {
		selected_to_unit = NULL;
	} else {
		//if no menu item was selected, select the first
		if(h < 0) {
			h = 0;
			TQListViewItemIterator it2(unitView);
			if(it2.current()) {
				selected_to_unit = unitItems[it2.current()];
			} else {
				selected_to_unit = NULL;
			}
		}
		toCombo->setCurrentItem(h);
	}
	toCombo->blockSignals(false);
	block_unit_convert = false;
	//update conversion display
	convert();
}

void QalculateUnitsDialog::toUnitSelected(int index) {
	if(index < (int) comboItems.size()) {
		selected_to_unit = comboItems[index];
		convert();
	}
}
void QalculateUnitsDialog::convertClicked() {
	if(toEdit->hasFocus()) {
		convertReversed();
	} else {
		convert();
	}
}
void QalculateUnitsDialog::convert() {
	if(selected_unit && selected_to_unit) {
		//determine conversion direction
		bool b = false;
		if(selected_unit == selected_to_unit) {
			toEdit->setText(fromEdit->text());
		} else {
			EvaluationOptions eo;
			eo.approximation = APPROXIMATION_APPROXIMATE;
			eo.parse_options.angle_unit = evalops.parse_options.angle_unit;
			PrintOptions po;
			po.is_approximate = &b;
			po.number_fraction_format = FRACTION_DECIMAL;
			MathStructure v_mstruct = CALCULATOR->convert(CALCULATOR->unlocalizeExpression(fromEdit->text().ascii(), evalops.parse_options), selected_unit, selected_to_unit, eo);
			toEdit->setText(v_mstruct.print(po).c_str());
			b = b || v_mstruct.isApproximate();
		}
		if(b && printops.use_unicode_signs && can_display_unicode_string_function(SIGN_ALMOST_EQUAL, (void*) equalsLabel)) {
			equalsLabel->setText(SIGN_ALMOST_EQUAL);
		} else {
			equalsLabel->setText("=");
		}
	}
}
void QalculateUnitsDialog::convertReversed() {
	if(selected_unit && selected_to_unit) {
		bool b = false;
		if(selected_unit == selected_to_unit) {
			fromEdit->setText(toEdit->text());
		} else {
			EvaluationOptions eo;
			eo.approximation = APPROXIMATION_APPROXIMATE;
			eo.parse_options.angle_unit = evalops.parse_options.angle_unit;
			PrintOptions po;
			po.is_approximate = &b;
			po.number_fraction_format = FRACTION_DECIMAL;
			MathStructure v_mstruct = CALCULATOR->convert(CALCULATOR->unlocalizeExpression(toEdit->text().ascii(), evalops.parse_options), selected_to_unit, selected_unit, eo);
			fromEdit->setText(v_mstruct.print(po).c_str());
			b = b || v_mstruct.isApproximate();
		}
		if(b && printops.use_unicode_signs && can_display_unicode_string_function(SIGN_ALMOST_EQUAL, (void*) equalsLabel)) {
			equalsLabel->setText(SIGN_ALMOST_EQUAL);
		} else {
			equalsLabel->setText("=");
		}
	}
}

void QalculateUnitsDialog::keyPressEvent(TQKeyEvent *e) {
	if(e->key() == Key_Return) {
		convertClicked();
		e->accept();
		return;
	}
	KDialog::keyPressEvent(e);
}


#include "qalculateunitsdialog.moc"
