/**
 * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
 *                                             ,B" ]L,,p%%%,,,§;, "K
 * Copyright (C) 2014-19 Mandelbulber Team     §R-==%w["'~5]m%=L.=~5N
 *                                        ,=mm=§M ]=4 yJKA"/-Nsaj  "Bw,==,,
 * This file is part of Mandelbulber.    §R.r= jw",M  Km .mM  FW ",§=ß., ,TN
 *                                     ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
 * Mandelbulber is free software:     §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
 * you can redistribute it and/or     §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
 * modify it under the terms of the    "§M=M =D=4"N #"%==A%p M§ M6  R' #"=~.4M
 * GNU General Public License as        §W =, ][T"]C  §  § '§ e===~ U  !§[Z ]N
 * published by the                    4M",,Jm=,"=e~  §  §  j]]""N  BmM"py=ßM
 * Free Software Foundation,          ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
 * either version 3 of the License,    TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
 * or (at your option)                   TW=,-#"%=;[  =Q:["V""  ],,M.m == ]N
 * any later version.                      J§"mr"] ,=,," =="""J]= M"M"]==ß"
 *                                          §= "=C=4 §"eM "=B:m|4"]#F,§~
 * Mandelbulber is distributed in            "9w=,,]w em%wJ '"~" ,=,,ß"
 * the hope that it will be useful,                 . "K=  ,=RMMMßM"""
 * 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 Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
 *
 * ###########################################################################
 *
 * Authors: Krzysztof Marczak (buddhi1980@gmail.com)
 *
 * system data - place for system functions and definition of default file paths
 */

#include "system.hpp"

#include <qstylefactory.h>

#include <clocale>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>

#include <QTextStream>
#include <QtGui>

#include "global_data.hpp"
#include "initparameters.hpp"
#include "interface.hpp"
#include "render_window.hpp"

// custom includes
#ifndef _WIN32
#include <csignal>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#if defined(__APPLE__) || defined(__MACOSX)
#include "CoreFoundation/CoreFoundation.h"
#endif

//#define CLSUPPORT

sSystem systemData;
sActualFileNames actualFileNames;

bool InitSystem()
{
	setlocale(LC_ALL, "");
	systemData.locale = QLocale::system();
	systemData.locale.setNumberOptions(QLocale::OmitGroupSeparator);
	QLocale::setDefault(systemData.locale);

	QTextStream out(stdout);
	QTextStream outErr(stderr);

	systemData.globalTimer.start();

	systemData.homeDir = QDir::toNativeSeparators(QDir::homePath() + QDir::separator());
#ifdef _WIN32 /* WINDOWS */
	systemData.sharedDir = QDir::toNativeSeparators(QDir::currentPath() + QDir::separator());
	systemData.docDir =
		QDir::toNativeSeparators(QDir::currentPath() + QDir::separator() + "doc" + QDir::separator());
#elif SHARED_DIR_IS_APP_DIR
	/* used for AppImage, which requires fixed data bundled at same location, as the application */
	QString sharePath;
#if defined(__APPLE__) || defined(__MACOSX)
	CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
	CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
	sharePath = QString::fromCFString(macPath);
	CFRelease(appUrlRef);
	CFRelease(macPath);
#else
	sharePath = QDir::currentPath();
#endif
	out << "sharePath directory: " << sharePath << endl;

	systemData.sharedDir = QDir::toNativeSeparators(sharePath + QDir::separator());
	systemData.docDir =
		QDir::toNativeSeparators(sharePath + QDir::separator() + "doc" + QDir::separator());
#else //_WIN32
// if SHARED_DIR is not defined under Linux then use path relative to main executable directory
#ifndef SHARED_DIR
	QDir shareDir(QCoreApplication::applicationDirPath());
	bool success = false;
	if (shareDir.cdUp())
	{
		if (shareDir.cd("share"))
		{
			QDir shareMandelbulber = shareDir;
			if (shareMandelbulber.cd("mandelbulber2"))
			{
				systemData.sharedDir =
					QDir::cleanPath(shareMandelbulber.absolutePath()) + QDir::separator();
				success = true;
			}
			else
			{
				outErr << "Error! There is no 'mandelbulber2' directory in "
							 << shareMandelbulber.absolutePath() << endl;
			}

			QDir shareDocMandelbulber = shareDir;
			if (shareDocMandelbulber.cd("doc/mandelbulber2"))
			{
				systemData.docDir =
					QDir::cleanPath(shareDocMandelbulber.absolutePath()) + QDir::separator();
			}
			else
			{
				outErr << "Error! There is no 'doc/mandelbulber2' directory in "
							 << shareDocMandelbulber.absolutePath() << endl;
			}
		}
		else
		{
			outErr << "Error! There is no 'share' directory in " << shareDir.absolutePath() << endl;
		}
	}
	else
	{
		outErr << "Error: " << QCoreApplication::applicationDirPath() << " has no parent directory!"
					 << endl;
	}

	// try to use default share dir
	if (!success)
	{
		outErr << "Trying to use /usr/share/mandelbulber2 as program data directory" << endl;
		if (shareDir.cd("/usr/share/mandelbulber2"))
		{
			systemData.sharedDir = QDir::cleanPath(shareDir.absolutePath()) + QDir::separator();
		}
		else
		{
			outErr << "Error! Directory "
						 << "/usr/share/mandelbulber2"
						 << "doesn't exist!" << endl;
		}

		if (shareDir.cd("/usr/share/doc/mandelbulber2"))
		{
			systemData.docDir = QDir::cleanPath(shareDir.absolutePath() + QDir::separator());
		}
		else
		{
			outErr << "Error! Directory "
						 << "/usr/share/doc/mandelbulber2"
						 << "doesn't exist!" << endl;
		}
	}

#else	// SHARED_DIR
	systemData.sharedDir = QDir::toNativeSeparators(QString(SHARED_DIR) + QDir::separator());
	systemData.docDir = QDir::toNativeSeparators(QString(SHARED_DOC_DIR) + QDir::separator());
#endif // else SHARED_DIR
#endif // else _WIN32

// logfile
#ifdef _WIN32 /* WINDOWS */
	systemData.logfileName = systemData.homeDir + "mandelbulber_log.txt";
#else
	systemData.logfileName = systemData.homeDir + ".mandelbulber_log.txt";
#endif
	FILE *logfile = fopen(systemData.logfileName.toLocal8Bit().constData(), "w");
	if (logfile)
	{
		fclose(logfile);
	}

	out << "Mandelbulber " << MANDELBULBER_VERSION_STRING << "\n";
	out << "Log file name: " << systemData.logfileName << endl;
	WriteLogString("Mandelbulber version", QString(MANDELBULBER_VERSION_STRING), 1);

	// detecting number of CPU cores
	systemData.numberOfThreads = get_cpu_count();

	printf("Detected %d CPUs\n", systemData.numberOfThreads);
	WriteLogDouble("CPUs detected", systemData.numberOfThreads, 2);

#ifdef ONETHREAD // for debugging
	NR_THREADS = 1;
#endif

// data directory location
#ifdef _WIN32 /* WINDOWS */
	systemData.SetDataDirectoryHidden(systemData.homeDir + "mandelbulber" + QDir::separator());
	systemData.SetDataDirectoryPublic(systemData.homeDir + "mandelbulber" + QDir::separator());
#else
	systemData.SetDataDirectoryHidden(
		QDir::toNativeSeparators(systemData.homeDir + ".mandelbulber" + QDir::separator()));
	systemData.SetDataDirectoryPublic(
		QDir::toNativeSeparators(systemData.homeDir + "mandelbulber" + QDir::separator()));
#endif
	out << "Program data files directory " << systemData.sharedDir << endl;
	out << "Default data hidden directory: " << systemData.GetDataDirectoryHidden() << endl;
	WriteLogString("Default data hidden directory", systemData.GetDataDirectoryHidden(), 1);
	out << "Default data public directory: " << systemData.GetDataDirectoryPublic() << endl;
	WriteLogString("Default data public directory", systemData.GetDataDirectoryPublic(), 1);

	//*********** temporary set to false ************
	systemData.noGui = false;
	systemData.silent = false;

	systemData.lastSettingsFile = QDir::toNativeSeparators(
		systemData.GetSettingsFolder() + QDir::separator() + QString("settings.fract"));
	systemData.lastImageFile = QDir::toNativeSeparators(
		systemData.GetImagesFolder() + QDir::separator() + QString("image.jpg"));
	systemData.lastImagePaletteFile = QDir::toNativeSeparators(
		systemData.sharedDir + "textures" + QDir::separator() + QString("colour palette.jpg"));
	systemData.lastGradientFile = QDir::toNativeSeparators(
		systemData.GetGradientsFolder() + QDir::separator() + "colors.gradient");

	QLocale systemLocale = QLocale::system();
	systemData.decimalPoint = systemLocale.decimalPoint();
	WriteLogString("Decimal point", QString(systemData.decimalPoint), 2);

	systemData.supportedLanguages.insert("en_EN", "English");
	systemData.supportedLanguages.insert("pl_PL", "Polski");
	systemData.supportedLanguages.insert("de_DE", "Deutsch");
	systemData.supportedLanguages.insert("it_IT", "Italiano");
	systemData.supportedLanguages.insert("nl_NL", "Nederlands");
	systemData.supportedLanguages.insert("es_ES", "Español");

	// get number of columns of console
	systemData.terminalWidth = 80;
	systemData.loggingVerbosity = 3;
	systemData.settingsLoadedFromCLI = false;
	systemData.globalStopRequest = false;
	systemData.isOutputTty = IsOutputTty();

#ifndef WIN32
	handle_winch(-1);
#endif
	return true;
}

// SIGWINCH is called when the window is resized.
void handle_winch(int sig)
{
	(void)sig;
#ifndef _WIN32
	signal(SIGWINCH, SIG_IGN);
	struct winsize w
	{
	};
	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
	systemData.terminalWidth = w.ws_col;
	if (systemData.terminalWidth <= 0) systemData.terminalWidth = 80;
	if (systemData.terminalWidth >= 150) systemData.terminalWidth = 150;
	if (!systemData.isOutputTty) systemData.terminalWidth = 80;
	signal(SIGWINCH, handle_winch);
#endif
}

int get_cpu_count()
{
	return QThread::idealThreadCount();
}

void WriteLogCout(const QString &text, int verbosityLevel)
{
	// output to console
	cout << text.toStdString();
	cout.flush();
	WriteLog(text, verbosityLevel);
}

void WriteLog(const QString &text, int verbosityLevel)
{
	// verbosity level:
	// 1 - only errors
	// 2 - main events / actions
	// 3 - detailed events / actions

	QMutex mutex;

	if (verbosityLevel <= systemData.loggingVerbosity)
	{
		mutex.lock();
		FILE *logfile = fopen(systemData.logfileName.toLocal8Bit().constData(), "a");
#ifdef _WIN32
		QString logText = QString("PID: %1, time: %2, %3\n")
												.arg(QCoreApplication::applicationPid())
												.arg(QString::number(clock() / 1.0e3, 'f', 3))
												.arg(text);
#else
		QString logText =
			QString("PID: %1, time: %2, %3\n")
				.arg(QCoreApplication::applicationPid())
				.arg(QString::number((systemData.globalTimer.nsecsElapsed()) / 1.0e9, 'f', 9))
				.arg(text);
#endif
		if (logfile)
		{
			fputs(logText.toLocal8Bit().constData(), logfile);
			fclose(logfile);
		}
		mutex.unlock();
		// write to log in window
		if (gMainInterface && gMainInterface->mainWindow != nullptr)
		{
			emit gMainInterface->mainWindow->AppendToLog(logText);
		}
	}
}

void WriteLogString(const QString &text, const QString &value, int verbosityLevel)
{
	WriteLog(text + ", value = " + value, verbosityLevel);
}

void WriteLogDouble(const QString &text, double value, int verbosityLevel)
{
	WriteLog(text + ", value = " + QString::number(value), verbosityLevel);
}

void WriteLogInt(const QString &text, int value, int verbosityLevel)
{
	WriteLog(text + ", value = " + QString::number(value), verbosityLevel);
}

void WriteLogSizeT(const QString &text, size_t value, int verbosityLevel)
{
	WriteLog(text + ", value = " + QString::number(value), verbosityLevel);
}

bool CreateDefaultFolders()
{
	// create data directory if not exists
	bool result = true;

	result &= CreateFolder(systemData.GetDataDirectoryHidden());
	result &= CreateFolder(systemData.GetDataDirectoryPublic());
	result &= CreateFolder(systemData.GetImagesFolder());
	result &= CreateFolder(systemData.GetThumbnailsFolder());
	result &= CreateFolder(systemData.GetToolbarFolder());
	result &= CreateFolder(systemData.GetHttpCacheFolder());
	result &= CreateFolder(systemData.GetCustomWindowStateFolder());
	result &= CreateFolder(systemData.GetSettingsFolder());
	result &= CreateFolder(systemData.GetSlicesFolder());
	result &= CreateFolder(systemData.GetMaterialsFolder());
	result &= CreateFolder(systemData.GetAnimationFolder());
	result &= CreateFolder(systemData.GetNetrenderFolder());
	result &= CreateFolder(systemData.GetGradientsFolder());

	RetrieveToolbarPresets(false);
	RetrieveExampleMaterials(false);

#ifdef CLSUPPORT
	string oclDir = systemData.dataDirectory + "/custom_ocl_formulas";
	QString qoclDir = QString::fromStdString(oclDir);
	if (!QDir(qoclDir).exists())
	{
		result &= CreateFolder(oclDir);
		if (result)
		{
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example1.c", oclDir + "/cl_example1.c");
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example2.c", oclDir + "/cl_example2.c");
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example3.c", oclDir + +"/cl_example3.c");
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example1Init.c",
				oclDir + "/cl_example1Init.c");
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example2Init.c",
				oclDir + "/cl_example2Init.c");
			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example3Init.c",
				oclDir + "/cl_example3Init.c");
		}
	}
#endif

	actualFileNames.actualFilenameSettings =
		QString("settings") + QDir::separator() + "default.fract";
	actualFileNames.actualFilenameSettings =
		QDir::toNativeSeparators(actualFileNames.actualFilenameSettings);

	actualFileNames.actualFilenameImage = QString("images") + QDir::separator() + "image.jpg";
	actualFileNames.actualFilenameImage =
		QDir::toNativeSeparators(actualFileNames.actualFilenameImage);

	actualFileNames.actualFilenamePalette =
		systemData.sharedDir + "textures" + QDir::separator() + "colour palette.jpg";
	actualFileNames.actualFilenamePalette =
		QDir::toNativeSeparators(actualFileNames.actualFilenamePalette);

	ClearNetRenderCache();
	DeleteOldChache(systemData.GetThumbnailsFolder(), 90);
	DeleteOldChache(systemData.GetHttpCacheFolder(), 10);

	return result;
}

bool CreateFolder(const QString &qName)
{
	if (QDir(qName).exists())
	{
		WriteLogString("Directory already exists", qName, 1);
		return true;
	}
	else
	{
		if (QDir().mkdir(qName))
		{
			WriteLogString("Directory created", qName, 2);
			return true;
		}
		else
		{
			WriteLogString("Directory can't be created", qName, 1);
			qCritical() << "error: directory " << qName << " cannot be created" << endl;
			return false;
		}
	}
}

void DeleteAllFilesFromDirectory(
	const QString &folder, const QString &filterExpression, QRegExp::PatternSyntax pattern)
{
	QRegExp rx(filterExpression);
	rx.setPatternSyntax(pattern);

	if (QDir(folder).exists())
	{
		QDirIterator folderIterator(folder);
		while (folderIterator.hasNext())
		{
			folderIterator.next();
			if (folderIterator.fileName() == "." || folderIterator.fileName() == "..") continue;
			if (rx.exactMatch(folderIterator.fileName()))
			{
				if (QFile::remove(folderIterator.filePath()))
				{
					WriteLogString("File deleted", folderIterator.filePath(), 2);
				}
				else
				{
					WriteLogString("File not deleted", folderIterator.filePath(), 1);
				}
			}
		}
	}
	else
	{
		WriteLogString("Directory does not exist", folder, 2);
	}
}

int fcopy(const QString &source, const QString &dest)
{
	// ------ file reading

	char *buffer;

	FILE *pFile = fopen(source.toLocal8Bit().constData(), "rb");
	if (pFile == nullptr)
	{
		qCritical() << "Can't open source file for copying: " << source << endl;
		WriteLogString("Can't open source file for copying", source, 1);
		return 1;
	}

	// obtain file size:
	fseek(pFile, 0, SEEK_END);
	const long int lSize = ftell(pFile);
	rewind(pFile);

	// allocate memory to contain the whole file:
	if (lSize > 0)
	{
		buffer = new char[lSize];

		// copy the file into the buffer:
		const size_t result = fread(buffer, 1, lSize, pFile);
		if (result != size_t(lSize))
		{
			qCritical() << "Can't read source file for copying: " << source << endl;
			WriteLogString("Can't read source file for copying", source, 1);
			delete[] buffer;
			fclose(pFile);
			return 2;
		}
	}
	else
	{
		qCritical() << "Can't obtain file size: " << source;
		fclose(pFile);
		return 4;
	}
	fclose(pFile);

	// ----- file writing

	pFile = fopen(dest.toLocal8Bit().constData(), "wb");
	if (pFile == nullptr)
	{
		qCritical() << "Can't open destination file for copying: " << dest << endl;
		WriteLogString("Can't open destination file for copying", dest, 1);
		delete[] buffer;
		return 3;
	}
	fwrite(buffer, 1, lSize, pFile);
	fclose(pFile);

	delete[] buffer;
	WriteLogString("File copied", dest, 2);
	return 0;
}

void Wait(long int time)
{
	QMutex dummy;
	dummy.lock();
	QWaitCondition waitCondition;
	waitCondition.wait(&dummy, time);
	dummy.unlock();
}

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
	QByteArray localMsg = msg.toLocal8Bit();
	QString text;
	switch (type)
	{
#if QT_VERSION >= 0x050500
		case QtInfoMsg:
			fprintf(stderr, "Info: %s\n", localMsg.constData());
			text = QString("Info: ") + QString(localMsg.constData());
			break;
#endif
		case QtDebugMsg:
			fprintf(stderr, "Debug: %s\n", localMsg.constData());
			text = QString("Debug: ") + QString(localMsg.constData());
			break;
		case QtWarningMsg:
			fprintf(stderr, "Warning: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
				context.line, context.function);
			text = QString("Warning: ") + QString(localMsg.constData()) + " (" + context.file + ":"
						 + QString::number(context.line) + ", " + context.function;
			break;
		case QtCriticalMsg:
			fprintf(stderr, "Critical: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
				context.line, context.function);
			text = QString("Critical: ") + QString(localMsg.constData()) + " (" + context.file + ":"
						 + QString::number(context.line) + ", " + context.function;
			break;
		case QtFatalMsg:
			fprintf(stderr, "Fatal: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
				context.line, context.function);
			text = QString("Fatal: ") + QString(localMsg.constData()) + " (" + context.file + ":"
						 + QString::number(context.line) + ", " + context.function;
			abort();
			break;
	}
	WriteLog(text, 1);

	if (type == QtFatalMsg) abort();
}

void UpdateDefaultPaths()
{
	systemData.lastSettingsFile =
		gPar->Get<QString>("default_settings_path") + QDir::separator() + QString("settings.fract");
	systemData.lastImageFile =
		gPar->Get<QString>("default_image_path") + QDir::separator() + QString("image.jpg");
	gPar->Set("file_background",
		gPar->Get<QString>("default_textures_path") + QDir::separator() + "background.jpg");
	gPar->Set(
		"file_envmap", gPar->Get<QString>("default_textures_path") + QDir::separator() + "envmap.jpg");
	gPar->Set("file_lightmap",
		gPar->Get<QString>("default_textures_path") + QDir::separator() + "lightmap.jpg");
}

void UpdateUIStyle()
{
	// Selecting default Fusion UI style in case if wrong style is selected
	if (gPar->Get<int>("ui_style_type") < 0
			|| gPar->Get<int>("ui_style_type") >= QStyleFactory::keys().size())
	{
		QStringList listOfStyles = QStyleFactory::keys();
		int indexOfFusion = listOfStyles.indexOf("Fusion");
		gPar->Set<int>("ui_style_type", indexOfFusion);
	}

	// set ui style
	if (gPar->Get<int>("ui_style_type") >= 0
			&& gPar->Get<int>("ui_style_type") < QStyleFactory::keys().size())
	{
		QApplication::setStyle(
			QStyleFactory::create(QStyleFactory::keys().at(gPar->Get<int>("ui_style_type"))));
	}
}

void UpdateUISkin()
{
	static QPalette defaultPalette; // cache "normal" skin on first call
	QPalette palette;

	QColor colorBackground1 = palette.color(QPalette::Window);
	QColor colorBackground2 = palette.color(QPalette::Base);
	QColor colorText1 = palette.color(QPalette::WindowText);
	QColor colorText2 = palette.color(QPalette::HighlightedText);

	switch (gPar->Get<int>("ui_skin"))
	{
		case 1: // dark skin
			colorBackground1 = QColor(53, 53, 53);
			colorBackground2 = QColor(25, 25, 25);
			colorText1 = Qt::white;
			colorText2 = Qt::black;
			break;
		case 2: // light skin (only roughly inverted dark skin)
			colorBackground1 = QColor(240, 240, 240);
			colorBackground2 = QColor(250, 250, 250);
			colorText1 = Qt::black;
			colorText2 = Qt::white;
			break;
		case 3: // Nasa Font light
			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
			QApplication::setFont(QFont("nasalization"));
			colorBackground1 = QColor(167, 173, 187);
			colorBackground2 = QColor(192, 196, 207);
			colorText1 = Qt::black;
			colorText2 = Qt::white;
			break;
		case 4: // Nasa Font dark
			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
			QApplication::setFont(QFont("nasalization"));
			colorBackground1 = QColor(52, 61, 70);
			colorBackground2 = QColor(79, 91, 102);
			colorText1 = Qt::white;
			colorText2 = Qt::black;
			break;
		case 5: // Nasa Font dark green
			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
			QApplication::setFont(QFont("nasalization"));
			colorBackground1 = QColor(20, 40, 10);
			colorBackground2 = QColor(30, 50, 0);
			colorText1 = Qt::green;
			colorText2 = Qt::black;
			break;
		case 6: // Nasa Font dark blue
			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
			QApplication::setFont(QFont("nasalization"));
			colorBackground1 = QColor(10, 10, 40);
			colorBackground2 = QColor(20, 20, 60);
			colorText1 = QColor(50, 150, 255);
			colorText2 = Qt::black;
			break;
		case 7: // Nasa Font dark brown
			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
			QApplication::setFont(QFont("nasalization"));
			colorBackground1 = QColor(40, 10, 10);
			colorBackground2 = QColor(60, 20, 20);
			colorText1 = QColor(255, 150, 50);
			colorText2 = Qt::black;
			break;
		default: // normal skin
			palette = defaultPalette;
			break;
	}
	if (gPar->Get<int>("ui_skin") != 0)
	{
		palette.setColor(QPalette::Window, colorBackground1);
		palette.setColor(QPalette::WindowText, colorText1);
		palette.setColor(QPalette::Base, colorBackground2);
		palette.setColor(QPalette::AlternateBase, colorBackground1);
		palette.setColor(QPalette::ToolTipBase, colorBackground2);
		palette.setColor(QPalette::ToolTipText, colorText1);
		palette.setColor(QPalette::Text, colorText1);
		palette.setColor(QPalette::Button, colorBackground1);
		palette.setColor(QPalette::ButtonText, colorText1);
		palette.setColor(QPalette::BrightText, Qt::red);
		palette.setColor(QPalette::Link, QColor(42, 130, 218));

		palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
		palette.setColor(QPalette::HighlightedText, colorText2);
	}
	// set ui skin
	QApplication::setPalette(palette);
}

void UpdateLanguage()
{
	// Set language from locale
	WriteLog("Prepare translator", 2);
	static QTranslator mandelbulberMainTranslator;
	static QTranslator mandelbulberFractalUiTranslator;
	static QTranslator qtTranslator;

	QString locale = systemData.locale.name();
	if (gPar)
	{
		if (systemData.supportedLanguages.contains(gPar->Get<QString>("language")))
		{
			locale = gPar->Get<QString>("language");
		}
	}

	WriteLogString("locale", locale, 2);
	mandelbulberMainTranslator.load(locale, systemData.sharedDir + QDir::separator() + "language");
	mandelbulberFractalUiTranslator.load(
		"formula_" + locale, systemData.sharedDir + QDir::separator() + "language");

	WriteLog("Installing translator", 2);
	QCoreApplication::installTranslator(&mandelbulberMainTranslator);
	QCoreApplication::installTranslator(&mandelbulberFractalUiTranslator);

	// try to load qt translator
	if (qtTranslator.load(
				QLatin1String("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))
			|| qtTranslator.load(
				QLatin1String("qtbase_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
	{
		QCoreApplication::installTranslator(&qtTranslator);
	}
}

void RetrieveToolbarPresets(bool force)
{
	if (QDir(systemData.GetToolbarFolder())
					.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries)
					.count()
				== 0
			|| force)
	{
		// first run of program (or all toolbar items deleted) -> copy toolbar presets to working folder
		QDirIterator toolbarFiles(systemData.sharedDir + "toolbar");
		while (toolbarFiles.hasNext())
		{
			toolbarFiles.next();
			if (toolbarFiles.fileName() == "." || toolbarFiles.fileName() == "..") continue;
			fcopy(toolbarFiles.filePath(),
				systemData.GetToolbarFolder() + QDir::separator() + toolbarFiles.fileName());
		}
	}
}

void RetrieveExampleMaterials(bool force)
{
	if (QDir(systemData.GetMaterialsFolder())
					.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries)
					.count()
				== 0
			|| force)
	{
		// first run of program (or all material items deleted) -> copy materials to working folder
		QDirIterator materialFiles(systemData.sharedDir + "materials");
		while (materialFiles.hasNext())
		{
			materialFiles.next();
			if (materialFiles.fileName() == "." || materialFiles.fileName() == "..") continue;
			fcopy(materialFiles.filePath(),
				systemData.GetMaterialsFolder() + QDir::separator() + materialFiles.fileName());
		}
	}
}

QThread::Priority GetQThreadPriority(enumRenderingThreadPriority priority)
{
	switch (priority)
	{
		case renderingPriorityLowest: return QThread::LowestPriority;
		case renderingPriorityLow: return QThread::LowPriority;
		case renderingPriorityNormal: return QThread::NormalPriority;
		case renderingPriorityHigh: return QThread::HighPriority;
		default: return QThread::NormalPriority;
	}
}

void CalcPreferredFontSize(bool noGui)
{
	if (!noGui)
	{
		const int fontSize = (int)QApplication::font().pointSizeF();
		systemData.SetPreferredFontPointSize(fontSize);

		QFontMetrics fm(QApplication::font());
		const int pixelFontSize = fm.height();

		const int thumbnailSize = (pixelFontSize * 8);
		systemData.SetPreferredThumbnailSize(thumbnailSize);
	}
	else
	{
		systemData.SetPreferredFontPointSize(10);
		systemData.SetPreferredFontSize(10);
		systemData.SetPreferredThumbnailSize(80);
	}
}

QString sSystem::GetIniFile() const
{
	double version = MANDELBULBER_VERSION;
	int versionInt = int(version * 100);

	QString iniFileName = QString("mandelbulber_%1.ini").arg(versionInt);
	QString fullIniFileName = dataDirectoryHidden + iniFileName;

	// if setting file doesn't exist then look for older files
	if (!QFile::exists(fullIniFileName))
	{
		QString tempFileName;
		for (int ver = versionInt; ver >= 212; ver--)
		{
			if (ver == 212)
			{
				tempFileName = QString("mandelbulber.ini");
			}
			else
			{
				tempFileName = QString("mandelbulber_%1.ini").arg(ver);
			}
			if (QFile::exists(dataDirectoryHidden + tempFileName))
			{
				fcopy(dataDirectoryHidden + tempFileName, fullIniFileName);
				WriteLogString("Found older settings file", dataDirectoryHidden + tempFileName, 1);
				break;
			}
		}
	}
	return fullIniFileName;
}

void sSystem::Upgrade() const
{
	QStringList moveFolders = {GetSettingsFolder(), GetImagesFolder(), GetSlicesFolder(),
		GetMaterialsFolder(), GetAnimationFolder()};
	for (int i = 0; i < moveFolders.size(); i++)
	{
		const QString &folderSource = moveFolders.at(i);
		QString folderTarget = folderSource;
		folderTarget.replace(dataDirectoryHidden, dataDirectoryPublic);
		if (QFileInfo::exists(folderTarget))
		{
			qCritical() << QString("target folder %1 already exists, won't move!").arg(folderTarget);
		}
		else if (!QDir().rename(folderSource, folderTarget))
		{
			qCritical() << QString("cannot move folder %1 to %2!").arg(folderSource, folderTarget);
		}
	}
}

QString sSystem::GetImageFileNameSuggestion()
{
	QString imageBaseName = QFileInfo(lastImageFile).completeBaseName();

	// if the last image file has been saved manually, this is the suggestion for the filename
	if (!lastImageFile.endsWith("image.jpg")) return imageBaseName;

	// otherwise if the settings has been loaded from a proper .fract file, this fileName's basename
	// is the suggestion
	if (lastSettingsFile.endsWith(".fract")) return QFileInfo(lastSettingsFile).completeBaseName();

	// maybe loaded by clipboard, no better suggestion, than the default lastImageFile's baseName
	return imageBaseName;
}

bool IsOutputTty()
{
#ifdef _WIN32 /* WINDOWS */
	return false;
#else
	return isatty(fileno(stdout));
#endif
}

void ClearNetRenderCache()
{
	QDir dir(systemData.GetNetrenderFolder());
	if (dir.exists())
	{
		QDirIterator it(dir);
		while (it.hasNext())
		{
			QFile file(it.next());
			file.remove();
		}
	}
}

void DeleteOldChache(const QString &directoryPath, int maxDays)
{
	QDir dir(directoryPath);
	int counter = 0;
	qint64 totalSize = 0;
	if (dir.exists())
	{
		QDirIterator it(dir);
		while (it.hasNext())
		{
			QFile file(it.next());
			QFileInfo fileInfo(file);
			QDateTime dateTime = fileInfo.lastModified();
			qint64 days = dateTime.daysTo(QDateTime::currentDateTime());
			if (days > maxDays)
			{
				totalSize += file.size();
				file.remove();
				counter++;
			}
		}
	}
	if (counter > 0)
	{
		QString message = QString("Removed %1 old files from %2. Saved %3 MB")
												.arg(counter)
												.arg(directoryPath)
												.arg(totalSize / 1024 / 1024);
		qInfo() << message;
		WriteLog(message, 2);
	}
}
