/***************************************************************************
 *   Copyright (C) 2005-2006 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.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "kqalculate.h"
#include "preferences.h"
#include <string>
#include <twin.h>
#include <kuniqueapplication.h>
#include <tdeaboutdata.h>
#include <tdecmdlineargs.h>
#include <tdelocale.h>
#include <unistd.h>
#include <tqtextcodec.h>
#include <tqobject.h>
#include <tdemessagebox.h>
#include <tqtimer.h>
#include <klineedit.h>
#include <tqtooltip.h>
#include <tqclipboard.h>
#include "qalculateexpressionedit.h"
#include "qalculateresultdisplay.h"

#include "qalculate_tde_utils.h"

static const char description[] =  I18N_NOOP("A powerful and easy to use desktop calculator");

static const char version[] = VERSION;

static TDECmdLineOptions options[] = {
	{"c", 0, 0},
	{"clipboard", I18N_NOOP("Calculate X11-clipboard content (selected text)"), 0},
	{"+[Expression]", I18N_NOOP( "Initial expression to calculate" ), 0},
	TDECmdLineLastOption
};

extern KnownVariable *vans[5];
extern MathStructure *mstruct, *matrix_mstruct, *parsed_mstruct, *parsed_tostruct;
extern TQString result_text, result_history_text, parsed_text;
extern TQWidget *expressionWidget, *resultWidget, *statusWidget_l;
extern TQWidget *topWidget;
extern KQalculate *mainWin;

extern bool load_global_defs, fetch_exchange_rates_at_startup, first_time, first_qalculate_run;
extern bool b_busy;
extern bool canplot;
extern bool close_to_systray;

extern FILE *view_pipe_r, *view_pipe_w, *command_pipe_r, *command_pipe_w;
extern pthread_t view_thread, command_thread;
extern pthread_attr_t view_thread_attr, command_thread_attr;
extern bool command_thread_started;

extern TQValueVector<TQString> recent_functions_pre;
extern TQValueVector<TQString> recent_variables_pre;
extern TQValueVector<TQString> recent_units_pre;

TQTimer *error_timer;

void create_qalculate_window(KUniqueApplication *parent) {

	TQTextCodec::setCodecForCStrings(TQTextCodec::codecForName("utf8"));

	b_busy = false;

	new Calculator();
	if(TDEGlobal::locale()->decimalSymbol() == ",") CALCULATOR->useDecimalComma();
	CALCULATOR->place_currency_code_before = TDEGlobal::locale()->positivePrefixCurrencySymbol();

	//load application specific preferences
	load_preferences();

	mstruct = new MathStructure();
	parsed_mstruct = new MathStructure();
	parsed_tostruct = new MathStructure();
	parsed_tostruct->setUndefined();
	matrix_mstruct = new MathStructure();

	canplot = CALCULATOR->canPlot();

	mainWin = new KQalculate(parent);
	topWidget = mainWin;
	expressionWidget = (TQWidget*) mainWin->expressionEdit;
	resultWidget = (TQWidget*) mainWin->resultLabel;
	statusWidget_l = (TQWidget*) mainWin->statusLabel_l;
	
}

TQString parse_expression_arguments(TDECmdLineArgs *args) {
	TQString calc_arg;
	if(args->isSet("clipboard")) {
		calc_arg = tqApp->clipboard()->text(TQClipboard::Selection);
		if(calc_arg.isEmpty()) {
			calc_arg = tqApp->clipboard()->text(TQClipboard::Clipboard);
		}
	} else {
		for(int i = 0; i < args->count(); i++) {
			if(i > 0) {
				calc_arg += " ";
			}
			calc_arg += args->arg(i);
		}		
	}
	return calc_arg;
}

void start_qalculate() {	

	TDECmdLineArgs* args = TDECmdLineArgs::parsedArgs();
	TQString calc_arg = parse_expression_arguments(args);	
	args->clear();
	if(!calc_arg.isEmpty()) {
		mainWin->expressionEdit->setText(calc_arg);
	} else if(first_time) {
		mainWin->resultLabel->setText(i18n("Enter a mathematical expression above.<br>Ex. 5 + 2 / 3"));
	} else {
		mainWin->resultLabel->setText("<div align=\"right\"><font size=2>&nbsp;<br></font><font size=6>= 0</font></div>");
	}

	tqApp->processEvents();

	//exchange rates
	if(first_qalculate_run) {
		if(KMessageBox::questionYesNo(mainWin, i18n("You need to download exchange rates to be able to convert between different currencies. You can later get current exchange rates by selecting \"Update Exchange Rates\" under the File menu.\n\nDo you want to fetch exchange rates now from the Internet?"), i18n("Update exchange rates?")) == KMessageBox::Yes) {
			tqApp->processEvents();
			mainWin->fetch_exchange_rates(5);
		}
		tqApp->processEvents();
	} else if(fetch_exchange_rates_at_startup) {
		mainWin->fetch_exchange_rates(5);
		tqApp->processEvents();
	}
	
	CALCULATOR->loadExchangeRates();

	std::string ans_str = i18n("ans").ascii();
	vans[0] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(i18n("Temporary").ascii(), ans_str, m_undefined, i18n("Last Answer").ascii(), false));
	vans[0]->addName(i18n("answer").ascii());
	vans[0]->addName(ans_str + "1");
	vans[1] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(i18n("Temporary").ascii(), ans_str + "2", m_undefined, i18n("Answer 2").ascii(), false));
	vans[2] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(i18n("Temporary").ascii(), ans_str + "3", m_undefined, i18n("Answer 3").ascii(), false));
	vans[3] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(i18n("Temporary").ascii(), ans_str + "4", m_undefined, i18n("Answer 4").ascii(), false));
	vans[4] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(i18n("Temporary").ascii(), ans_str + "5", m_undefined, i18n("Answer 5").ascii(), false));

	//load global definitions
	if(load_global_defs && !CALCULATOR->loadGlobalDefinitions()) {
		KMessageBox::error(mainWin, i18n("Failed to load global definitions!"), i18n("Error"));
		tqApp->processEvents();
	}

	//load local definitions
	CALCULATOR->loadLocalDefinitions();

	//check for calculation errros regularly
	error_timer = new TQTimer(mainWin);
	TQObject::connect(error_timer, TQ_SIGNAL(timeout()), mainWin, TQ_SLOT(onErrorTimeout()));

	generate_units_tree_struct();
	generate_functions_tree_struct();
	generate_variables_tree_struct();
	mainWin->create_fmenu();
	mainWin->create_vmenu();
	mainWin->create_umenu();
	mainWin->create_toumenu();
	mainWin->create_setpmenu();
	
	for(int i = ((int) recent_functions_pre.size()) - 1; i >= 0; i--) {
		mainWin->function_inserted(CALCULATOR->getActiveFunction(recent_functions_pre[i].ascii()));
	}
	for(int i = ((int) recent_variables_pre.size()) - 1; i >= 0; i--) {
		mainWin->variable_inserted(CALCULATOR->getActiveVariable(recent_variables_pre[i].ascii()));
	}
	for(int i = ((int) recent_units_pre.size()) - 1; i >= 0; i--) {
		Unit *u = CALCULATOR->getActiveUnit(recent_units_pre[i].ascii());
		if(!u) u = CALCULATOR->getCompositeUnit(recent_units_pre[i].ascii());
		mainWin->unit_inserted(u);
	}
	
	mainWin->update_completion();

	int pipe_wr[] = {0, 0};
	pipe(pipe_wr);
	view_pipe_r = fdopen(pipe_wr[0], "r");
	view_pipe_w = fdopen(pipe_wr[1], "w");
	pthread_attr_init(&view_thread_attr);
	pthread_create(&view_thread, &view_thread_attr, view_proc, view_pipe_r);
	
	int pipe_wr2[] = {0, 0};
	pipe(pipe_wr2);
	command_pipe_r = fdopen(pipe_wr2[0], "r");
	command_pipe_w = fdopen(pipe_wr2[1], "w");
	pthread_attr_init(&command_thread_attr);
	command_thread_started = false;

	TQObject::connect(tqApp, TQ_SIGNAL(lastWindowClosed()), tqApp, TQ_SLOT(quit()));
	TQObject::connect(tqApp, TQ_SIGNAL(aboutToQuit()), mainWin, TQ_SLOT(aboutToQuit()));
	
	error_timer->start(100);
	
	if(!calc_arg.isEmpty()) {
		mainWin->execute_expression();
	}

}

class QalculateApp : public KUniqueApplication {

public: 

	bool started;

	QalculateApp() {started = false;}
	virtual ~QalculateApp() {}

	int newInstance() {		
		if(mainWin && started) {
		
			TDECmdLineArgs* args = TDECmdLineArgs::parsedArgs();
			TQString calc_arg = parse_expression_arguments(args);
			args->clear();

			KWin::WindowInfo info = KWin::windowInfo(mainWin->winId(), (unsigned long) NET::WMGeometry);
			KWin::setOnDesktop(mainWin->winId(), KWin::currentDesktop());
			mainWin->move(info.geometry().topLeft());			
			mainWin->setShown(true);
			mainWin->show();
			mainWin->raise();
			KWin::activateWindow(mainWin->winId());

			if(!calc_arg.isEmpty()) {
				mainWin->expressionEdit->setText(calc_arg);
				mainWin->execute_expression();
			}

			return 0;
		} else {						
			if(isRestored() && TDEMainWindow::canBeRestored(1)) {
				create_qalculate_window(this);
				setMainWidget(mainWin);
				mainWin->restore(1, false);
				start_qalculate();
			} else {
				create_qalculate_window(this);
				setMainWidget(mainWin);
				mainWin->show();
				start_qalculate();
			}
			started = true;			
			return KUniqueApplication::newInstance();
		}
		started = true;
		return 0;
	}

};

int main(int argc, char **argv) {

	mainWin = NULL;

	TDEAboutData about(PACKAGE, I18N_NOOP("Qalculate!"), version, description, TDEAboutData::License_GPL, "(C) 2005-2006 Niklas Knutsson", 0, "http://qalculate.sourceforge.net/", "nique769@users.sourceforge.net");
	about.addAuthor("Niklas Knutsson", 0, "nq@altern.org");
	TDECmdLineArgs::init(argc, argv, &about);
	TDECmdLineArgs::addCmdLineOptions(options);
	KUniqueApplication::addCmdLineOptions();	
		
	if(!QalculateApp::start()) {
 		return 0;
	}
	
	QalculateApp app;
	
	return app.exec();

}


