/***************************************************************************
 *   Copyright (C) 2006-2012 by Thomas Schweitzer                          *
 *   thomas-schweitzer(at)arcor.de                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2.0 as   *
 *   published by the Free Software Foundation.                            *
 *                                                                         *
 *   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 in the file LICENSE.GPL; if not, write to the *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "config.h"
#include "IndentHandler.h"

#include <cstdlib>
#include <unistd.h>

#include "MainWindow.h"
#include "SettingsPaths.h"
#include "UiGuiErrorMessage.h"
#include "UiGuiIniFileParser.h"
#include "UiGuiSettings.h"
#include "TemplateBatchScript.h"

#include <tqaction.h>
#include <tqapplication.h>
#include <tqcheckbox.h>
#include <tqcombobox.h>
#include <tqcursor.h>
#include <tqdir.h>
#include <tqfile.h>
#include <tqfiledialog.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlineedit.h>
#include <tqmessagebox.h>
#include <tqpopupmenu.h>
#include <tqprocess.h>
#include <tqspinbox.h>
#include <tqregexp.h>
#include <tqstringlist.h>
#include <tqtextcodec.h>
#include <tqtextstream.h>
#include <tqtoolbox.h>
#include <tqtoolbutton.h>
#include <tqtooltip.h>

// \defgroup grp_Indenter All concerning handling of the indenter.

/*
    \class IndentHandler
    \brief A widget for handling many indenters that are configured by an ini file.

    This is a widget that is used by the main window. It handles access to the
    indenter config file and calls the chosen indenter to reformat the source text.
    Calls the indenter each time a setting has been changed and informs
    the main window about the reformatted source code.
*/


/*
    \brief Constructor of the indent handler.

    By calling this constructor the indenter to be loaded, can be selected by setting
    its \a indenterID, which is the number of found indenter ini files in alphabetic
    order starting at index 0.
 */
IndentHandler::IndentHandler(int indenterID, MainWindow *mainWindow, TQWidget *parent) :
        TQWidget(parent), m_mainWindow(mainWindow), m_parent(parent),
        m_indenterSelectionCombobox(nullptr), m_indenterParameterHelpButton(nullptr),
        m_indenterParameterCategoriesToolBox(nullptr), m_errorMessageDialog(nullptr),
        m_toolBoxContainerLayout(nullptr), m_indenterSettings(nullptr)
{
	// Create the settings object, which loads all UiGui settings from a file.
	m_settings = UiGuiSettings::getInstance();

	if (indenterID < 0)
	{
		indenterID = 0;
	}

	// define this widgets resize behavior
	setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding);

	if (m_mainWindow)
	{
		connect((TQObject*)m_mainWindow->actionLoadIndenterConfigFile, TQ_SIGNAL(activated()),
		        this, TQ_SLOT(openConfigFileDialog()));
		connect((TQObject*)m_mainWindow->actionSaveIndenterConfigFile, TQ_SIGNAL(activated()),
		        this, TQ_SLOT(saveasIndentCfgFileDialog()));
		connect((TQObject*)m_mainWindow->actionCreateShellScript, TQ_SIGNAL(activated()),
		        this, TQ_SLOT(createIndenterCallShellScript()));
		connect((TQObject*)m_mainWindow->actionResetIndenterParameters, TQ_SIGNAL(activated()),
		        this, TQ_SLOT(resetIndenterParameter()));
	}

	// create vertical layout box, into which the toolbox will be added
	m_toolBoxContainerLayout = new TQVBoxLayout(this);
	m_toolBoxContainerLayout->setMargin(2);
	m_toolBoxContainerLayout->setSpacing(5);

	// Create horizontal layout for indenter selector and help button.
	TQHBoxLayout *hboxLayout = new TQHBoxLayout();
	hboxLayout->setSpacing(5);
	m_toolBoxContainerLayout->addLayout(hboxLayout);

	// Create the indenter selection combo box.
	m_indenterSelectionCombobox = new TQComboBox(this);
	m_indenterSelectionCombobox->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Minimum);
	connect(m_indenterSelectionCombobox, TQ_SIGNAL(activated(int)), this, TQ_SLOT(setIndenter(int)));
	hboxLayout->addWidget(m_indenterSelectionCombobox);

	// Create the indenter parameter help button.
	m_indenterParameterHelpButton = new TQToolButton(this);
	m_indenterParameterHelpButton->setIconSet(TQPixmap(TQString(APP_ICONS_PATH)+ "help.png"));
	connect(m_indenterParameterHelpButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(showIndenterManual()));
	hboxLayout->addWidget(m_indenterParameterHelpButton);

	// create a toolbox and set its resize behavior
	m_indenterParameterCategoriesToolBox = new TQToolBox(this);
	m_indenterParameterCategoriesToolBox->setSizePolicy(TQSizePolicy::MinimumExpanding,
	        TQSizePolicy::Expanding);
	m_toolBoxContainerLayout->addWidget(m_indenterParameterCategoriesToolBox);

	m_indenterConfigFilename = "";
	m_indenterExecutableCallString = "";
	m_indenterExecutableSuffix     = "";

	m_indenterDirectoryStr  = SettingsPaths::getIndenterPath();
	m_tempDirectoryStr      = SettingsPaths::getTempPath();
	m_settingsDirectoryStr  = SettingsPaths::getSettingsPath();

	if (m_mainWindow)
	{
		m_errorMessageDialog = new UiGuiErrorMessage(m_mainWindow);
	}
	else
	{
		m_errorMessageDialog = new UiGuiErrorMessage(this);
	}

	TQDir indenterDirectory = TQDir(m_indenterDirectoryStr);
	m_indenterIniFileList = indenterDirectory.entryList(TQString("uigui_*.ini"));
	if (m_indenterIniFileList.count() > 0)
	{
		// Take care if the selected indenterID is smaller or greater than the number of existing
		// indenters
		if (indenterID < 0)
		{
			indenterID = 0;
		}
		if (indenterID >= m_indenterIniFileList.count())
		{
			indenterID = m_indenterIniFileList.count() - 1;
		}

		// Reads and parses the indenter ini file specified by indenterID and creates toolbox entries
		readIndentIniFile(m_indenterDirectoryStr + "/" + m_indenterIniFileList[indenterID]);

		// Find out how the indenter can be executed.
		createIndenterCallString();

		// Load the users last settings made for this indenter.
		loadConfigFile(m_settingsDirectoryStr + "/" + m_indenterFileName + ".cfg");

		// Fill the indenter selection combo box with the list of available indenters.
		TQStringList availableIndenters = getAvailableIndenters();
		if (!availableIndenters.isEmpty())
		{
			m_indenterSelectionCombobox->insertStringList(availableIndenters);
			m_indenterSelectionCombobox->setCurrentItem(indenterID);
		}
	}
	else
	{
		m_errorMessageDialog->showMessage(tr("No indenter ini files"),
		        tr("No valid indenter ini file found in directory ") +
		        TQDir(m_indenterDirectoryStr).absPath());
	}
}

/*
    \brief Implicitly writes the current indenter parameters to the indenters config file.
 */
IndentHandler::~IndentHandler()
{
	// Generate the parameter string that will be saved to the indenters config file.
	TQString parameterString = getParameterString();
	if (!m_indenterFileName.isEmpty())
	{
		saveConfigFile(m_settingsDirectoryStr + "/" + m_indenterFileName + ".cfg", parameterString);
	}

	delete m_errorMessageDialog;
}

/*
    \brief Opens the context menu, used for some actions like saving the indenter config file,
		at the event position.
 */
void IndentHandler::contextMenuEvent(TQContextMenuEvent *event)
{
	if (m_mainWindow)
	{
		m_mainWindow->menuIndenter->exec(event->globalPos());
	}
}

/*
    \brief Creates the content for a shell script that can be used as a external tool call
    to indent an as parameter defined file.
 */
TQString IndentHandler::generateShellScript(const TQString &configFilename)
{
	TQString indenterCompleteCallString;
	TQString parameterInputFile;
	TQString parameterOuputFile;
	TQString parameterParameterFile;
	TQString replaceInputFileCommand;

	// Define the placeholder for parameter variables either in batch or bash programming.
	TQString shellParameterPlaceholder = "$1";

	parameterInputFile = " " + m_inputFileParameter + "\"" + shellParameterPlaceholder + "\"";

	if (m_outputFileParameter != "none" && m_outputFileParameter != "stdout")
	{
		if (m_outputFileName == m_inputFileName)
		{
			parameterOuputFile = " " + m_outputFileParameter + "\"" + shellParameterPlaceholder + "\"";
		}
		else
		{
			parameterOuputFile = " " + m_outputFileParameter + m_outputFileName + ".tmp";
		}
	}

	// If the config file name is empty it is assumed that all parameters are sent via command line
	// call
	if (m_globalConfigFilename.isEmpty())
	{
		parameterParameterFile = " " + getParameterString();
	}
	// else if needed add the parameter to the indenter call string where the config file can be
	// found.
	else if (m_useCfgFileParameter != "none")
	{
		parameterParameterFile = " " + m_useCfgFileParameter + "\"./" + configFilename + "\"";
	}

	// Assemble indenter call string for parameters according to the set order.
	if (m_parameterOrder == "ipo")
	{
		indenterCompleteCallString = parameterInputFile + parameterParameterFile + parameterOuputFile;
	}
	else if (m_parameterOrder == "pio")
	{
		indenterCompleteCallString = parameterParameterFile + parameterInputFile + parameterOuputFile;
	}
	else if (m_parameterOrder == "poi")
	{
		indenterCompleteCallString = parameterParameterFile + parameterOuputFile + parameterInputFile;
	}
	else
	{
		indenterCompleteCallString = parameterInputFile + parameterOuputFile + parameterParameterFile;
	}

	// Generate the indenter call string either for win32 or other systems.
	indenterCompleteCallString = "#!/bin/bash\n" + m_indenterExecutableCallString +
	        indenterCompleteCallString;

	// If the indenter writes to stdout pipe the output into a file
	if (m_outputFileParameter == "stdout")
	{
		indenterCompleteCallString = indenterCompleteCallString + " >" + m_outputFileName + ".tmp";
	}

	// If the output filename is not the same as the input filename copy the output over the input.
	if (m_outputFileName != m_inputFileName)
	{
		replaceInputFileCommand = "mv " + m_outputFileName + ".tmp \"" + shellParameterPlaceholder +
		        "\"\n";
	}

	TQString shellScript(TemplateBatchScript::getTemplateBatchScript());
	shellScript = shellScript.replace("__INDENTERCALLSTRING2__",
	        indenterCompleteCallString + "\n" + replaceInputFileCommand);
	indenterCompleteCallString = indenterCompleteCallString.replace("$1", "$file2indent");
	replaceInputFileCommand    = replaceInputFileCommand.replace("$1", "$file2indent");
	shellScript = shellScript.replace("__INDENTERCALLSTRING1__",
	        indenterCompleteCallString + "\n" + replaceInputFileCommand);

	return shellScript;
}

/*
    \brief Format \a sourceCode by calling the indenter.

    The \a inputFileExtension has to be given as parameter so the called indenter
    can identify the programming language if needed.
 */
TQString IndentHandler::callIndenter(const TQString &sourceCode, const TQString &inputFileExtension)
{
	return callExecutableIndenter(sourceCode, inputFileExtension);
}

/*
    \brief Format \a sourceCode by calling the binary executable of the indenter.

    The \a inputFileExt has to be given as parameter so the called indenter
    can identify the programming language if needed.
 */
TQString IndentHandler::callExecutableIndenter(const TQString &sourceCode, const TQString &inputFileExt)
{
	if (m_indenterFileName.isEmpty() || inputFileExt.isEmpty())
	{
		return TQString::null;
	}

	TQString  indenterCompleteCallString;
	TQString  parameterInputFile;
	TQString  parameterOuputFile;
	TQString  parameterParameterFile;
	TQProcess indentProcess;

	TQObject::connect(&indentProcess, TQ_SIGNAL(processExited()), this, TQ_SLOT(indenterProcessFinished()));

	// Generate the parameter string that will be saved to the indenters config file
	TQString parameterString = getParameterString();

	if (!m_globalConfigFilename.isEmpty())
	{
		saveConfigFile(m_tempDirectoryStr + "/" + m_globalConfigFilename, parameterString);
	}

	// Only add a dot to file extension if the string is not empty
	TQString inputFileExtension = "." + inputFileExt;

	// Delete any previously used input src file and create a new input src file.
	TQFile::remove(m_tempDirectoryStr + "/" + m_inputFileName + inputFileExtension);
	TQFile inputSrcFile(m_tempDirectoryStr + "/" + m_inputFileName + inputFileExtension);
	// Write the source code to the input file for the indenter
	if (inputSrcFile.open(IO_ReadWrite | IO_Translate))
	{
		TQCString sourceCodeCString = sourceCode.utf8();
		inputSrcFile.writeBlock(sourceCodeCString.data(), sourceCodeCString.length());
		inputSrcFile.close();
		// tqDebug("Wrote source code to be indented to file %s", inputSrcFile.name().local8Bit().data());
	}
	else
	{
		tqDebug("Couldn't write to be indented source code to file %s", inputSrcFile.name().local8Bit().data());
	}

	// Set the input file for the indenter to be called.
	if (m_inputFileParameter.stripWhiteSpace() == "<" || m_inputFileParameter == "stdin")
	{
		parameterInputFile = " < " + inputSrcFile.name();
	}
	else
	{
		parameterInputFile = " " + m_inputFileParameter + m_inputFileName + inputFileExtension;
	}

	// Set the output file for the to be called indenter.
	if (m_outputFileParameter != "none" && m_outputFileParameter != "stdout")
	{
		parameterOuputFile = " " + m_outputFileParameter + m_outputFileName + inputFileExtension;
	}

	// If the config file name is empty it is assumed that all parameters are sent via command line
	// call
	if (m_globalConfigFilename.isEmpty())
	{
		parameterParameterFile = " " + parameterString;
	}
	// if needed add the parameter to the indenter call string where the config file can be found
	else if (m_useCfgFileParameter != "none")
	{
		parameterParameterFile = " " + m_useCfgFileParameter  + m_tempDirectoryStr + "/" +
		        m_globalConfigFilename;
	}

	// Assemble indenter call string for parameters according to the set order.
	if (m_parameterOrder == "ipo")
	{
		indenterCompleteCallString = parameterInputFile + parameterParameterFile + parameterOuputFile;
	}
	else if (m_parameterOrder == "pio")
	{
		indenterCompleteCallString = parameterParameterFile + parameterInputFile + parameterOuputFile;
	}
	else if (m_parameterOrder == "poi")
	{
		indenterCompleteCallString = parameterParameterFile + parameterOuputFile + parameterInputFile;
	}
	else
	{
		indenterCompleteCallString = parameterInputFile + parameterOuputFile + parameterParameterFile;
	}

	// If no indenter executable call string could be created before, show an error message.
	if (m_indenterExecutableCallString.isEmpty())
	{
		m_errorMessageDialog->showMessage(tr("No indenter executable"), tr(
							"There exists no indenter executable with the name \"%1\" in the directory \"%2\" nor in the global environment.").arg(
							m_indenterFileName).arg(m_indenterDirectoryStr));
		return sourceCode;
	}

	// Generate the indenter call string either for win32 or other systems.
	indenterCompleteCallString = m_indenterExecutableCallString + indenterCompleteCallString;

	// errors and standard outputs from the process call are merged together
	//indentProcess.setReadChannelMode(TQProcess::MergedChannels);

	// Set the directory for the indenter execution
	indentProcess.setWorkingDirectory(TQFileInfo(m_tempDirectoryStr).absFilePath());

	// tqDebug(TQString("Will call the indenter in the directory ") + indentProcess.workingDirectory().path() +
	//         " using this commandline call: " + indenterCompleteCallString);

	indentProcess.clearArguments();
	TQStringList arguments = TQStringList::split(" ", indenterCompleteCallString);
	for (const TQString &argument : arguments)
	{
		indentProcess.addArgument(argument);
	}

	m_indenterProcessFinished = false;
	indentProcess.start();
	int counter = 100; // roughtly 10s at 100ms interval
	while (!m_indenterProcessFinished && counter > 0)
	{
		usleep(100 * 1000);
		tqApp->processEvents();
		--counter;
	}

	TQString formattedSourceCode = sourceCode;
 	bool calledProcessSuccessfully = indentProcess.normalExit();
	// test if there was an error during starting the process of the indenter
	if (!calledProcessSuccessfully || indentProcess.exitStatus() != 0)
	{
		TQString processReturnString  = "<html><body>";
		processReturnString += tr("<b>Returned error code:</b> ") +
		        TQString::number(indentProcess.exitStatus()) + "<br/>";
		processReturnString += tr("<br/><b>Call string:</b><br/>") +
		        encodeToHTML(indenterCompleteCallString) + "<br/>";
		processReturnString += tr("<br/><b>Process standard output:</b><br/>") +
		        encodeToHTML(TQString(indentProcess.readStdout())) + "<br/>";
		processReturnString += tr("<br/><b>Process error output:</b><br/>") +
		        encodeToHTML(TQString(indentProcess.readStderr())) + "<br/>";
		TQApplication::restoreOverrideCursor();
		m_errorMessageDialog->showMessage(tr("Error calling Indenter"), processReturnString);
	}
	else
	{
		if (m_outputFileParameter == "stdout")
		{
			formattedSourceCode = TQString(indentProcess.readStdout());
		}
		// ... else read the output file generated by the indenter call.
		else
		{
			TQFile outSrcFile(m_tempDirectoryStr + "/" + m_outputFileName + inputFileExtension);
			if (outSrcFile.open(IO_ReadOnly | IO_Translate))
			{
				TQTextStream outSrcStrm(&outSrcFile);
				outSrcStrm.setCodec(TQTextCodec::codecForName("UTF-8"));
				formattedSourceCode = outSrcStrm.read();
				outSrcFile.close();
				// tqDebug("Read indenter output from file " + outSrcFile.name());
			}
			else
			{
				tqDebug("Couldn't read indenter output from file " + outSrcFile.name());
			}
		}
	}

	// Delete the temporary input and output files.
	TQFile::remove(m_tempDirectoryStr + "/" + m_outputFileName + inputFileExtension);
	TQFile::remove(m_tempDirectoryStr + "/" + m_inputFileName + inputFileExtension);

	return formattedSourceCode;
}

/*
    \brief Generates and returns a string with all parameters needed to call the indenter.
 */
TQString IndentHandler::getParameterString()
{
	TQString parameterString = "";

	// generate parameter string for all boolean values
	for (const ParamBoolean &pBoolean : m_paramBooleans)
	{
		if (pBoolean.checkBox->isChecked())
		{
			if (!pBoolean.trueString.isEmpty())
			{
				parameterString += pBoolean.trueString + m_cfgFileParameterEnding;
			}
		}
		else
		{
			if (!pBoolean.falseString.isEmpty())
			{
				parameterString += pBoolean.falseString + m_cfgFileParameterEnding;
			}
		}
	}

	// generate parameter string for all numeric values
	for (const ParamNumeric &pNumeric : m_paramNumerics)
	{
		if (pNumeric.valueEnabledChkBox->isChecked())
		{
			parameterString += pNumeric.paramCallName + TQString::number(pNumeric.spinBox->value()) +
			        m_cfgFileParameterEnding;
		}
	}

	// generate parameter string for all string values
	for (const ParamString &pString : m_paramStrings)
	{
		if (!pString.lineEdit->text().isEmpty() && pString.valueEnabledChkBox->isChecked())
		{
			// Create parameter definition for each value devided by a | sign.
			TQStringList pStringList = TQStringList::split("|", pString.lineEdit->text());
			for (const TQString &paramValue : pStringList)
			{
				parameterString += pString.paramCallName + paramValue + m_cfgFileParameterEnding;
			}
		}
	}

	// generate parameter string for all multiple choice values
	for (const ParamMultiple &pMultiple : m_paramMultiples)
	{
		if (pMultiple.valueEnabledChkBox->isChecked())
		{
			parameterString += pMultiple.choicesStrings[pMultiple.comboBox->currentItem()] +
			        m_cfgFileParameterEnding;
		}
	}

	return parameterString;
}

/*
    \brief Write settings for the indenter to a config file.
 */
void IndentHandler::saveConfigFile(const TQString &filePathName, const TQString &paramString)
{
	TQFile::remove(filePathName);
	TQFile cfgFile(filePathName);

	cfgFile.open(IO_ReadWrite | IO_Translate);
	TQCString paramCString = paramString.local8Bit();
	cfgFile.writeBlock(paramCString.data(), paramCString.length());
	cfgFile.close();
}

/*
    \brief Load the config file for the indenter and apply the settings made there.
 */
bool IndentHandler::loadConfigFile(const TQString &filePathName)
{
	TQFile   cfgFile(filePathName);
	TQString cfgFileData = TQString::null;
	TQRegExp stringRegex("\"[^\"]*\"");
	TQRegExp endParamRegex("\\s*(#.*)?" + m_cfgFileParameterEnding);

	if (!cfgFile.exists())
	{
		// If the config file to be loaded does not exist, leave all values as they are and return false
		return false;
	}
	else
	{
		// else if the config file exists, retrieve its whole content
		cfgFile.open(IO_ReadOnly | IO_Translate);
		cfgFileData = TQString(cfgFile.readAll());
		cfgFile.close();
	}

	m_indenterConfigFilename = filePathName;

	// Search for name of each boolean parameter and set its value if found.
	for (const ParamBoolean &pBoolean : m_paramBooleans)
	{
		bool found = false;
		bool paramValue = false;
		paramValue = m_indenterSettings->value(pBoolean.paramName + "/ValueDefault").toBool();

		// try regex matching first
		if (!pBoolean.trueRegexString.isEmpty() && !pBoolean.falseRegexString.isEmpty())
		{
			// search for the longer parameter string first
			if (pBoolean.trueRegexString.length() > pBoolean.falseRegexString.length())
			{
				if (cfgFileData.find(TQRegExp(pBoolean.trueRegexString), 0) != -1)
				{
					paramValue = true;
					found = true;
				}
				else if (cfgFileData.find(TQRegExp(pBoolean.falseRegexString), 0) != -1)
				{
					paramValue = false;
					found = true;
				}
			}
			else
			{
				if (cfgFileData.find(TQRegExp(pBoolean.falseRegexString), 0) != -1)
				{
					paramValue = false;
					found = true;
				}
				else if (cfgFileData.find(TQRegExp(pBoolean.trueRegexString), 0) != -1)
				{
					paramValue = true;
					found = true;
				}
			}
		}
		// try standard search if regex search failed
		if (!found)
		{
			// search for the longer parameter string first
			if (pBoolean.trueString.length() > pBoolean.falseString.length())
			{
				if (cfgFileData.find(pBoolean.trueString, 0, false) != -1)
				{
					paramValue = true;
				}
				else if (cfgFileData.find(pBoolean.falseString, 0, false) != -1)
				{
					paramValue = false;
				}
			}
			else
			{
				if (cfgFileData.find(pBoolean.falseString, 0, false) != -1)
				{
					paramValue = false;
				}
				else if (cfgFileData.find(pBoolean.trueString, 0, false) != -1)
				{
					paramValue = true;
				}
			}
		}
		pBoolean.checkBox->setChecked(paramValue);
	}

	// Search for name of each numeric parameter and set the value found behind it.
	for(const ParamNumeric &pNumeric : m_paramNumerics)
	{
		int paramValue = m_indenterSettings->value(pNumeric.paramName + "/ValueDefault").toInt();
		bool found = false;
		// try regex matching first
		if (!pNumeric.paramCallNameRegex.isEmpty())
		{
			TQRegExp pRegEx(pNumeric.paramCallNameRegex);
			int index = pRegEx.search(cfgFileData);
			if (index != -1)
			{
				index += pRegEx.matchedLength();
				int crPos = endParamRegex.search(cfgFileData, index + 1);
				if (crPos != -1)
				{
					found = true;
					paramValue = cfgFileData.mid(index, crPos - index).toInt();
				}
			}
		}

		// try standard search if regex search failed
		if (!found)
		{
			int index = cfgFileData.find(pNumeric.paramCallName, 0);
			if (index != -1)
			{
				index += pNumeric.paramCallName.length();
				int crPos = cfgFileData.find(m_cfgFileParameterEnding, index + 1);
				if (crPos != -1)
				{
					found = true;
					paramValue = cfgFileData.mid(index, crPos - index).toInt();
				}
			}
		}

		// Disconnect and reconnect the signal, otherwise it is emitted each time the value is set
		disconnect(pNumeric.spinBox, TQ_SIGNAL(valueChanged(int)),
		        this, TQ_SLOT(handleChangedIndenterSettings()));
		pNumeric.spinBox->setValue(paramValue);
		pNumeric.valueEnabledChkBox->setChecked(found);
		connect(pNumeric.spinBox, TQ_SIGNAL(valueChanged(int)),
		        this, TQ_SLOT(handleChangedIndenterSettings()));
	}

	// Search for name of each string parameter and set it.
	for (const ParamString &pString : m_paramStrings)
	{
		TQString paramValueStr = TQString::null;
		int numberOfValues = 0; // The number of the found values for this parameter name.
		// try regex matching first
		if (!pString.paramCallNameRegex.isEmpty())
		{
			TQRegExp pRegEx(pString.paramCallNameRegex);
			int index = pRegEx.search(cfgFileData);
			if (index != -1)
			{
				while (index != -1)
				{
					numberOfValues++;

					index += pRegEx.matchedLength();
					int crPos = stringRegex.search(cfgFileData, index);
					crPos += stringRegex.matchedLength();

					// Get the string and remember it.
					TQString paramStrVal = TQString::null;
					if ((crPos - index) >= 2)
					{
						// Remove leading and trailing quotes
						paramStrVal = TQString(cfgFileData.mid(index + 1, crPos - index - 2));
					}
					if (numberOfValues < 2)
					{
						paramValueStr = paramStrVal;
					}
					else
					{
					  // If the same parameter is set multiple times, concatenate the strings dvivided by a |.
						paramValueStr = paramValueStr + "|" + paramStrVal;
					}

					// Ignore till end of line because there could a comment
					// after the string itself in the configuration file
					crPos = endParamRegex.search(cfgFileData, crPos);
					crPos += stringRegex.matchedLength();

					// Get next value for this setting, if one exists.
					index = pRegEx.search(cfgFileData, crPos);
				}
			}
		}

		// try standard search if regex search failed
		if (numberOfValues == 0)
		{
			int index = cfgFileData.find(pString.paramCallName, 0, false);
			while (index != -1)
			{
				numberOfValues++;

				index += pString.paramCallName.length();
				int crPos = cfgFileData.find(m_cfgFileParameterEnding, index);

				// Get the string and remember it.
				TQString paramStrVal = TQString::null;
				if ((crPos - index) >= 2)
				{
					// Remove leading and trailing quotes
					paramStrVal = TQString(cfgFileData.mid(index + 1, crPos - index - 2));
				}
				if (numberOfValues < 2)
				{
					paramValueStr = paramStrVal;
				}
				else
				{
					// If the same parameter is set multiple times, concatenate the strings dvivided by a |.
					paramValueStr = paramValueStr + "|" + paramStrVal;
				}

				// Get next value for this setting, if one exists.
				index = cfgFileData.find(pString.paramCallName, crPos, false);
			}
		}

		// Set the text for the line edit.
		if (numberOfValues == 0)
		{
			paramValueStr = m_indenterSettings->value(pString.paramName + "/ValueDefault").toString();
		}
		pString.lineEdit->setText(paramValueStr);
		pString.valueEnabledChkBox->setChecked(numberOfValues != 0);
	}

	// search for name of each multiple choice parameter and set it
	for (const ParamMultiple &pMultiple : m_paramMultiples)
	{
		bool found = false;

		// try regex matching first
		for (int i = 0; i < pMultiple.choicesRegexStrings.count(); ++i)
		{
			int index = cfgFileData.find(TQRegExp(pMultiple.choicesRegexStrings[i]), 0);
			if (index != -1)
			{
				pMultiple.comboBox->setCurrentItem(i);
				found = true;
				break;
			}
		}

		// try standard search
		if (!found)
		{
			for (int i = 0; i < pMultiple.choicesStrings.count(); ++i)
			{
				int index = cfgFileData.find(pMultiple.choicesStrings[i], 0, false);
				if (index != -1)
				{
					pMultiple.comboBox->setCurrentItem(i);
					found = true;
					break;
				}
			}
		}

		// parameter was not set in config file, so use default value
		if (!found)
		{
			int defaultValue = m_indenterSettings->value(pMultiple.paramName + "/ValueDefault").toInt();
			pMultiple.comboBox->setCurrentItem(defaultValue);
		}
		pMultiple.valueEnabledChkBox->setChecked(found);
	}

	return true;
}

/*
    \brief Sets all indenter parameters to their default values defined in the ini file.
 */
void IndentHandler::resetToDefaultValues()
{
	// Search for name of each boolean parameter and set its value if found.
	for (ParamBoolean &pBoolean : m_paramBooleans)
	{
		// Boolean value that will be assigned to the checkbox.
		bool defaultValue = m_indenterSettings->value(pBoolean.paramName + "/ValueDefault").toBool();
		pBoolean.checkBox->setChecked(defaultValue);
	}

	// Search for name of each numeric parameter and set the value found behind it.
	for (ParamNumeric &pNumeric : m_paramNumerics)
	{
		int defaultValue = m_indenterSettings->value(pNumeric.paramName + "/ValueDefault").toInt();
		pNumeric.spinBox->setValue(defaultValue);
		pNumeric.valueEnabledChkBox->setChecked(m_indenterSettings->value(
		        pNumeric.paramName + "/Enabled").toBool());
	}

	// Search for name of each string parameter and set it.
	for (ParamString &pString : m_paramStrings)
	{
		TQString defaultValue =
		        m_indenterSettings->value(pString.paramName + "/ValueDefault").toString();
		pString.lineEdit->setText(defaultValue);
		pString.valueEnabledChkBox->setChecked(m_indenterSettings->value(
		        pString.paramName + "/Enabled").toBool());
	}

	// Search for name of each multiple choice parameter and set it.
	for (ParamMultiple &pMultiple : m_paramMultiples)
	{
		int defaultValue = m_indenterSettings->value(pMultiple.paramName + "/ValueDefault").toInt();
		pMultiple.comboBox->setCurrentItem(defaultValue);
		pMultiple.valueEnabledChkBox->setChecked(m_indenterSettings->value(pMultiple.paramName +
		        "/Enabled").toBool());
	}
}

/*
    \brief Feedback when the inderter process has finished
 */
void IndentHandler::indenterProcessFinished()
{
	m_indenterProcessFinished = true;
}

/*
    \brief Opens and parses the indenter ini file that is declared by \a iniFilePath.
 */
void IndentHandler::readIndentIniFile(const TQString &iniFilePath)
{
	if (iniFilePath.isEmpty())
	{
		m_errorMessageDialog->showMessage(tr("readIndentIniFile"),
		        tr("The iniFilePath for the indenter file is empty"));
		return;
	}

	// open the ini-file that contains all available indenter settings with their additional infos
	m_indenterSettings = new UiGuiIniFileParser(iniFilePath);

	//
	//  parse ini file indenter header
	//

	m_indenterName           = m_indenterSettings->value("header/indenterName").toString();
	m_indenterFileName       = m_indenterSettings->value("header/indenterFileName").toString();
	m_globalConfigFilename   = m_indenterSettings->value("header/configFilename").toString();
	m_useCfgFileParameter    = m_indenterSettings->value("header/useCfgFileParameter").toString();
	m_cfgFileParameterEnding = m_indenterSettings->value("header/cfgFileParameterEnding").toString();
	if (m_cfgFileParameterEnding == "cr")
	{
		m_cfgFileParameterEnding = "\n";
	}
	m_indenterShowHelpParameter = m_indenterSettings->value("header/showHelpParameter").toString();

	if (m_indenterFileName.isEmpty())
	{
		m_errorMessageDialog->showMessage(tr("Indenter ini file header error"),
							tr("The loaded indenter ini file \"%1\"has a faulty header. At least the indenter file name is not set.").
							arg(iniFilePath));
	}

	// Read the parameter order. Possible values are (p=parameter[file] i=inputfile o=outputfile)
	// pio, ipo, iop
	m_parameterOrder      = m_indenterSettings->value("header/parameterOrder", "pio").toString();
	m_inputFileParameter  = m_indenterSettings->value("header/inputFileParameter").toString();
	m_inputFileName       = m_indenterSettings->value("header/inputFileName").toString();
	m_outputFileParameter = m_indenterSettings->value("header/outputFileParameter").toString();
	m_outputFileName      = m_indenterSettings->value("header/outputFileName").toString();
	m_fileTypes = m_indenterSettings->value("header/fileTypes").toString();
	m_fileTypes.replace('|', " ");

	// read the categories names which are separated by "|"
	TQString categoriesStr = m_indenterSettings->value("header/categories").toString();
	TQStringList categories = TQStringList::split('|', categoriesStr);
	// Assure that the category list is never empty. At least contain a "general" section.
	if (categories.isEmpty())
	{
		categories.append("General");
	}

	IndenterParameterCategoryPage categoryPage;

	// For some unknown reasons, TQToolbox does not display the content of the first
	// widget once the indenter is changed. To work around this problem we insert a
	// dummy widget before the real categories and we will get rid of it at the end
	TQWidget *dummyWidget = new TQWidget();
	m_indenterParameterCategoriesToolBox->addItem(dummyWidget, "Dummy");

	// create a page for each category and store its references in a toolboxpage-array
	for (const TQString &category : categories)
	{
		categoryPage.widget = new TQWidget();
		categoryPage.widget->setName(category.local8Bit());
		categoryPage.widget->setSizePolicy(TQSizePolicy::MinimumExpanding,
		        TQSizePolicy::MinimumExpanding);
		categoryPage.vboxLayout = new TQVBoxLayout(categoryPage.widget);
		categoryPage.vboxLayout->setSpacing(6);
		categoryPage.vboxLayout->setMargin(9);
		categoryPage.vboxLayout->setName(category.local8Bit());
		m_indenterParameterCategoryPages.append(categoryPage);
		m_indenterParameterCategoriesToolBox->addItem(categoryPage.widget, category);
	}

	//
	//  parse ini file indenter parameters
	//

	// read all possible parameters written in brackets []
	m_indenterParameters = m_indenterSettings->childGroups();

	// read each parameter to create the corresponding input field
	for (const TQString &indenterParameter : m_indenterParameters)
	{
		// if it is not the indent header definition read the parameter and add it to
		// the corresponding category toolbox page
		if (indenterParameter != "header")
		{
			// read to which category the parameter belongs
			int category = m_indenterSettings->value(indenterParameter + "/Category").toInt();
			// Assure that the category number is never greater than the available categories.
			if (category > m_indenterParameterCategoryPages.size() - 1)
			{
				category = m_indenterParameterCategoryPages.size() - 1;
			}
			// read which type of input field the parameter needs
			TQString editType = m_indenterSettings->value(indenterParameter + "/EditorType").toString();

			// edit type is numeric so create a spinbox with label
			if (editType == "numeric")
			{
				// create checkbox which enables or disables the parameter
				TQCheckBox *chkBox = new TQCheckBox(m_indenterParameterCategoryPages.at(category).widget);
				chkBox->setChecked(m_indenterSettings->value(indenterParameter + "/Enabled").toBool());
				TQToolTip::add(chkBox,
					      "Enables/disables the parameter. If disabled the indenters default value will be used.");
				chkBox->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed);

				// create the spinbox
				TQSpinBox *spinBox = new TQSpinBox(m_indenterParameterCategoryPages.at(category).widget);
				TQString paramToolTip = m_indenterSettings->value(indenterParameter + "/Description").toString();
				TQToolTip::add(spinBox, paramToolTip);
				spinBox->setMaximumWidth(50);
				spinBox->setMinimumWidth(50);
				if (m_mainWindow != nullptr)
				{
					spinBox->installEventFilter(m_mainWindow);
				}
				if (m_indenterSettings->value(indenterParameter + "/MinVal").toString() != "")
				{
					spinBox->setMinValue(m_indenterSettings->value(indenterParameter + "/MinVal").toInt());
				}
				else
				{
					spinBox->setMinValue(0);
				}
				if (m_indenterSettings->value(indenterParameter + "/MaxVal").toString() != "")
				{
					spinBox->setMaxValue(m_indenterSettings->value(indenterParameter + "/MaxVal").toInt());
				}
				else
				{
					spinBox->setMaxValue(2000);
				}

				// create the label
				TQLabel *label = new TQLabel(m_indenterParameterCategoryPages.at(category).widget);
				label->setText(indenterParameter);
				label->setBuddy(spinBox);
				TQToolTip::add(label, paramToolTip);
				if (m_mainWindow != nullptr)
				{
					label->installEventFilter(m_mainWindow);
				}

				// put all into a layout and add it to the toolbox page
				TQHBoxLayout *hboxLayout = new TQHBoxLayout();
				hboxLayout->addWidget(chkBox);
				hboxLayout->addWidget(spinBox);
				hboxLayout->addWidget(label);
				m_indenterParameterCategoryPages.at(category).vboxLayout->addLayout(hboxLayout);

				// remember parameter name and reference to its spinbox
				ParamNumeric paramNumeric;
				paramNumeric.paramName     = indenterParameter;
				paramNumeric.paramCallName = m_indenterSettings->value(indenterParameter +
				        "/CallName").toString();
				paramNumeric.paramCallNameRegex = m_indenterSettings->value(indenterParameter +
				        "/CallNameRegex").toString();
				paramNumeric.spinBox       = spinBox;
				paramNumeric.label = label;
				paramNumeric.valueEnabledChkBox = chkBox;
				paramNumeric.spinBox->setValue(m_indenterSettings->value(paramNumeric.paramName +
				        "/ValueDefault").toInt());
				m_paramNumerics.append(paramNumeric);

				TQObject::connect(spinBox, TQ_SIGNAL(valueChanged(int)), this,
				        TQ_SLOT(handleChangedIndenterSettings()));
				TQObject::connect(chkBox, TQ_SIGNAL(clicked()), this, TQ_SLOT(handleChangedIndenterSettings()));
			}
			// edit type is boolean so create a checkbox
			else if (editType == "boolean")
			{
				// create the checkbox, make its settings and add it to the toolbox page
				TQCheckBox *chkBox = new TQCheckBox(m_indenterParameterCategoryPages.at(category).widget);
				chkBox->setText(indenterParameter);
				TQString paramToolTip = m_indenterSettings->value(indenterParameter + "/Description").toString();
				TQToolTip::add(chkBox, paramToolTip);
				if (m_mainWindow != nullptr)
				{
					chkBox->installEventFilter(m_mainWindow);
				}
				m_indenterParameterCategoryPages.at(category).vboxLayout->addWidget(chkBox);

				// remember parameter name and reference to its checkbox
				ParamBoolean paramBoolean;
				paramBoolean.paramName = indenterParameter;
				paramBoolean.checkBox  = chkBox;
				TQStringList trueFalseStrings = TQStringList::split("|",
				        m_indenterSettings->value(indenterParameter + "/TrueFalse").toString());
				if (trueFalseStrings.count() > 0)
				{
					paramBoolean.trueString  = trueFalseStrings[0];
					paramBoolean.falseString = trueFalseStrings[1];
				}
				TQStringList trueFalseRegexStrings = TQStringList::split("|",
				        m_indenterSettings->value(indenterParameter + "/TrueFalseRegex").toString());
				if (trueFalseRegexStrings.count() > 0)
				{
					paramBoolean.trueRegexString  = trueFalseRegexStrings[0];
					paramBoolean.falseRegexString = trueFalseRegexStrings[1];
				}
				paramBoolean.checkBox->setChecked(m_indenterSettings->value(paramBoolean.paramName +
				        "/ValueDefault").toBool());
				m_paramBooleans.append(paramBoolean);

				TQObject::connect(chkBox, TQ_SIGNAL(clicked()), this, TQ_SLOT(handleChangedIndenterSettings()));
			}
			// edit type is numeric so create a line edit with label
			else if (editType == "string")
			{
				// create check box which enables or disables the parameter
				TQCheckBox *chkBox = new TQCheckBox(m_indenterParameterCategoryPages.at(category).widget);
				chkBox->setChecked(m_indenterSettings->value(indenterParameter + "/Enabled").toBool());
				TQToolTip::add(chkBox,
				        "Enables/disables the parameter. If disabled the indenters default value will be used.");
				chkBox->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed);

				// create the line edit
				TQLineEdit *lineEdit = new TQLineEdit(m_indenterParameterCategoryPages.at(category).widget);
				TQString paramToolTip = m_indenterSettings->value(indenterParameter + "/Description").toString();
				TQToolTip::add(lineEdit, paramToolTip);
				lineEdit->setMaximumWidth(50);
				lineEdit->setMinimumWidth(50);
				if (m_mainWindow != nullptr)
				{
					lineEdit->installEventFilter(m_mainWindow);
				}

				// create the label
				TQLabel *label = new TQLabel(m_indenterParameterCategoryPages.at(category).widget);
				label->setText(indenterParameter);
				label->setBuddy(lineEdit);
				label->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Preferred);
				TQToolTip::add(label, paramToolTip);
				if (m_mainWindow != nullptr)
				{
					label->installEventFilter(m_mainWindow);
				}

				// put all into a layout and add it to the toolbox page
				TQHBoxLayout *hboxLayout = new TQHBoxLayout();
				hboxLayout->addWidget(chkBox);
				hboxLayout->addWidget(lineEdit);
				hboxLayout->addWidget(label);
				m_indenterParameterCategoryPages.at(category).vboxLayout->addLayout(hboxLayout);

				// remember parameter name and reference to its line edit
				ParamString paramString;
				paramString.paramName     = indenterParameter;
				paramString.paramCallName = m_indenterSettings->value(indenterParameter +
				        "/CallName").toString();
				paramString.paramCallNameRegex = m_indenterSettings->value(indenterParameter +
				        "/CallNameRegex").toString();
				paramString.lineEdit      = lineEdit;
				paramString.label = label;
				paramString.valueEnabledChkBox = chkBox;
				paramString.lineEdit->setText(m_indenterSettings->value(paramString.paramName +
				        "/ValueDefault").toString());
				m_paramStrings.append(paramString);

				TQObject::connect(lineEdit, TQ_SIGNAL(returnPressed()), this,
				        TQ_SLOT(handleChangedIndenterSettings()));
				TQObject::connect(chkBox, TQ_SIGNAL(clicked()), this, TQ_SLOT(handleChangedIndenterSettings()));
			}
			// edit type is multiple so create a combobox with label
			else if (editType == "multiple")
			{
				// create checkbox which enables or disables the parameter
				TQCheckBox *chkBox = new TQCheckBox(m_indenterParameterCategoryPages.at(category).widget);
				chkBox->setChecked(m_indenterSettings->value(indenterParameter + "/Enabled").toBool());
				TQToolTip::add(chkBox,
					"Enables/disables the parameter. If disabled the indenters default value will be used.");
				chkBox->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed);

				// create the combo box
				TQComboBox *comboBox = new TQComboBox(m_indenterParameterCategoryPages.at(category).widget);
				TQStringList choicesStrings = TQStringList::split("|",
				        m_indenterSettings->value(indenterParameter + "/Choices").toString());
				TQStringList choicesRegexStrings = TQStringList::split("|",
				        m_indenterSettings->value(indenterParameter + "/ChoicesRegex").toString());
				TQStringList choicesReadableStrings = TQStringList::split("|",
				        m_indenterSettings->value(indenterParameter + "/ChoicesReadable").toString(), true);
				if (!choicesReadableStrings.isEmpty())
				{
					comboBox->insertStringList(choicesReadableStrings);
				}
				else if (!choicesRegexStrings.isEmpty())
				{
					comboBox->insertStringList(choicesRegexStrings);
				}
				else
				{
					comboBox->insertStringList(choicesStrings);
				}
				TQString paramToolTip = m_indenterSettings->value(indenterParameter + "/Description").toString();
				TQToolTip::add(comboBox, paramToolTip);
				if (m_mainWindow != nullptr)
				{
					comboBox->installEventFilter(m_mainWindow);
				}

				// put all into a layout and add it to the toolbox page
				TQHBoxLayout *hboxLayout = new TQHBoxLayout();
				hboxLayout->addWidget(chkBox);
				hboxLayout->addWidget(comboBox);
				m_indenterParameterCategoryPages.at(category).vboxLayout->addLayout(hboxLayout);

				// remember parameter name and reference to its lineedit
				ParamMultiple paramMultiple;
				paramMultiple.paramName      = indenterParameter;
				paramMultiple.comboBox       = comboBox;
				paramMultiple.choicesStrings = choicesStrings;
				paramMultiple.choicesRegexStrings = choicesRegexStrings;
				paramMultiple.choicesReadableStrings = choicesReadableStrings;
				paramMultiple.valueEnabledChkBox = chkBox;
				paramMultiple.comboBox->setCurrentItem(m_indenterSettings->value(paramMultiple.paramName +
				        "/ValueDefault").toInt());
				m_paramMultiples.append(paramMultiple);

				TQObject::connect(comboBox, TQ_SIGNAL(activated(int)), this,
				        TQ_SLOT(handleChangedIndenterSettings()));
				TQObject::connect(chkBox, TQ_SIGNAL(clicked()), this, TQ_SLOT(handleChangedIndenterSettings()));
			}
		}
	}

	// put a spacer at each page end
	for(IndenterParameterCategoryPage &categoryPage : m_indenterParameterCategoryPages)
	{
		categoryPage.vboxLayout->addStretch();
	}

	// Get rid of the dummy widget previously created
	m_indenterParameterCategoriesToolBox->removeItem(dummyWidget);
	delete dummyWidget;
}

/*
    \brief Searches and returns all indenters a configuration file is found for.

    Opens all uigui ini files found in the list \a m_indenterIniFileList, opens each ini file
    and reads the there defined real name of the indenter. These names are being returned as TQStringList.
 */
TQStringList IndentHandler::getAvailableIndenters()
{
	TQStringList indenterNamesList;

	// Loop for every existing uigui ini file
	for (const TQString indenterIniFile : m_indenterIniFileList)
	{
		// Open the ini file and search for the indenter name
		TQFile file(m_indenterDirectoryStr + "/" + indenterIniFile);
		if (file.open(IO_ReadOnly | IO_Translate))
		{
			int index = -1;
      TQTextStream stream(&file);
			TQString line;
			// Search for the string "indenterName=" and get the following string until line end.
			while (index == -1 && !file.atEnd())
			{
				line = stream.readLine();
				index = line.find("indenterName=", 0);
			}

			if (index == 0)
			{
				line = line.remove(0, 13);
				indenterNamesList << line.stripWhiteSpace();
			}
		}
		file.close();
	}
	return indenterNamesList;
}

/*
    \brief Deletes all elements in the toolbox and initializes the indenter selected by \a indenterID.
 */
void IndentHandler::setIndenter(int indenterID)
{
	TQApplication::setOverrideCursor(TQt::WaitCursor);

	// Generate the parameter string that will be saved to the indenters config file.
	TQString parameterString = getParameterString();
	if (!m_indenterFileName.isEmpty())
	{
		saveConfigFile(m_settingsDirectoryStr + "/" + m_indenterFileName + ".cfg", parameterString);
	}

	// Make sure the selected indenterID is valid
	if (indenterID < 0)
	{
		indenterID = 0;
	}
	if (indenterID >= m_indenterIniFileList.count())
	{
		indenterID = m_indenterIniFileList.count() - 1;
	}

	m_settings->setValueByName("SelectedIndenter", indenterID);

	// delete all toolbox pages and by this its children
	for (IndenterParameterCategoryPage &categoryPage : m_indenterParameterCategoryPages)
	{
		m_indenterParameterCategoriesToolBox->removeItem(categoryPage.widget);
		delete categoryPage.widget;
	}

	// empty all lists, which stored infos for the toolbox pages and its widgets
	m_indenterParameterCategoryPages.clear();
	m_paramStrings.clear();
	m_paramNumerics.clear();
	m_paramBooleans.clear();
	m_paramMultiples.clear();
	delete m_indenterSettings;

	readIndentIniFile(m_indenterDirectoryStr + "/" + m_indenterIniFileList[indenterID]);

	// Find out how the indenter can be executed.
	createIndenterCallString();

	// Load the users last settings made for this indenter.
	loadConfigFile(m_settingsDirectoryStr + "/" + m_indenterFileName + ".cfg");

	handleChangedIndenterSettings();

	TQApplication::restoreOverrideCursor();
}

/*
    \brief Returns a string containing by the indenter supported file types/extensions divided by a space.
 */
TQString IndentHandler::getPossibleIndenterFileExtensions()
{
	return m_fileTypes;
}

/*
    \brief Returns the path and filename of the current indenter config file.
 */
TQString IndentHandler::getIndenterCfgFile()
{
	TQFileInfo fileInfo(m_indenterDirectoryStr + "/" + m_globalConfigFilename);
	return fileInfo.absFilePath();
}

/*
    \brief Tries to create a call path string for the indenter executable. If successful returns true.
 */
bool IndentHandler::createIndenterCallString()
{
	TQProcess indentProcess(this);
	TQObject::connect(&indentProcess, TQ_SIGNAL(processExited()), this, TQ_SLOT(indenterProcessFinished()));

	if (m_indenterFileName.isEmpty())
	{
		return false;
	}

	// First try to call the indenter inside of the data dir, using some suffix
	// ------------------------------------------------------------------------

	// Set the directory for the indenter execution
	indentProcess.setWorkingDirectory(TQFileInfo(m_indenterDirectoryStr).absFilePath());

	TQStringList extentionList;
	extentionList << "" << ".exe" << ".bat" << ".com" << ".sh";
	for (const TQString &suffix : extentionList)
	{
		m_indenterExecutableSuffix     = suffix;
		m_indenterExecutableCallString = TQFileInfo(m_indenterDirectoryStr).absFilePath() + "/" +
			m_indenterFileName;
		m_indenterExecutableCallString += suffix;

		// Only try to call the indenter, if the file exists.
		if (TQFile::exists(m_indenterExecutableCallString))
		{
			// Only try to call the indenter directly if it is no php file
			if (TQFileInfo(m_indenterExecutableCallString).extension(false).lower() != "php")
			{
				indentProcess.clearArguments();
				indentProcess.addArgument(m_indenterExecutableCallString);
				indentProcess.addArgument(m_indenterShowHelpParameter);
				m_indenterProcessFinished = false;
				indentProcess.start();
				int counter = 20; // roughtly 2s at 100ms interval
				while (!m_indenterProcessFinished && counter > 0)
				{
					usleep(100 * 1000);
					tqApp->processEvents();
					--counter;
				}
				if (indentProcess.normalExit())
				{
					m_indenterExecutableCallString = "\"" + m_indenterExecutableCallString + "\"";
					return true;
				}
				else
				{
					if (indentProcess.isRunning())
					{
						indentProcess.kill();
					}
				}
			}

			// Test for needed interpreters
			// ----------------------------
			// If the file could not be executed, try to find a shebang at its start or test if its a php
			// file.
			TQString interpreterName = "";
			TQFile   indenterExecutable(m_indenterExecutableCallString);

			// If indenter executable file has .php as suffix, use php as default interpreter
			if (TQFileInfo(m_indenterExecutableCallString).extension(false).lower() == "php")
			{
				interpreterName = "php -f";
			}
			// Else try to open the file and read the shebang.
			else if (indenterExecutable.open(IO_ReadOnly))
			{
				// Read the first line of the file.
				TQTextStream indenterExecutableContent(&indenterExecutable);
				TQString     firstLineOfIndenterExe = indenterExecutableContent.readLine();
				indenterExecutable.close();

				// If the initial shebang is found, read the named intepreter. e.g. perl
				if (firstLineOfIndenterExe.startsWith("#!"))
				{
					// Get the rightmost word. by splitting the string into only full words.
					TQStringList indenterFirstLineStrings = TQStringList::split("/", firstLineOfIndenterExe);
					interpreterName = indenterFirstLineStrings.last();
				}
			}

			// Try to call the interpreter, if it exists.
			if (!interpreterName.isEmpty())
			{
				m_indenterExecutableCallString = interpreterName + " \"" +
					m_indenterExecutableCallString + "\"";
				indentProcess.clearArguments();
				indentProcess.addArgument(interpreterName);
				indentProcess.addArgument("-h");
				m_indenterProcessFinished = false;
				indentProcess.start();
				int counter = 20; // roughtly 2s at 100ms interval
				while (!m_indenterProcessFinished && counter > 0)
				{
					usleep(100 * 1000);
					tqApp->processEvents();
					--counter;
				}
				if (indentProcess.normalExit())
				{
					return true;
				}
				// now we know an interpreter is needed but it could not be called, so inform the user.
				else
				{
					if (indentProcess.isRunning())
					{
						indentProcess.kill();
					}
					m_errorMessageDialog->showMessage(tr("Interpreter needed"),
							tr("To use the selected indenter the program \"%1\" needs to be available in the global environment. "
								"You should add an entry to your path settings.").arg(interpreterName));
					return true;
				}
			}
		}
	}

	// If unsuccessful try if the indenter executable is a JavaScript file
	// -------------------------------------------------------------------
	m_indenterExecutableSuffix     = ".js";
	m_indenterExecutableCallString = TQFileInfo(m_indenterDirectoryStr).absFilePath() + "/" +
		m_indenterFileName;
	m_indenterExecutableCallString += m_indenterExecutableSuffix;
	if (TQFile::exists(m_indenterExecutableCallString))
	{
		return true;
	}

	// If unsuccessful try to call the indenter global, using some suffix
	// ------------------------------------------------------------------
	extentionList.clear();
	extentionList << "" << ".exe" << ".bat" << ".com" << ".sh";
	for (const TQString &suffix : extentionList)
	{
		m_indenterExecutableSuffix     = suffix;
		m_indenterExecutableCallString = m_indenterFileName + suffix;
		indentProcess.clearArguments();
		indentProcess.addArgument(m_indenterExecutableCallString);
		indentProcess.addArgument(m_indenterShowHelpParameter);
		m_indenterProcessFinished = false;
		indentProcess.start();
		int counter = 20; // roughtly 2s at 100ms interval
		while (!m_indenterProcessFinished && counter > 0)
		{
			usleep(100 * 1000);
			tqApp->processEvents();
			--counter;
		}
		if (indentProcess.normalExit())
		{
			return true;
		}
		else
		{
			if (indentProcess.isRunning())
			{
				indentProcess.kill();
			}
		}
	}

	// If even globally calling the indenter fails, try calling .com and .exe via wine
	// -------------------------------------------------------------------------------
	m_indenterExecutableCallString = "\"" + TQFileInfo(m_indenterDirectoryStr).absFilePath() + "/" +
		m_indenterFileName;

	extentionList.clear();
	extentionList << ".exe" << ".com";
	for (const TQString &suffix : extentionList)
	{
		m_indenterExecutableSuffix = suffix;
		if (TQFile::exists(m_indenterDirectoryStr + "/" + m_indenterFileName + suffix))
		{
			indentProcess.clearArguments();
			indentProcess.addArgument("wine");
			indentProcess.addArgument("--version");
			m_indenterProcessFinished = false;
			indentProcess.start();
			int counter = 20; // roughtly 2s at 100ms interval
			while (!m_indenterProcessFinished && counter > 0)
			{
				usleep(100 * 1000);
				tqApp->processEvents();
				--counter;
			}
			// if the process of wine was not callable assume that wine is not installed
			if (indentProcess.normalExit())
			{
				m_errorMessageDialog->showMessage(tr("wine not installed"), tr(
							"There exists only a win32 executable of the indenter and wine does not seem to be installed. Please install wine to be able to run the indenter."));
				m_indenterExecutableCallString = "";
				return false;
			}
			else
			{
				if (indentProcess.isRunning())
				{
					indentProcess.kill();
				}
				m_indenterExecutableCallString = "\"" +
					TQFileInfo(m_indenterDirectoryStr).absFilePath() + "/";
				m_indenterExecutableCallString += m_indenterFileName + suffix + "\"";
				m_indenterExecutableCallString  = "wine " + m_indenterExecutableCallString;

				return true;
			}
		}
	}

	m_indenterExecutableCallString = "";
	m_indenterExecutableSuffix     = "";
	if (indentProcess.isRunning())
	{
		indentProcess.kill();
	}
	return false;
}

/*
    \brief Returns a string that points to where the indenters manual can be found.
 */
TQString IndentHandler::getManual()
{
	if (m_indenterSettings != nullptr)
	{
		return m_indenterSettings->value("header/manual").toString();
	}
	else
	{
		return "";
	}
}

/*
    \brief This slot gets the reference to the indenters manual and opens it.
 */
void IndentHandler::showIndenterManual() const
{
//--- 	TQString manualReference = getManual();
//--- 	TQDesktopServices::openUrl(manualReference);
}

/*
    \brief Returns the name of the currently selected indenter.
 */
TQString IndentHandler::getCurrentIndenterName()
{
	TQString currentIndenterName = m_indenterSelectionCombobox->currentText();

	// Remove the supported programming languages from indenters name, which are set in braces.
	if (currentIndenterName.find("(") > 0)
	{
		// Using index-1 to also leave out the blank before the brace.
		currentIndenterName = currentIndenterName.left(currentIndenterName.find("(") - 1);
	}

	return currentIndenterName;
}

/*
    \brief Shows a file open dialog to open an existing config file for the current indenter.
    If the file was successfully opened, the indent handler is called to load the settings
		and update itself.
*/
void IndentHandler::openConfigFileDialog()
{
	TQString configFilePath = TQFileDialog::getOpenFileName(getIndenterCfgFile(), tr("All files (*.*)"),
	        nullptr, nullptr, tr("Choose indenter config file"));

	if (!configFilePath.isEmpty())
	{
		// If the config file was loaded successfully, inform any who is interested about it.
		if (loadConfigFile(configFilePath))
		{
			handleChangedIndenterSettings();
		}
	}
}

/*
    \brief Calls the "save as" dialog to save the indenter config file under a chosen name.
    If the file already exists and it should be overwritten, a warning will be shown before.
*/
void IndentHandler::saveasIndentCfgFileDialog()
{
	TQString fileName = TQFileDialog::getSaveFileName(getIndenterCfgFile(), tr("All files (*.*)"),
	        nullptr, nullptr, tr("Save indent config file"));

	if (!fileName.isEmpty())
	{
		TQFile::remove(fileName);
		TQFile outCfgFile(fileName);
		outCfgFile.open(IO_ReadWrite | IO_Translate);
		TQCString paramCString = getParameterString().local8Bit();
		outCfgFile.writeBlock(paramCString.data(), paramCString.length());
		outCfgFile.close();
	}
}

/*
    \brief Invokes the indenter to create a shell script.
    Lets the indenter create a shell script for calling the indenter out of any
    other application and open a save dialog for saving the shell script.
*/
void IndentHandler::createIndenterCallShellScript()
{
	TQString shellScriptExtension;
	shellScriptExtension = "sh";

	TQString fileExtensions = tr("Shell Script") + " (*." + shellScriptExtension + ");;" + tr(
		"All files") + " (*.*)";

	TQString currentIndenterName = getCurrentIndenterName();
	currentIndenterName = currentIndenterName.replace(" ", "_");

	TQString indenterScriptName = "call_" + currentIndenterName + "." + shellScriptExtension;
	TQString shellScriptFileName = TQFileDialog::getSaveFileName(indenterScriptName,
	        fileExtensions, nullptr, nullptr, tr("Save shell script"));

	// Saving has been canceled if the filename is empty
	if (shellScriptFileName.isEmpty())
	{
		return;
	}

	// Delete any old file, write the new contents and set executable permissions.
	TQFile::remove(shellScriptFileName);
	TQFile outSrcFile(shellScriptFileName);
	if (outSrcFile.open(IO_ReadWrite | IO_Translate))
	{
		TQString shellScriptConfigFilename = TQFileInfo(shellScriptFileName).baseName() + "." +
		        TQFileInfo(m_globalConfigFilename).extension(false);

		// Get the content of the shell/batch script.
		TQString indenterCallShellScript = generateShellScript(shellScriptConfigFilename);

		// Replace placeholder for script name in script template.
		indenterCallShellScript = indenterCallShellScript.replace("__INDENTERCALLSTRINGSCRIPTNAME__",
		        TQFileInfo(shellScriptFileName).fileName());

		TQCString indenterString = indenterCallShellScript.local8Bit();
		outSrcFile.writeBlock(indenterString.data(), indenterString.length());
		// For non Windows systems set the files executable flag
		outSrcFile.close();
		int _ = std::system("chmod a+x " + shellScriptFileName.local8Bit());

		// Save the indenter config file to the same directory, where the shell srcipt was saved to,
		// because the script will reference it there via "./".
		if (!m_globalConfigFilename.isEmpty())
		{
			saveConfigFile(TQFileInfo(shellScriptFileName).dirPath(true) + "/" + shellScriptConfigFilename,
			        getParameterString());
		}
	}
}

/*
    \brief Resets all parameters to the indenter's default values as they are specified
		in the uigui ini file, but asks the user whether to do it really.
 */
void IndentHandler::resetIndenterParameter()
{
	int messageBoxAnswer = TQMessageBox::question(this, tr("Really reset parameters?"),
	        tr("Do you really want to reset the indenter parameters to the default values?"),
	        TQMessageBox::Yes, TQMessageBox::Cancel);
	if (messageBoxAnswer == TQMessageBox::Yes)
	{
		resetToDefaultValues();
	}
}

/*
    \brief Emits the \a indenterSettingsChanged signal
 */
void IndentHandler::handleChangedIndenterSettings()
{
	emit(indenterSettingsChanged());
}

/*!
    \brief Converts characters < > and & in the \a text to HTML codes &lt &gt and &amp.
 */
TQString IndentHandler::encodeToHTML(const TQString &text)
{
	TQString htmlText = text;
	htmlText.replace("&", "&amp;");
	htmlText.replace("<", "&lt;");
	htmlText.replace(">", "&gt;");
	htmlText.replace('"', "&quot;");
	htmlText.replace("'", "&#39;");
	htmlText.replace("^", "&circ;");
	htmlText.replace("~", "&tilde;");
	htmlText.replace("€", "&euro;");
	htmlText.replace("©", "&copy;");
	return htmlText;
}

#include "IndentHandler.moc"
