#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QFontDialog>
#include <ribbon.h>
#include <qpiconfig.h>
#include <qad_types.h>
#include <qad_locations.h>
#include "serverdesigner.h"
#include "SH_ui_loader.h"
#include "SH_plugins_manager.h"
#include "script_analyzer.h"

enum ItemType {
	itObject = 1,
	itProperty,
	itPacket,
	itGroupProperties = 0x10,
	itGroupEvents,
	itGroupCommands,
	itGroupObjects,
};



ServerDesigner::ServerDesigner(): DesignerMainWindow("Server Designer"), Ui::ServerDesigner() {
	setupUi(this);
	setRecentMenu(menuOpen_recent);
	CORE->widgetScripts = widgetScripts;
	actionDownload_assembly->setEnabled(false); // TODO: не реализованный функционал
	new Ribbon(this);
	conn_edit.setWindowTitle(tr("Server Designer - Connection editor"));
	conn_edit.setWindowIcon(actionConnection_editor->icon());
	widgetScripts->codeEdit->setCreatableObjects(engine.creatableObjects());
	connect(widgetServer, SIGNAL(scriptError(int,QString,QString,QString)), this, SLOT(scriptError(int,QString,QString,QString)));
	connect(&watcher, SIGNAL(fileChanged(QString)), this, SLOT(on_actionReload_triggered()));
	connect(&engine, SIGNAL(error(int,QString,QString,QString)), this, SLOT(scriptError(int,QString,QString,QString)));
	connect(widgetScripts, SIGNAL(currentScriptChanged(QString)), this, SLOT(currentScriptChanged(QString)), Qt::QueuedConnection);
	connect(widgetServer, SIGNAL(requestBAF(QByteArray*)), this, SLOT(requestBAF(QByteArray*)));
	connect(actionOpen_script, SIGNAL(triggered(bool)), widgetScripts, SLOT(openScript()));
	connect(actionSave_script, SIGNAL(triggered(bool)), widgetScripts, SLOT(saveCurrentScript()));
	connect(actionSave_script_as, SIGNAL(triggered(bool)), widgetScripts, SLOT(saveAsCurrentScript()));
	connect(actionClose_script, SIGNAL(triggered(bool)), widgetScripts, SLOT(closeCurrentScript()));
	connect(CORE, SIGNAL(stopTest()), this, SLOT(stopTest()));
	connect(CORE, SIGNAL(saveProject()), this, SLOT(saveProject()));
	connect(CORE, SIGNAL(loadFile(QString)), this, SLOT(loadFile(const QString &)));
	connect(CORE, SIGNAL(loadScript(QString)), widgetScripts, SLOT(loadFile(const QString &)));
	RUNTIME->plugins_manager->localInterface().loadPlugins();
	engine.setDebugWidget(logView);
	engine.preparePlugins();
	auto pil = RUNTIME->plugins_manager->localInterface().getPlugins(SHPluginBase::Script);
	for (const auto & i: pil) {
		widgetScripts->codeEdit->ac_plugins << i.plugin;
	}
	widgetScripts->codeEdit->updateACPlugins();
	addSnippet("Server start", "function server_started()");
	addSnippet("Prepare UI for user role", "function server_prepare_ui_for_role(role)");
	session.addMainWidget(&device_stream_dialog);
	session.addMainWidget(&user_roles_dialog);
	session.setFile(QAD::userPath(QAD::ltConfig, "session"));
	loadSession();
	emit CORE->projectChanged();
}


bool ServerDesigner::event(QEvent * e) {
	if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish)
		;//_setIconSize();
	return EMainWindow::event(e);
}


ServerDesigner::~ServerDesigner() {
	saveSession();
}


bool ServerDesigner::eventFilter(QObject * o, QEvent * e) {
	return EMainWindow::eventFilter(o, e);
}


void ServerDesigner::procArguments(const QStringList & args) {
	bool is_shs = false, is_shs_db = false;
	foreach (QString a, args) {
		if (a == "-shs") {
			is_shs = true;
			continue;
		}
		if (a == "-shs_db") {
			is_shs_db = true;
			continue;
		}
		if (is_shs) {
			RUNTIME->shs_file = a;
			is_shs = false;
		}
		if (is_shs_db) {
			RUNTIME->shs_db_file = a;
			is_shs_db = false;
		}
	}
}


void ServerDesigner::on_widgetScripts_titleChangeRequest(QString title) {
	setWindowTitle(QString("Server Designer - %1[*] (\"%2\")").arg(QFileInfo(file_name).baseName(), title));
}


void ServerDesigner::on_actionNew_script_triggered() {
	widgetScripts->newScript();
	if (!file_name.isEmpty() && RUNTIME->server_project.qs_main.isEmpty()) {
		QFileInfo fi(file_name);
		widgetScripts->saveCurrentScriptTo(fi.dir().path() + "/" + fi.baseName() + ".qs");
		if (!widgetScripts->currentFile().isEmpty()) {
			RUNTIME->server_project.addScriptFile(widgetScripts->currentFile());
			emit CORE->projectChanged();
		}
	}
}


void ServerDesigner::on_actionSave_all_scripts_triggered() {
	widgetScripts->saveAllScripts();
}


void ServerDesigner::reset(bool full) {
	RUNTIME->widget_list.clear();
	file_name.clear();
	widgetScripts->closeAll();
	treeVariables->clear();
	widgetForm->reset();
	widgetScripts->codeEdit->form_ui = nullptr;
	widgetScripts->codeEdit->setBlockObjects();
	watchUI();
	loadDB(QString());
	RUNTIME->server_project = SHS::ServerProject();
	emit CORE->projectChanged();
}


bool ServerDesigner::load(const QString & path) {
	if (path.isEmpty()) return false;
	SHS::ServerProject p;
	QFile f(path);
	if (f.exists()) {
		if (!p.load(path))
			return false;
		bool proj_changed = false;

		if (RUNTIME->shs_file.isEmpty())
			RUNTIME->shs_file = p.shs;
		else if (p.shs != RUNTIME->shs_file) {
			p.shs = RUNTIME->shs_file;
			proj_changed = true;
		}
		if (RUNTIME->shs_db_file.isEmpty())
			RUNTIME->shs_db_file = p.shs_db;
		else if (p.shs_db != RUNTIME->shs_db_file) {
			p.shs_db = RUNTIME->shs_db_file;
			proj_changed = true;
		}

		if (proj_changed)
			p.save(path);
	}
	//qDebug() << "shs file" << p.shs;
	//qDebug() << "shs_db file" << p.shs_db;
	QApplication::setOverrideCursor(Qt::WaitCursor);
	widgetScripts->closeAll();
	QDir::setCurrent(QFileInfo(path).dir().absolutePath());
	QDir().mkdir("res");
	RUNTIME->server_project = SHS::ServerProject();
	file_name = path;
	p.makeEmptyNames(QDir::current().relativeFilePath(path));
	engine.setDir(QDir::current().absolutePath());
	loadFile(p.ui);
	loadFile(p.dat);
	loadFile(p.var);
	loadFile(p.cm);
	for (const auto & s: p.qs)
		loadFile(s);
	loadFile(p.qs_main);
	RUNTIME->server_project = p;
	emit CORE->projectChanged();
	if (!f.exists())
		saveFile();
	setChanged(false);
	QApplication::restoreOverrideCursor();
	return true;
}


bool ServerDesigner::save(const QString & path) {
	if (!RUNTIME->server_project.save(path))
		return false;
	file_name = path;
	saveVAR(RUNTIME->server_project.var);
	widgetScripts->saveAllScripts();
	setChanged(false);
	QDir::setCurrent(QFileInfo(path).dir().absolutePath());
	return true;
}


void ServerDesigner::loadFiles(const QStringList & path) {
	for (const auto & s: path)
		loadFile(s);
}


void ServerDesigner::loadFile(const QString & path) {
	if (path.isEmpty()) return;
	QFile f(path);
	QFileInfo fi(path);
	if (fi.completeSuffix().toLower() == "sdp") {
		load(path);
		return;
	}
	if (fi.completeSuffix().toLower() == "ui") {
		RUNTIME->server_project.ui = QDir::current().relativeFilePath(path);
		noWatchUI();
		widgetForm->loadFile(path);
		watchUI();
	}
	if (fi.completeSuffix().toLower() == "var") {
		loadVAR(path);
	}
	if (!fi.exists()) return;
	if (fi.completeSuffix().toLower() == "baf") {
		loadFiles(ass.unpack(path));
	}
	if (fi.completeSuffix().toLower() == "dat") {
		loadDB(path);
	}
	if (fi.completeSuffix().toLower() == "cm") {
		loadCM(path);
	}
	if (fi.completeSuffix().toLower() == "qs") {
		widgetScripts->loadFile(QDir::current().relativeFilePath(path));
	}
	emit CORE->projectChanged();
}


void ServerDesigner::loadingSession(QPIConfig & conf) {
	setRecentFiles(conf.getValue("recent_files").toStringList());
	QString fs = conf.getValue("editor_font").toString();
	if (!fs.isEmpty()) {
		QFont f = widgetScripts->codeEdit->editorFont();
		f.fromString(fs);
		widgetScripts->codeEdit->setEditorFont(f);
	}
}


void ServerDesigner::savingSession(QPIConfig & conf) {
	conf.setValue("recent_files", recentFiles());
	conf.setValue("editor_font", widgetScripts->codeEdit->editorFont().toString());
}


void ServerDesigner::on_actionLoad_assembly_triggered() {
	QString f = QFileDialog::getOpenFileName(this, "Select file", "", "Assembly(*.baf)");
	if (f.isEmpty()) return;
	QApplication::setOverrideCursor(Qt::WaitCursor);
	loadFiles(ass.unpack(f));
	QApplication::restoreOverrideCursor();
	setChanged(false);
}


void ServerDesigner::on_actionBuild_assembly_triggered() {
	QString f = QFileDialog::getSaveFileName(this, "Select file", "", "Assembly(*.baf)");
	if (f.isEmpty()) return;
	if (!f.endsWith(".baf")) f += ".baf";
	makeAssembly(f);
}


void ServerDesigner::on_actionExtract_assembly_triggered() {
	QString f = QFileDialog::getOpenFileName(this, "Select file", "", "Assembly(*.baf)");
	if (f.isEmpty()) return;
	QString op = QFileDialog::getExistingDirectory(this, "Select output directory", "");
	if (op.isEmpty()) return;
	QApplication::setOverrideCursor(Qt::WaitCursor);
	SHAssembly ass;
	QDir dir(op + "/" + QFileInfo(f).fileName() + ".files");
	dir.mkpath(dir.absolutePath());
	ass.unpack(f, dir.absolutePath());
	QApplication::restoreOverrideCursor();
}


void ServerDesigner::on_actionUpload_assembly_triggered() {
	QByteArray baf;
	requestBAF(&baf);
	if (baf.isEmpty())
		return;
	UploadDialog dlg;
	dlg.exec(baf);
}


void ServerDesigner::on_actionDownload_assembly_triggered() {

}


void ServerDesigner::on_actionPreferences_triggered() {
	QFont f = widgetScripts->codeEdit->editorFont();
	bool ok = false;
	f = QFontDialog::getFont(&ok, f, this, tr("Select editor font"));
	if (!ok) return;
	widgetScripts->codeEdit->setEditorFont(f);
}


void ServerDesigner::on_actionAdd_script_to_project_triggered() {
	if (!RUNTIME->server_project.addScriptFile(widgetScripts->currentFile()))
		return;
	emit CORE->projectChanged();
}


void ServerDesigner::on_actionUser_roles_triggered() {
	if (!user_roles_dialog.exec(RUNTIME->server_project.roles)) return;
	RUNTIME->server_project.roles = user_roles_dialog.getRoles();
	saveProject();
}


void ServerDesigner::on_treeVariables_itemClicked(QTreeWidgetItem * item, int col) {
	if (!item) return;
	Qt::ItemFlags f = item->flags();
	f.setFlag(Qt::ItemIsEditable, col != 1);
	item->setFlags(f);
}


void ServerDesigner::on_treeVariables_itemDoubleClicked(QTreeWidgetItem * item, int col) {
	if (!item || col != 1) return;
	var_dialog.set(item->text(col));
	if (var_dialog.exec() != QDialog::Accepted) return;
	item->setText(col, var_dialog.get());
}


void ServerDesigner::on_treeVariables_itemSelectionChanged() {
	int sz = treeVariables->selectedItems().size();
	buttonVarDel->setEnabled(sz > 0);
	buttonVarClone->setEnabled(sz > 0);
}


QList<SHS::ScriptVariable> ServerDesigner::variables() const {
	QList<SHS::ScriptVariable> ret;
	SHS::ScriptVariable v;
	for (int i = 0; i < treeVariables->topLevelItemCount(); ++i) {
		QTreeWidgetItem * ti = treeVariables->topLevelItem(i);
		QString dur = "inf";
		if (!ti->text(1).isEmpty())
			dur = ti->text(1);
		v = SHS::ScriptVariable(ti->text(0), dur, ti->text(3));
		v.display_name = ti->text(2);
		ret << v;
	}
	return ret;
}


QStringList ServerDesigner::variableNames() const {
	QStringList ret;
	for (int i = 0; i < treeVariables->topLevelItemCount(); ++i)
		ret << treeVariables->topLevelItem(i)->text(0);
	return ret;
}


void ServerDesigner::stopTest() {
	RUNTIME->connection.stop();
	RUNTIME->connection.boundedDevices().forEach([](PIIODevice * d){d->close();});
	engine.remove();
	widgetForm->stopTest();
}


void ServerDesigner::requestBAF(QByteArray * content) {
	QString tfp = QDir::tempPath() + "/sh_assembly.baf";
	if (!makeAssembly(tfp)) return;
	QFile f(tfp);
	if (!f.open(QIODevice::ReadOnly)) {
		QMessageBox::critical(this, "Make assembly", tr("Can`t read assembly \"%1\"!").arg(tfp));
		return;
	}
	QAD::CursorOverrider curov;
	*content = f.readAll();
	f.close();
	f.remove();
}


bool ServerDesigner::makeAssembly(const QString & f) {
	if (!engine.loadFromProject(RUNTIME->server_project)) {
		QMessageBox::critical(this, "Make assembly", tr("Errors in script!"));
		return false;
	}
	QAD::CursorOverrider curov;

	/*QString script = engine.fullScript();
	ScriptAnalyzer::removeStrings(script);
	ScriptAnalyzer::removeComments(script);
	QFile _f("___.txt");
	_f.open(QIODevice::ReadWrite);
	_f.resize(0);
	_f.write(script.toUtf8());*/

	noWatchUI();
	SHAssembly ass;
	QString uip = QDir::tempPath() + "/" + QFileInfo(RUNTIME->server_project.ui).fileName() + "p";
	QFile::remove(uip);
	QFile::copy(RUNTIME->server_project.ui, uip);
	QString rp = SHSUiLoader::prepareUI(uip, RUNTIME->server_project.ui);
	watchUI();
	QString sdpp = QDir::tempPath() + "/" + QFileInfo(file_name).fileName();
	RUNTIME->server_project.used_plugins = ScriptAnalyzer::getUsedPlugins(engine.fullScript());
	qDebug() << "found used plugins:" << RUNTIME->server_project.used_plugins;
	RUNTIME->server_project.save(sdpp);
	QStringList reslist = RUNTIME->server_project.res;
	reslist << "img/";
	ass.pack(f, RUNTIME->server_project.getAssFiles() << rp << uip << sdpp << RUNTIME->server_project.ui << RUNTIME->server_project.shs << RUNTIME->server_project.shs_db, reslist);
	QFile::remove(rp);
	QFile::remove(uip);
	QFile::remove(sdpp);
	return true;
}


void ServerDesigner::loadDB(const QString & path) {
	//qDebug() << "load DB" << path;
	RUNTIME->server_project.dat = QDir::current().relativeFilePath(path);
	objectTree->clear();
	if (!QFile::exists(path)) return;
	RUNTIME->setServerData(loadFromFile<SHS::ServerData>(path));
	//RUNTIME->designer_info.setDatabase(database);
	engine.createBlockObjects();
	engine.prepare();
	widgetScripts->codeEdit->setBlockObjects();
	QMap<QString, SHS::Object> pol;
	auto pil = RUNTIME->plugins_manager->localInterface().getPlugins(SHPluginBase::Script);
	for (const auto & p: pil) {
		SHS::Object & po(pol[p.plugin->name()]);
		po = ((SHPluginBaseScript*)p.plugin)->object;
		po.swapIOFlags();
	}
	widgetScripts->codeEdit->setPlugins(pol);
	widgetScripts->codeEdit->updateACObjects();
	objectTree->create(RUNTIME->server_data.blocks);
	//loadCM(RUNTIME->server_project.cm);
}


void ServerDesigner::loadCM(const QString & path) {
	RUNTIME->server_project.cm = QDir::current().relativeFilePath(path);
	if (path.isEmpty()) return;
	QPIConfig f(path, QIODevice::ReadWrite);
	//if (!f.isOpen()) return;
	conn_edit.setModel(f.getValue("model").toByteArray());
	addDevices();
	conn_edit.recreateConnection();
	f.resize(0);
	f.readAll();
	QString cs = conn_edit.configuration();
	PIString pcs = Q2PIString(cs);
	if (conn_edit.name().isEmpty()) conn_edit.setName("conn");
	f.setValue("name", conn_edit.name(), "s", false);
	f.setValue("model", conn_edit.model(), false);
	f.setValue("config", cs.toLatin1(), false);
	f.writeAll();
	RUNTIME->connection.configureFromString(&pcs, Q2PIString(conn_edit.name()));
}


QPair<QString, QString> splitVar(const QString & s) {
	QString dur = "inf", com = s;
	if (s.contains("{") && s.contains("}")) {
		dur = s.mid(s.indexOf("{") + 1);
		dur = dur.left(dur.lastIndexOf("}"));
		com = com.left(s.indexOf("{"));
	}
	if (dur.isEmpty()) dur = "inf";
	return QPair<QString, QString>(com, dur);
}
void ServerDesigner::loadVAR(const QString & path) {
	RUNTIME->server_project.var = QDir::current().relativeFilePath(path);
	widgetScripts->codeEdit->setVariables(QStringList());
	if (path.isEmpty()) return;
	treeVariables->clear();
	QList<SHS::ScriptVariable> vl;
	{
	QFile file(path);
	if (file.open(QIODevice::ReadOnly)) {
		char first = 0; file.read(&first, 1);
		if (first == '\r' || first == '\n') {
			file.close();
			QPIConfig f(path, QIODevice::ReadWrite);
			QPIConfig::Branch b = f.allLeaves();
			foreach (QPIConfig::Entry * e, b) {
				QPair<QString, QString> dp = splitVar(e->value());
				vl << SHS::ScriptVariable(e->name(), dp.second, dp.first);
			}
		}
	}
	}
	if (vl.isEmpty())
		vl = loadFromFile<typeof(vl)>(path, SHS::ScriptVariable::Version);
	foreach (const auto & v, vl) {
		QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << v.name << v.store << v.display_name << v.description);
		ti->setFlags(ti->flags() | Qt::ItemIsEditable);
		treeVariables->addTopLevelItem(ti);
	}
	widgetScripts->codeEdit->setVariables(variableNames());
}


void ServerDesigner::saveVAR(const QString & path) {
	if (path.isEmpty()) return;
	saveToFile(path, variables(), SHS::ScriptVariable::Version);
}


void gatherPath(const SHS::Object & o, PISet<PIString> & ret) {
	QMapIterator<QString, SHS::Command> it(o.commands);
	while (it.hasNext()) {
		it.next();
		for (const auto & p: it.value().devices)
			ret << p;
	}
	QMapIterator<QString, SHS::Object> oit(o.objects);
	while (oit.hasNext())
		gatherPath(oit.next().value(), ret);
}
void ServerDesigner::addDevices() {
	PISet<PIString> devs;
	gatherPath(RUNTIME->server_data.blocks, devs);
	auto it = devs.makeIterator();
	int i = 0;
	while (it.next()) {
		QString p = PI2QString(it.key());
		if (p.isEmpty()) continue;
		conn_edit.addDevice(QString("auto%1").arg(i++), p);
// 		qDebug() << "AUTO add device" << p;
	}
}


void ServerDesigner::addSnippet(QString title, QString content) {
	auto * li = new QListWidgetItem(title);
	li->setData(Qt::UserRole, content);
	listSnippets->addItem(li);
}


void ServerDesigner::saveProject() {
	if (!file_name.isEmpty())
		save(file_name);
}


void ServerDesigner::on_actionEdit_with_Qt_Designer_triggered() {
	widgetForm->editForm();
}


void ServerDesigner::on_actionConnection_editor_triggered() {
	if (!widgetForm->hasForm() || RUNTIME->server_project.cm.isEmpty()) return;
	QPIConfig f(RUNTIME->server_project.cm, QIODevice::ReadWrite);
	if (!f.isOpen()) return;
	conn_edit.setModel(f.getValue("model").toByteArray());
	addDevices();
	if (conn_edit.exec() == QDialog::Rejected) return;
	f.resize(0);
	f.readAll();
	QString cs = conn_edit.configuration();
	PIString pcs = Q2PIString(cs);
	f.setValue("name", conn_edit.name());
	f.setValue("model", conn_edit.model());
	f.setValue("config", cs.toLatin1());
	RUNTIME->connection.configureFromString(&pcs, Q2PIString(conn_edit.name()));
}


void ServerDesigner::on_actionDevice_streams_triggered() {
	device_stream_dialog.setDeviceStreams(RUNTIME->server_project.device_parameters);
	if (device_stream_dialog.exec() != QDialog::Accepted) return;
	RUNTIME->server_project.device_parameters = device_stream_dialog.deviceStreams();
	saveProject();
}


void ServerDesigner::on_actionTest_with_form_triggered() {
	widgetScripts->saveAllScripts();
	if (!widgetForm->loadForTest()) return;
	RUNTIME->is_test = true;
	RUNTIME->server_data.res_path = QDir::currentPath() + "/res";
	//engine.remove();
	//qDebug() << engine->availableExtensions();
	if (!RUNTIME->server_project.dat.isEmpty())
		RUNTIME->connection.loadFromCMFile(RUNTIME->server_project.cm);
	//piCout << "0";
	engine.createBlockObjects();
	//piCout << "1";
	engine.assignForm(widgetForm->testForm());
	//piCout << "2";
	engine.assignConnection(&(RUNTIME->connection));
	//piCout << "3";
	engine.assignVariables(variables());
	//piCout << "5";
	engine.prepare();
	//piCout << "6";
	widgetScripts->codeEdit->setBlockObjects();
	widgetScripts->codeEdit->updateACObjects();
	widgetScripts->clearError();
	RUNTIME->connection.start();
	engine.loadFromProject(RUNTIME->server_project);
	if (!engine.start()) {
		stopTest();
		return;
	}
	engine.connection()->start();
	widgetForm->startTest();
	engine.started();
}


void ServerDesigner::on_buttonVarAdd_clicked() {
	var_dialog.set("1 d");
	if (var_dialog.exec() != QDialog::Accepted) return;
	for (int i = 0; i < treeVariables->topLevelItemCount(); ++i)
		if (treeVariables->topLevelItem(i)->text(0) == lineNewVar->text())
			return;
	QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << lineNewVar->text() << var_dialog.get());
	ti->setFlags(ti->flags() | Qt::ItemIsEditable);
	treeVariables->addTopLevelItem(ti);
	variablesChanged();
}


void ServerDesigner::on_buttonVarDel_clicked() {
	qDeleteAll(treeVariables->selectedItems());
	variablesChanged();
}


void ServerDesigner::on_buttonVarClone_clicked() {
	auto sil = treeVariables->selectedItems();
	QStringList vnl = variableNames();
	for (auto * i: sil) {
		auto * ni = i->clone();
		ni->setText(0, uniqueName(ni->text(0), vnl));
		vnl << ni->text(0);
		treeVariables->addTopLevelItem(ni);
	}
	variablesChanged();
}


void ServerDesigner::on_objectTree_propertyDoubleClicked(QStringList path) {
	widgetScripts->insertText(path.join("."));
	widgetScripts->codeEdit->centerCursor();
	widgetScripts->codeEdit->setFocus();
}


void ServerDesigner::on_objectTree_commandDoubleClicked(SHS::Command cmd) {
	if (cmd.flags.testFlag(SHS::Input)) {
		widgetScripts->insertEvent(QString("function on_%1_%2(%3)").arg(cmd.object_path.join("_"), cmd.name, cmd.user_arguments.join(", ")));
	}
	if (cmd.flags.testFlag(SHS::Output)) {
		widgetScripts->insertFunction(QString("%1.%2(%3)").arg(cmd.object_path.join("."), cmd.name, cmd.user_arguments.join(", ")));
	}
	widgetScripts->codeEdit->centerCursor();
	widgetScripts->codeEdit->setFocus();
}


void ServerDesigner::on_listSnippets_itemDoubleClicked(QListWidgetItem * item) {
	QString content = item->data(Qt::UserRole).toString();
	widgetScripts->insertEvent(content);
}


void ServerDesigner::currentScriptChanged(const QString & path) {
	treeProject->setCurrentFile(path);
}


void ServerDesigner::watchUI() {
	noWatchUI();
	if (!RUNTIME->server_project.ui.isEmpty())
		watcher.addPath(RUNTIME->server_project.ui);
}


void ServerDesigner::noWatchUI() {
	if (!watcher.files().isEmpty())
		watcher.removePaths(watcher.files());
}


void ServerDesigner::scriptError(int line, QString file, QString message, QString stack) {
	QString msg("<html><head/><body><a href=\"%1:%2\"><span style=\" text-decoration: underline; color:#0000ff;\">%1:%2</span></a> %3<br>%4</br></body></html>");
	widgetScripts->scriptError(line, file, msg.arg(file).arg(line).arg(message, stack.trimmed().replace("\n", "</br><br>")));
	stopTest();
}


void ServerDesigner::variablesChanged() {
	widgetScripts->codeEdit->setVariables(variableNames());
	saveVAR(RUNTIME->server_project.var);
}
