#include "settings.h"
#include <QApplication>
#include <QTextCodec>
#include <algorithm>
#include "utils.h"
#include <QDir>
#include "systemconsts.h"
#include <QDebug>
#include <QMessageBox>

const char ValueToChar[28] = {'0', '1', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
                              'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                              's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};

Settings* pSettings;

Settings::Settings(const QString &filename):
    mFilename(filename),
    mSettings(filename,QSettings::IniFormat),
    mDirs(this),
    mEditor(this),
    mEnvironment(this),
    mCompilerSets(this),
    mExecutor(this),
    mDebugger(this),
    mHistory(this)
{
    load();
}

Settings::~Settings()
{
    mEditor.save();
}

void Settings::beginGroup(const QString &group)
{
    mSettings.beginGroup(group);
}

void Settings::endGroup()
{
    mSettings.endGroup();
}

void Settings::saveValue(const QString& group, const QString &key, const QVariant &value) {
    mSettings.beginGroup(group);
    auto act = finally([this] {
        this->mSettings.endGroup();
    });
    mSettings.setValue(key,value);
}

void Settings::saveValue(const QString &key, const QVariant &value)
{
    mSettings.setValue(key,value);
}

QVariant Settings::value(const QString &group, const QString &key, const QVariant &defaultValue)
{
    mSettings.beginGroup(group);
    auto act = finally([this] {
        this->mSettings.endGroup();
    });
    return mSettings.value(key,defaultValue);
}

QVariant Settings::value(const QString &key, const QVariant &defaultValue)
{
    return mSettings.value(key,defaultValue);
}

void Settings::load()
{

    mCompilerSets.loadSets();
    mEnvironment.load();
    mEditor.load();
    mExecutor.load();
    mDebugger.load();
    mHistory.load();
}

Settings::Dirs &Settings::dirs()
{
    return mDirs;
}

Settings::Editor &Settings::editor()
{
    return mEditor;
}

Settings::CompilerSets &Settings::compilerSets()
{
    return mCompilerSets;
}

Settings::Environment &Settings::environment()
{
    return mEnvironment;
}

Settings::Executor &Settings::executor()
{
    return mExecutor;
}

QString Settings::filename() const
{
    return mFilename;
}

Settings::History& Settings::history()
{
    return mHistory;
}

Settings::Debugger& Settings::debugger()
{
    return mDebugger;
}

Settings::Dirs::Dirs(Settings *settings):
    _Base(settings, SETTING_DIRS)
{
}

QString Settings::Dirs::app() const
{
    return QApplication::instance()->applicationDirPath();
}

QString Settings::Dirs::data(Settings::Dirs::DataType dataType) const
{
    using DataType = Settings::Dirs::DataType;
    QString dataDir = includeTrailingPathDelimiter(app())+"data";
    switch (dataType) {
    case DataType::None:
        return dataDir;
    case DataType::ColorSheme:
        return ":/colorschemes/colorschemes";
    }
    return "";
}

QString Settings::Dirs::config(Settings::Dirs::DataType dataType) const
{
    using DataType = Settings::Dirs::DataType;
    QFileInfo configFile(pSettings->filename());
    QString configDir = configFile.path();
    switch (dataType) {
    case DataType::None:
        return configDir;
    case DataType::ColorSheme:
        return includeTrailingPathDelimiter(configDir)+"scheme";
    }
    return "";
}

void Settings::Dirs::doSave()
{

}

void Settings::Dirs::doLoad()
{

}

Settings::_Base::_Base(Settings *settings, const QString &groupName):
    mSettings(settings),
    mGroup(groupName)
{

}

void Settings::_Base::beginGroup()
{
    mSettings->beginGroup(mGroup);
}

void Settings::_Base::endGroup()
{
    mSettings->endGroup();
}

void Settings::_Base::saveValue(const QString &key, const QVariant &value)
{
    mSettings->saveValue(key,value);
}

QVariant Settings::_Base::value(const QString &key, const QVariant &defaultValue)
{
    return mSettings->value(key,defaultValue);
}

bool Settings::_Base::boolValue(const QString &key, bool defaultValue)
{
    return value(key,defaultValue).toBool();
}

int Settings::_Base::intValue(const QString &key, int defaultValue)
{
    return value(key,defaultValue).toInt();
}

QStringList Settings::_Base::stringListValue(const QString &key, const QStringList &defaultValue)
{
    return value(key,defaultValue).toStringList();
}

QColor Settings::_Base::colorValue(const QString &key, const QColor& defaultValue)
{
    return value(key,defaultValue).value<QColor>();
}

QString Settings::_Base::stringValue(const QString &key, const QString& defaultValue)
{
    return value(key,defaultValue).toString();
}

void Settings::_Base::save()
{
    beginGroup();
    doSave();
    endGroup();
}

void Settings::_Base::load()
{
    beginGroup();
    doLoad();
    endGroup();
}

Settings::Editor::Editor(Settings *settings): _Base(settings, SETTING_EDITOR)
{

}

QByteArray Settings::Editor::defaultEncoding()
{
    return mDefaultEncoding;
}

void Settings::Editor::setDefaultEncoding(const QByteArray &value)
{
    mDefaultEncoding = value;
}

bool Settings::Editor::autoIndent()
{
    return mAutoIndent;
}

void Settings::Editor::setAutoIndent(bool value)
{
    mAutoIndent = value;
}

QColor Settings::Editor::caretColor() const
{
    return mCaretColor;
}

void Settings::Editor::setCaretColor(const QColor &caretColor)
{
    mCaretColor = caretColor;
}

bool Settings::Editor::keepCaretX() const
{
    return mKeepCaretX;
}

void Settings::Editor::setKeepCaretX(bool keepCaretX)
{
    mKeepCaretX = keepCaretX;
}

bool Settings::Editor::halfPageScroll() const
{
    return mHalfPageScroll;
}

void Settings::Editor::setHalfPageScroll(bool halfPageScroll)
{
    mHalfPageScroll = halfPageScroll;
}

bool Settings::Editor::gutterFontOnlyMonospaced() const
{
    return mGutterFontOnlyMonospaced;
}

void Settings::Editor::setGutterFontOnlyMonospaced(bool gutterFontOnlyMonospaced)
{
    mGutterFontOnlyMonospaced = gutterFontOnlyMonospaced;
}

int Settings::Editor::gutterRightOffset() const
{
    return mGutterRightOffset;
}

void Settings::Editor::setGutterRightOffset(int gutterRightOffset)
{
    mGutterRightOffset = gutterRightOffset;
}

int Settings::Editor::copyWithFormatAs() const
{
    return mCopyWithFormatAs;
}

void Settings::Editor::setCopyWithFormatAs(int copyWithFormatAs)
{
    mCopyWithFormatAs = copyWithFormatAs;
}

QString Settings::Editor::colorScheme() const
{
    return mColorScheme;
}

void Settings::Editor::setColorScheme(const QString &colorScheme)
{
    mColorScheme = colorScheme;
}

bool Settings::Editor::removeSymbolPairs() const
{
    return mRemoveSymbolPairs;
}

void Settings::Editor::setRemoveSymbolPairs(bool value)
{
    mRemoveSymbolPairs = value;
}

bool Settings::Editor::syntaxCheckWhenLineChanged() const
{
    return mSyntaxCheckWhenLineChanged;
}

void Settings::Editor::setSyntaxCheckWhenLineChanged(bool syntaxCheckWhenLineChanged)
{
    mSyntaxCheckWhenLineChanged = syntaxCheckWhenLineChanged;
}

bool Settings::Editor::syntaxCheckWhenSave() const
{
    return mSyntaxCheckWhenSave;
}

void Settings::Editor::setSyntaxCheckWhenSave(bool syntaxCheckWhenSave)
{
    mSyntaxCheckWhenSave = syntaxCheckWhenSave;
}

bool Settings::Editor::syntaxCheck() const
{
    return mSyntaxCheck;
}

void Settings::Editor::setSyntaxCheck(bool syntaxCheck)
{
    mSyntaxCheck = syntaxCheck;
}

bool Settings::Editor::overwriteSymbols() const
{
    return mOverwriteSymbols;
}

void Settings::Editor::setOverwriteSymbols(bool overwriteSymbols)
{
    mOverwriteSymbols = overwriteSymbols;
}

bool Settings::Editor::completeGlobalInclude() const
{
    return mCompleteGlobalInclude;
}

void Settings::Editor::setCompleteGlobalInclude(bool completeGlobalInclude)
{
    mCompleteGlobalInclude = completeGlobalInclude;
}

bool Settings::Editor::completeDoubleQuote() const
{
    return mCompleteDoubleQuote;
}

void Settings::Editor::setCompleteDoubleQuote(bool completeDoubleQuote)
{
    mCompleteDoubleQuote = completeDoubleQuote;
}

bool Settings::Editor::completeSingleQuote() const
{
    return mCompleteSingleQuote;
}

void Settings::Editor::setCompleteSingleQuote(bool completeSingleQuote)
{
    mCompleteSingleQuote = completeSingleQuote;
}

bool Settings::Editor::completeComment() const
{
    return mCompleteComment;
}

void Settings::Editor::setCompleteComment(bool completeComment)
{
    mCompleteComment = completeComment;
}

bool Settings::Editor::completeBrace() const
{
    return mCompleteBrace;
}

void Settings::Editor::setCompleteBrace(bool completeBrace)
{
    mCompleteBrace = completeBrace;
}

bool Settings::Editor::completeBracket() const
{
    return mCompleteBracket;
}

void Settings::Editor::setCompleteBracket(bool completeBracket)
{
    mCompleteBracket = completeBracket;
}

bool Settings::Editor::completeParenthese() const
{
    return mCompleteParenthese;
}

void Settings::Editor::setCompleteParenthese(bool completeParenthese)
{
    mCompleteParenthese = completeParenthese;
}

bool Settings::Editor::completeSymbols() const
{
    return mCompleteSymbols;
}

void Settings::Editor::setCompleteSymbols(bool completeSymbols)
{
    mCompleteSymbols = completeSymbols;
}

QString Settings::Editor::copyHTMLColorScheme() const
{
    return mCopyHTMLColorScheme;
}

void Settings::Editor::setCopyHTMLColorScheme(const QString &copyHTMLColorScheme)
{
    mCopyHTMLColorScheme = copyHTMLColorScheme;
}

bool Settings::Editor::copyHTMLUseEditorColor() const
{
    return mCopyHTMLUseEditorColor;
}

void Settings::Editor::setCopyHTMLUseEditorColor(bool copyHTMLUseEditorColor)
{
    mCopyHTMLUseEditorColor = copyHTMLUseEditorColor;
}

bool Settings::Editor::copyHTMLUseBackground() const
{
    return mCopyHTMLUseBackground;
}

void Settings::Editor::setCopyHTMLUseBackground(bool copyHTMLUseBackground)
{
    mCopyHTMLUseBackground = copyHTMLUseBackground;
}

QString Settings::Editor::copyRTFColorScheme() const
{
    return mCopyRTFColorScheme;
}

void Settings::Editor::setCopyRTFColorScheme(const QString &copyRTFColorScheme)
{
    mCopyRTFColorScheme = copyRTFColorScheme;
}

bool Settings::Editor::copyRTFUseEditorColor() const
{
    return mCopyRTFUseEditorColor;
}

void Settings::Editor::setCopyRTFUseEditorColor(bool copyRTFUseEditorColor)
{
    mCopyRTFUseEditorColor = copyRTFUseEditorColor;
}

bool Settings::Editor::copyRTFUseBackground() const
{
    return mCopyRTFUseBackground;
}

void Settings::Editor::setCopyRTFUseBackground(bool copyRTFUseBackground)
{
    mCopyRTFUseBackground = copyRTFUseBackground;
}

int Settings::Editor::copyLineLimits() const
{
    return mCopyLineLimits;
}

void Settings::Editor::setCopyLineLimits(int copyLineLimits)
{
    mCopyLineLimits = copyLineLimits;
}

int Settings::Editor::copyCharLimits() const
{
    return mCopyCharLimits;
}

void Settings::Editor::setCopyCharLimits(int copyCharLimits)
{
    mCopyCharLimits = copyCharLimits;
}

bool Settings::Editor::copySizeLimit() const
{
    return mCopySizeLimit;
}

void Settings::Editor::setCopySizeLimit(bool copyLimit)
{
    mCopySizeLimit = copyLimit;
}

int Settings::Editor::gutterLeftOffset() const
{
    return mGutterLeftOffset;
}

void Settings::Editor::setGutterLeftOffset(int gutterLeftOffset)
{
    mGutterLeftOffset = gutterLeftOffset;
}

int Settings::Editor::gutterFontSize() const
{
    return mGutterFontSize;
}

void Settings::Editor::setGutterFontSize(int gutterFontSize)
{
    mGutterFontSize = gutterFontSize;
}

QString Settings::Editor::gutterFontName() const
{
    return mGutterFontName;
}

void Settings::Editor::setGutterFontName(const QString &gutterFontName)
{
    mGutterFontName = gutterFontName;
}

bool Settings::Editor::gutterUseCustomFont() const
{
    return mGutterUseCustomFont;
}

void Settings::Editor::setGutterUseCustomFont(bool gutterUseCustomFont)
{
    mGutterUseCustomFont = gutterUseCustomFont;
}

bool Settings::Editor::gutterLineNumbersStartZero() const
{
    return mGutterLineNumbersStartZero;
}

void Settings::Editor::setGutterLineNumbersStartZero(bool gutterLineNumbersStartZero)
{
    mGutterLineNumbersStartZero = gutterLineNumbersStartZero;
}

bool Settings::Editor::gutterAddLeadingZero() const
{
    return mGutterAddLeadingZero;
}

void Settings::Editor::setGutterAddLeadingZero(bool gutterAddLeadingZero)
{
    mGutterAddLeadingZero = gutterAddLeadingZero;
}

bool Settings::Editor::gutterShowLineNumbers() const
{
    return mGutterShowLineNumbers;
}

void Settings::Editor::setGutterShowLineNumbers(bool gutterShowLineNumbers)
{
    mGutterShowLineNumbers = gutterShowLineNumbers;
}

int Settings::Editor::gutterDigitsCount() const
{
    return mGutterDigitsCount;
}

void Settings::Editor::setGutterDigitsCount(int gutterDigitsCount)
{
    mGutterDigitsCount = gutterDigitsCount;
}

bool Settings::Editor::gutterAutoSize() const
{
    return mGutterAutoSize;
}

void Settings::Editor::setGutterAutoSize(bool gutterAutoSize)
{
    mGutterAutoSize = gutterAutoSize;
}

bool Settings::Editor::gutterVisible() const
{
    return mGutterVisible;
}

void Settings::Editor::setGutterVisible(bool gutterVisible)
{
    mGutterVisible = gutterVisible;
}

bool Settings::Editor::fontOnlyMonospaced() const
{
    return mFontOnlyMonospaced;
}

void Settings::Editor::setFontOnlyMonospaced(bool fontOnlyMonospaced)
{
    mFontOnlyMonospaced = fontOnlyMonospaced;
}

int Settings::Editor::fontSize() const
{
    return mFontSize;
}

void Settings::Editor::setFontSize(int fontSize)
{
    mFontSize = fontSize;
}

QString Settings::Editor::fontName() const
{
    return mFontName;
}

void Settings::Editor::setFontName(const QString &fontName)
{
    mFontName = fontName;
}

bool Settings::Editor::scrollByOneLess() const
{
    return mScrollByOneLess;
}

void Settings::Editor::setScrollByOneLess(bool scrollByOneLess)
{
    mScrollByOneLess = scrollByOneLess;
}

bool Settings::Editor::scrollPastEol() const
{
    return mScrollPastEol;
}

void Settings::Editor::setScrollPastEol(bool scrollPastEol)
{
    mScrollPastEol = scrollPastEol;
}

bool Settings::Editor::scrollPastEof() const
{
    return mScrollPastEof;
}

void Settings::Editor::setScrollPastEof(bool scrollPastEof)
{
    mScrollPastEof = scrollPastEof;
}

bool Settings::Editor::autoHideScrollbar() const
{
    return mAutoHideScrollbar;
}

void Settings::Editor::setAutoHideScrollbar(bool autoHideScrollbar)
{
    mAutoHideScrollbar = autoHideScrollbar;
}

void Settings::Editor::doSave()
{
    saveValue("default_encoding",mDefaultEncoding);
    // indents
    saveValue("auto_indent", mAutoIndent);
    saveValue("add_indent", mAddIndent);
    saveValue("tab_to_spaces", mTabToSpaces);
    saveValue("tab_width", mTabWidth);
    saveValue("show_indent_lines", mShowIndentLines);
    saveValue("indent_line_color",mIndentLineColor);
    // caret
    saveValue("enhance_home_key",mEnhanceHomeKey);
    saveValue("enhance_end_key",mEnhanceEndKey);
    saveValue("keep_caret_x",mKeepCaretX);
    saveValue("caret_for_insert",static_cast<int>(mCaretForInsert));
    saveValue("caret_for_overwrite",static_cast<int>(mCaretForOverwrite));
    saveValue("caret_color",mCaretColor);

    //scroll
    saveValue("auto_hide_scroll_bar", mAutoHideScrollbar);
    saveValue("scroll_past_eof", mScrollPastEof);
    saveValue("scroll_past_eol", mScrollPastEol);
    saveValue("scroll_by_one_less", mScrollByOneLess);
    saveValue("half_page_scroll", mHalfPageScroll);

    //Font
    //font
    saveValue("font_name",mFontName);
    saveValue("font_size", mFontSize);
    saveValue("font_only_monospaced",mFontOnlyMonospaced);

    //gutter
    saveValue("gutter_visible", mGutterVisible);
    saveValue("gutter_auto_size", mGutterAutoSize);
    saveValue("gutter_digits_count", mGutterDigitsCount);
    saveValue("gutter_show_line_numbers",mGutterShowLineNumbers);
    saveValue("gutter_add_leading_zero",mGutterAddLeadingZero);
    saveValue("gutter_line_numbers_start_zero",mGutterLineNumbersStartZero);
    saveValue("gutter_use_custom_font",mGutterUseCustomFont);
    saveValue("gutter_font_name",mGutterFontName);
    saveValue("gutter_font_size",mGutterFontSize);
    saveValue("gutter_font_only_monospaced",mGutterFontOnlyMonospaced);

    //copy
    saveValue("copy_limit",mCopySizeLimit);
    saveValue("copy_char_limits",mCopyCharLimits);
    saveValue("copy_line_limits",mCopyLineLimits);
    saveValue("copy_with_format_as",mCopyWithFormatAs);
    saveValue("copy_rtf_use_background",mCopyRTFUseBackground);
    saveValue("copy_rtf_use_editor_color_scheme",mCopyRTFUseEditorColor);
    saveValue("copy_rtf_color_scheme",mCopyRTFColorScheme);
    saveValue("copy_html_use_background",mCopyHTMLUseBackground);
    saveValue("copy_html_use_editor_color_scheme",mCopyHTMLUseEditorColor);
    saveValue("copy_html_color_scheme", mCopyHTMLColorScheme);

    //color scheme
    saveValue("color_scheme", mColorScheme);

    //Symbol Completion
    saveValue("complete_symbols", mCompleteSymbols);
    saveValue("complete_parenthese", mCompleteParenthese);
    saveValue("complete_bracket", mCompleteBracket);
    saveValue("complete_brace", mCompleteBrace);
    saveValue("complete_comment", mCompleteComment);
    saveValue("complete_single_quote", mCompleteSingleQuote);
    saveValue("complete_double_quote", mCompleteDoubleQuote);
    saveValue("complete_global_include", mCompleteGlobalInclude);
    saveValue("overwrite_symbols", mOverwriteSymbols);
    saveValue("remove_symbol_pairs",mRemoveSymbolPairs);

    //Auto Syntax Check
    saveValue("check_syntax",mSyntaxCheck);
    saveValue("check_syntax_when_save",mSyntaxCheckWhenSave);
    saveValue("check_syntax_when_line_changed",mSyntaxCheckWhenLineChanged);
}

void Settings::Editor::doLoad()
{
    mDefaultEncoding = value("default_encoding", ENCODING_SYSTEM_DEFAULT).toByteArray();
    // indents
    mAutoIndent = boolValue("auto_indent", true);
    mAddIndent = boolValue("add_indent", true);
    mTabToSpaces = boolValue("tab_to_spaces",false);
    mTabWidth = intValue("tab_width",4);
    mShowIndentLines = boolValue("show_indent_lines",true);
    mIndentLineColor = colorValue("indent_line_color",QColorConstants::Svg::silver);
    // caret
    mEnhanceHomeKey = boolValue("enhance_home_key", true);
    mEnhanceEndKey = boolValue("enhance_end_key",true);
    mKeepCaretX = boolValue("keep_caret_x",true);
    mCaretForInsert = static_cast<SynEditCaretType>( intValue("caret_for_insert",static_cast<int>(SynEditCaretType::ctVerticalLine)));
    mCaretForOverwrite = static_cast<SynEditCaretType>( intValue("caret_for_overwrite",static_cast<int>(SynEditCaretType::ctBlock)));
    mCaretColor = colorValue("caret_color",QColorConstants::Svg::black);

    //scroll
    mAutoHideScrollbar = boolValue("auto_hide_scroll_bar", false);
    mScrollPastEof = boolValue("scroll_past_eof", true);
    mScrollPastEol = boolValue("scroll_past_eol", true);
    mScrollByOneLess = boolValue("scroll_by_one_less", false);
    mHalfPageScroll = boolValue("half_page_scroll",false);

    //Font
    //font
    mFontName = stringValue("font_name","consolas");
    mFontSize = intValue("font_size",QGuiApplication::font().pointSize());
    mFontOnlyMonospaced = boolValue("font_only_monospaced",true);

    //gutter
    mGutterVisible = boolValue("gutter_visible",true);
    mGutterAutoSize = boolValue("gutter_auto_size",true);
    mGutterLeftOffset = intValue("gutter_left_offset",28);
    mGutterRightOffset = intValue("gutter_right_offset",24);
    mGutterDigitsCount = intValue("gutter_digits_count",1);
    mGutterShowLineNumbers = boolValue("gutter_show_line_numbers",true);
    mGutterAddLeadingZero = boolValue("gutter_add_leading_zero",true);
    mGutterLineNumbersStartZero = boolValue("gutter_line_numbers_start_zero",false);
    mGutterUseCustomFont = boolValue("gutter_use_custom_font",false);
    mGutterFontName = stringValue("gutter_font_name","consolas");
    mGutterFontSize = intValue("gutter_font_size",QGuiApplication::font().pointSize());
    mGutterFontOnlyMonospaced = boolValue("gutter_font_only_monospaced",true);

    //copy
    mCopySizeLimit = boolValue("copy_limit",true);
    mCopyCharLimits = intValue("copy_char_limits",100);
    mCopyLineLimits = intValue("copy_line_limits",100000);
    mCopyWithFormatAs = intValue("copy_with_format_as",0);
    mCopyRTFUseBackground = boolValue("copy_rtf_use_background",false);
    mCopyRTFUseEditorColor = boolValue("copy_rtf_use_editor_color_scheme",true);
    mCopyRTFColorScheme = stringValue("copy_rtf_color_scheme","Intellij Classic");
    mCopyHTMLUseBackground = boolValue("copy_html_use_background",false);
    mCopyHTMLUseEditorColor = boolValue("copy_html_use_editor_color_scheme",true);
    mCopyHTMLColorScheme = stringValue("copy_html_color_scheme","Intellij Classic");

    //color
    mColorScheme = stringValue("color_scheme", "VS Code");

    //Symbol Completion
    mCompleteSymbols = boolValue("complete_symbols",true);
    mCompleteParenthese = boolValue("complete_parenthese",true);
    mCompleteBracket = boolValue("complete_bracket",true);
    mCompleteBrace = boolValue("complete_brace",true);
    mCompleteComment = boolValue("complete_comment",true);
    mCompleteSingleQuote = boolValue("complete_single_quote",true);
    mCompleteDoubleQuote = boolValue("complete_double_quote",true);
    mCompleteGlobalInclude = boolValue("complete_global_include",true);
    mOverwriteSymbols = boolValue("overwrite_symbols",true);
    mRemoveSymbolPairs = boolValue("remove_symbol_pairs",true);

    //Auto Syntax Check
    mSyntaxCheck = boolValue("check_syntax",true);
    mSyntaxCheckWhenSave = boolValue("check_syntax_when_save",true);
    mSyntaxCheckWhenLineChanged = boolValue("check_syntax_when_line_changed",true);
}

SynEditCaretType Settings::Editor::caretForOverwrite() const
{
    return mCaretForOverwrite;
}

void Settings::Editor::setCaretForOverwrite(const SynEditCaretType &caretForOverwrite)
{
    mCaretForOverwrite = caretForOverwrite;
}

SynEditCaretType Settings::Editor::caretForInsert() const
{
    return mCaretForInsert;
}

void Settings::Editor::setCaretForInsert(const SynEditCaretType &caretForInsert)
{
    mCaretForInsert = caretForInsert;
}

bool Settings::Editor::enhanceEndKey() const
{
    return mEnhanceEndKey;
}

void Settings::Editor::setEnhanceEndKey(bool enhanceEndKey)
{
    mEnhanceEndKey = enhanceEndKey;
}

bool Settings::Editor::enhanceHomeKey() const
{
    return mEnhanceHomeKey;
}

void Settings::Editor::setEnhanceHomeKey(bool enhanceHomeKey)
{
    mEnhanceHomeKey = enhanceHomeKey;
}

QColor Settings::Editor::indentLineColor() const
{
    return mIndentLineColor;
}

void Settings::Editor::setIndentLineColor(const QColor &indentLineColor)
{
    mIndentLineColor = indentLineColor;
}

bool Settings::Editor::showIndentLines() const
{
    return mShowIndentLines;
}

void Settings::Editor::setShowIndentLines(bool showIndentLines)
{
    mShowIndentLines = showIndentLines;
}

int Settings::Editor::tabWidth() const
{
    return mTabWidth;
}

void Settings::Editor::setTabWidth(int tabWidth)
{
    mTabWidth = tabWidth;
}

bool Settings::Editor::tabToSpaces() const
{
    return mTabToSpaces;
}

void Settings::Editor::setTabToSpaces(bool tabToSpaces)
{
    mTabToSpaces = tabToSpaces;
}

bool Settings::Editor::addIndent() const
{
    return mAddIndent;
}

void Settings::Editor::setAddIndent(bool addIndent)
{
    mAddIndent = addIndent;
}

Settings::CompilerSet::CompilerSet(const QString& compilerFolder):
    mAutoAddCharsetParams(true)
{
    if (!compilerFolder.isEmpty()) {
        setProperties(compilerFolder+"/bin");

        //manually set the directories
        setDirectories(compilerFolder);

        setExecutables();

        setUserInput();

        setDefines();
    }
    setOptions();
}

Settings::CompilerSet::CompilerSet(const Settings::CompilerSet &set):
    mCCompiler(set.mCCompiler),
    mCppCompiler(set.mCppCompiler),
    mMake(set.mMake),
    mDebugger(set.mDebugger),
    mProfiler(set.mProfiler),
    mResourceCompiler(set.mResourceCompiler),
    mBinDirs(set.mBinDirs),
    mCIncludeDirs(set.mCIncludeDirs),
    mCppIncludeDirs(set.mCppIncludeDirs),
    mLibDirs(set.mLibDirs),
    mDumpMachine(set.mDumpMachine),
    mVersion(set.mVersion),
    mType(set.mType),
    mName(set.mName),
    mDefines(set.mDefines),
    mTarget(set.mTarget),
    mUseCustomCompileParams(set.mUseCustomCompileParams),
    mUseCustomLinkParams(set.mUseCustomLinkParams),
    mCustomCompileParams(set.mCustomCompileParams),
    mCustomLinkParams(set.mCustomLinkParams),
    mAutoAddCharsetParams(set.mAutoAddCharsetParams)
{
    // Executables, most are hardcoded
    for (PCompilerOption pOption:set.mOptions) {
        PCompilerOption p=std::make_shared<CompilerOption>();
        *p=*pOption;
        mOptions.push_back(pOption);
    }
}

void Settings::CompilerSet::addOption(const QString &name, const QString section, bool isC,
    bool isCpp, bool isLinker, int value, const QString &setting, const QStringList &choices)
{
    PCompilerOption pOption = std::make_shared<CompilerOption>();
    pOption->name = name;
    pOption->section = section;
    pOption->isC = isC;
    pOption->isCpp = isCpp;
    pOption->isLinker = isLinker;
    pOption->value = value;
    pOption->setting= setting;
    pOption->choices = choices;
    mOptions.push_back(pOption);
}

PCompilerOption Settings::CompilerSet::findOption(const QString &setting)
{
    for (PCompilerOption pOption : mOptions) {
        if (pOption->setting == setting) {
            return pOption;
        }
    }
    return PCompilerOption();
}

char Settings::CompilerSet::getOptionValue(const QString &setting)
{
    PCompilerOption pOption = findOption(setting);
    if (pOption) {
        return ValueToChar[pOption->value];
    } else {
        return '0';
    }
}

void Settings::CompilerSet::setOption(const QString &setting, char valueChar)
{
    PCompilerOption pOption = findOption(setting);
    if (pOption) {
        setOption(pOption,valueChar);
    }
}

void Settings::CompilerSet::setOption(PCompilerOption &option, char valueChar)
{
    option->value = charToValue(valueChar);
}

static void checkDirs(const QStringList& dirlist, QString& gooddirs, QString& baddirs) {
    gooddirs = "";
    baddirs = "";

    for (int i=0; i<dirlist.count();i++) {
        QDir dir(dirlist[i]);
        if (!dir.exists()) {
            if (baddirs.isEmpty()) {
                baddirs = dirlist[i];
            } else {
                baddirs += ";" + dirlist[i];
            }
        } else {
            if (gooddirs.isEmpty()) {
                gooddirs = dirlist[i];
            } else {
                gooddirs += ";" + dirlist[i];
            }
        }
    }
}


bool Settings::CompilerSet::dirsValid(QString &msg)
{
    QString goodbin, badbin, goodlib, badlib, goodinc, badinc, goodinccpp, badinccpp;
    msg = "";

    if (mBinDirs.count()>0) {// we need some bin dir, so treat count=0 as an error too
        checkDirs(mBinDirs,goodbin,badbin);
        if (!badbin.isEmpty()) {
            msg += QObject::tr("The following %1 directories don't exist:").arg(
                        QObject::tr("binary")
                        );
            msg += "<br />";
            msg += badbin.replace(';',"<br />");
            msg += "<br />";
            msg += "<br />";
            return false;
        }
    } else {
        msg += QObject::tr("No %1 directories have been specified.").arg(
                    QObject::tr("binary")
                    );
        msg += "<br />";
        msg += "<br />";
        return false;
    }
    checkDirs(mCIncludeDirs,goodbin,badbin);
    if (!badbin.isEmpty()) {
        msg += QObject::tr("The following %1 directories don't exist:").arg(
                    QObject::tr("C include")
                    );
        msg += "<br />";
        msg += badbin.replace(';',"<br />");
        msg += "<br />";
        msg += "<br />";
        return false;
    }

    checkDirs(mCppIncludeDirs,goodbin,badbin);
    if (!badbin.isEmpty()) {
        msg += QObject::tr("The following %1 directories don't exist:").arg(
                    QObject::tr("C++ include")
                    );
        msg += "<br />";
        msg += badbin.replace(';',"<br />");
        msg += "<br />";
        msg += "<br />";
        return false;
    }

    checkDirs(mLibDirs,goodbin,badbin);
    if (!badbin.isEmpty()) {
        msg += QObject::tr("The following %1 directories don't exist:").arg(
                    QObject::tr("C++ include")
                    );
        msg += "<br />";
        msg += badbin.replace(';',"<br />");
        msg += "<br />";
        msg += "<br />";
        return false;
    }

    if (!msg.isEmpty())
        return false;
    else
        return true;
}

bool Settings::CompilerSet::validateExes(QString &msg)
{
    msg ="";
    if (!QFile(mCCompiler).exists()) {
        msg += QObject::tr("Cannot find the %1 \"%2\"")
                .arg("C Compiler")
                .arg(mCCompiler);
    }
    if (!QFile(mCppCompiler).exists()) {
        msg += QObject::tr("Cannot find the %1 \"%2\"")
                .arg("C++ Compiler")
                .arg(mCppCompiler);
    }
    if (!QFile(mMake).exists()) {
        msg += QObject::tr("Cannot find the %1 \"%2\"")
                .arg("Maker")
                .arg(mMake);
    }
    if (!QFile(mDebugger).exists()) {
        msg += QObject::tr("Cannot find the %1 \"%2\"")
                .arg("Maker")
                .arg(mDebugger);
    }
    if (!msg.isEmpty())
        return false;
    else
        return true;
}

const QString &Settings::CompilerSet::CCompiler() const
{
    return mCCompiler;
}

void Settings::CompilerSet::setCCompiler(const QString &name)
{
    mCCompiler = name;
}

const QString &Settings::CompilerSet::cppCompiler() const
{
    return mCppCompiler;
}

void Settings::CompilerSet::setCppCompiler(const QString &name)
{
    mCppCompiler = name;
}

const QString &Settings::CompilerSet::make() const
{
    return mMake;
}

void Settings::CompilerSet::setMake(const QString &name)
{
    mMake = name;
}

const QString &Settings::CompilerSet::debugger() const
{
    return mDebugger;
}

void Settings::CompilerSet::setDebugger(const QString &name)
{
    mDebugger = name;
}

const QString &Settings::CompilerSet::profiler() const
{
    return mProfiler;
}

void Settings::CompilerSet::setProfiler(const QString &name)
{
    mProfiler = name;
}

const QString &Settings::CompilerSet::resourceCompiler() const
{
    return mResourceCompiler;
}

void Settings::CompilerSet::setResourceCompiler(const QString &name)
{
    mResourceCompiler = name;
}

QStringList &Settings::CompilerSet::binDirs()
{
    return mBinDirs;
}

QStringList &Settings::CompilerSet::CIncludeDirs()
{
    return mCIncludeDirs;
}

QStringList &Settings::CompilerSet::CppIncludeDirs()
{
    return mCppIncludeDirs;
}

QStringList &Settings::CompilerSet::libDirs()
{
    return mLibDirs;
}

const QString &Settings::CompilerSet::dumpMachine()
{
    return mDumpMachine;
}

void Settings::CompilerSet::setDumpMachine(const QString &value)
{
    mDumpMachine = value;
}

const QString &Settings::CompilerSet::version()
{
    return mVersion;
}

void Settings::CompilerSet::setVersion(const QString &value)
{
    mVersion = value;
}

const QString &Settings::CompilerSet::type()
{
    return mType;
}

void Settings::CompilerSet::setType(const QString& value)
{
    mType = value;
}

const QString &Settings::CompilerSet::name()
{
    return mName;
}

void Settings::CompilerSet::setName(const QString &value)
{
    mName = value;
}

QStringList& Settings::CompilerSet::defines()
{
    return mDefines;
}

const QString &Settings::CompilerSet::target()
{
    return mTarget;
}

void Settings::CompilerSet::setTarget(const QString &value)
{
    mTarget = value;
}

void Settings::CompilerSet::setUseCustomCompileParams(bool value)
{
    mUseCustomCompileParams = value;
}

bool Settings::CompilerSet::useCustomLinkParams()
{
    return mUseCustomLinkParams;
}

void Settings::CompilerSet::setUseCustomLinkParams(bool value)
{
    mUseCustomLinkParams = value;
}

const QString &Settings::CompilerSet::customCompileParams()
{
    return mCustomCompileParams;
}

void Settings::CompilerSet::setCustomCompileParams(const QString &value)
{
    mCustomCompileParams = value;
}

const QString &Settings::CompilerSet::customLinkParams()
{
    return mCustomLinkParams;
}

void Settings::CompilerSet::setCustomLinkParams(const QString &value)
{
    mCustomLinkParams = value;
}

bool Settings::CompilerSet::autoAddCharsetParams()
{
    return mAutoAddCharsetParams;
}

void Settings::CompilerSet::setAutoAddCharsetParams(bool value)
{
    mAutoAddCharsetParams = value;
}

CompilerOptionList &Settings::CompilerSet::options()
{
    return mOptions;
}

int Settings::CompilerSet::charToValue(char valueChar)
{
    if (valueChar == '1') {
        return 1;
    } else if ( (valueChar>='a') && (valueChar<='z')) {
        return (valueChar-'a')+2;
    } else {
        return 0;
    }
}

static void addExistingDirectory(QStringList& dirs, const QString& directory) {
    if (!directoryExists(directory))
        return;
    QFileInfo dirInfo(directory);
    QString dirPath = dirInfo.absoluteFilePath();
    if (dirs.contains(dirPath))
        return;
    dirs.append(dirPath);
}

void Settings::CompilerSet::setProperties(const QString &binDir)
{
    if (!fileExists(binDir,GCC_PROGRAM))
        return;
    // Obtain version number and compiler distro etc
    QStringList arguments;
    arguments.append("-v");
    QByteArray output = getCompilerOutput(binDir,GCC_PROGRAM,arguments);

    //Target
    QByteArray targetStr = "Target: ";
    int delimPos1 = output.indexOf(targetStr);
    if (delimPos1<0)
        return; // unknown binary
    delimPos1+=strlen(targetStr);
    int delimPos2 = delimPos1;
    while (delimPos2<output.length() && !isNonPrintableAsciiChar(output[delimPos2]))
        delimPos2++;
    mTarget = output.mid(delimPos1,delimPos2-delimPos1);

    if (mTarget.contains("x86_64"))
        mTarget = "x86_64";
    else
        mTarget = "i686";

    //Find version number
    targetStr = "gcc version ";
    delimPos1 = output.indexOf(targetStr);
    if (delimPos1<0)
        return; // unknown binary
    delimPos1+=strlen(targetStr);
    delimPos2 = delimPos1;
    while (delimPos2<output.length() && !isNonPrintableAsciiChar(output[delimPos2]))
        delimPos2++;
    mVersion = output.mid(delimPos1,delimPos2-delimPos1);

    // Find compiler builder
    delimPos1 = delimPos2;
    while ((delimPos1 < output.length()) && !(output[delimPos1] == '('))
        delimPos1++;
    while ((delimPos2 < output.length()) && !(output[delimPos2] == ')'))
        delimPos2++;
    mType = output.mid(delimPos1 + 1, delimPos2 - delimPos1 - 1);

    // Assemble user friendly name if we don't have one yet
    if (mName == "") {
        if (mType.contains("tdm64")) {
            mName = "TDM-GCC " + mVersion;
        } else if (mType.contains("tdm")) {
            mName = "TDM-GCC " + mVersion;
        } else if (mType.contains("MSYS2")) {
            mName = "MinGW-w64 GCC " + mVersion;
        } else if (mType.contains("GCC")) {
            mName = "MinGW GCC " + mVersion;
        } else {
            mName = "MinGW GCC " + mVersion;
        }
    }

    // Set compiler folder
    QDir tmpDir(binDir);
    tmpDir.cdUp();
    QString folder = tmpDir.path();

    // Obtain compiler target
    arguments.clear();
    arguments.append("-dumpmachine");
    mDumpMachine = getCompilerOutput(binDir, GCC_PROGRAM, arguments);

    // Add the default directories
    addExistingDirectory(mBinDirs, includeTrailingPathDelimiter(folder) +  "bin");
    addExistingDirectory(mLibDirs, includeTrailingPathDelimiter(folder) + "lib");
    addExistingDirectory(mCIncludeDirs, includeTrailingPathDelimiter(folder) + "include");
    addExistingDirectory(mCppIncludeDirs, includeTrailingPathDelimiter(folder) + "include");

    // Find default directories
    // C include dirs
    arguments.clear();
    arguments.append("-xc");
    arguments.append("-v");
    arguments.append("-E");
    arguments.append(NULL_FILE);
    output = getCompilerOutput(binDir,GCC_PROGRAM,arguments);


    delimPos1 = output.indexOf("#include <...> search starts here:");
    delimPos2 = output.indexOf("End of search list.");
    if (delimPos1 >0 && delimPos2>0 ) {
        delimPos1 += QByteArray("#include <...> search starts here:").length();
        QList<QByteArray> lines = output.mid(delimPos1, delimPos2-delimPos1).split('\n');
        for (QByteArray& line:lines) {
            QByteArray trimmedLine = line.trimmed();
            if (!trimmedLine.isEmpty()) {
                addExistingDirectory(mCIncludeDirs,trimmedLine);
            }
        }
    }

    // Find default directories
    // C++ include dirs
    arguments.clear();
    arguments.append("-xc++");
    arguments.append("-E");
    arguments.append("-v");
    arguments.append(NULL_FILE);
    output = getCompilerOutput(binDir,GCC_PROGRAM,arguments);
    //gcc -xc++ -E -v NUL

    delimPos1 = output.indexOf("#include <...> search starts here:");
    delimPos2 = output.indexOf("End of search list.");
    if (delimPos1 >0 && delimPos2>0 ) {
        delimPos1 += QByteArray("#include <...> search starts here:").length();
        QList<QByteArray> lines = output.mid(delimPos1, delimPos2-delimPos1).split('\n');
        for (QByteArray& line:lines) {
            QByteArray trimmedLine = line.trimmed();
            if (!trimmedLine.isEmpty()) {
                addExistingDirectory(mCppIncludeDirs,trimmedLine);
            }
        }
    }

    // Find default directories
    arguments.clear();
    arguments.append("-print-search-dirs");
    arguments.append(NULL_FILE);
    output = getCompilerOutput(binDir,GCC_PROGRAM,arguments);
    // bin dirs
    targetStr = QByteArray("programs: =");
    delimPos1 = output.indexOf(targetStr);
    if (delimPos1>=0) {
        delimPos1+=targetStr.length();
        delimPos2 = delimPos1;
        while (delimPos2 < output.length() && output[delimPos2]!='\n')
            delimPos2+=1;
        QList<QByteArray> lines = output.mid(delimPos1,delimPos2-delimPos1).split(';');
        for (QByteArray& line:lines) {
            QByteArray trimmedLine = line.trimmed();
            if (!trimmedLine.isEmpty())
                addExistingDirectory(mBinDirs,trimmedLine);
        }
    }
    // lib dirs
    targetStr = QByteArray("libraries: =");
    delimPos1 = output.indexOf(targetStr);
    if (delimPos1>=0) {
        delimPos1+=targetStr.length();
        delimPos2 = delimPos1;
        while (delimPos2 < output.length() && output[delimPos2]!='\n')
            delimPos2+=1;
        QList<QByteArray> lines = output.mid(delimPos1,delimPos2-delimPos1).split(';');
        for (QByteArray& line:lines) {
            QByteArray trimmedLine = line.trimmed();
            if (!trimmedLine.isEmpty())
                addExistingDirectory(mLibDirs,trimmedLine);
        }
    }
}

void Settings::CompilerSet::setDefines() {
    // get default defines
    QStringList arguments;
    arguments.append("-dM");
    arguments.append("-E");
    arguments.append("-x");
    arguments.append("c++");
    arguments.append("-std=c++17");
    arguments.append(NULL_FILE);
    QFileInfo ccompiler(mCCompiler);
    QByteArray output = getCompilerOutput(ccompiler.absolutePath(),ccompiler.baseName(),arguments);
    // 'cpp.exe -dM -E -x c++ -std=c++17 NUL'

    mDefines.clear();
    QList<QByteArray> lines = output.split('\n');
    for (QByteArray& line:lines) {
        QByteArray trimmedLine = line.trimmed();
        if (!trimmedLine.isEmpty()) {
            mDefines.append(trimmedLine);
        }
    }
}

void Settings::CompilerSet::setExecutables()
{
    mCCompiler = findProgramInBinDirs(GCC_PROGRAM);
    mCppCompiler = findProgramInBinDirs(GPP_PROGRAM);
    mDebugger = findProgramInBinDirs(GDB_PROGRAM);
    mMake = findProgramInBinDirs(MAKE_PROGRAM);
    mResourceCompiler = findProgramInBinDirs(WINDRES_PROGRAM);
    mProfiler = findProgramInBinDirs(GPROF_PROGRAM);
}

void Settings::CompilerSet::setDirectories(const QString& folder)
{
    // Try to obtain our target/autoconf folder
    if (!mDumpMachine.isEmpty()) {
        //mingw-w64 bin folder
        addExistingDirectory(mBinDirs,
            includeTrailingPathDelimiter(folder) + "lib/"
            "gcc/" + mDumpMachine
            + "/" + mVersion);

        // Regular include folder
        addExistingDirectory(mCIncludeDirs, includeTrailingPathDelimiter(folder) + mDumpMachine + "/include");
        addExistingDirectory(mCppIncludeDirs, includeTrailingPathDelimiter(folder)+ mDumpMachine + "/include");

        // Other include folder?
        addExistingDirectory(mCIncludeDirs,
            includeTrailingPathDelimiter(folder) + "lib/gcc/"
            + mDumpMachine + "/" + mVersion + "/include");
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder) + "lib/gcc/"
            + mDumpMachine + "/" + mVersion + "/include");

        addExistingDirectory(mCIncludeDirs,
            includeTrailingPathDelimiter(folder) + "lib/gcc/"
             + mDumpMachine + "/" + mVersion + "/include-fixed");
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder) + "lib/gcc/"
                + mDumpMachine + "/" + mVersion + "/include-fixed");

        // C++ only folder (mingw.org)
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder)  + "lib/gcc/"
                + mDumpMachine + "/" + mVersion + "/include/c++");
        addExistingDirectory(mCppIncludeDirs,
             includeTrailingPathDelimiter(folder)  + "lib/gcc/"
                 + mDumpMachine + "/" + mVersion + "/include/c++/"
                 + mDumpMachine);
        addExistingDirectory(mCppIncludeDirs,
             includeTrailingPathDelimiter(folder)  + "lib/gcc/"
                 + mDumpMachine + "/" + mVersion + "/include/c++/backward");

        // C++ only folder (Mingw-w64)
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder)  + "include/c++/"
            + mVersion );
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder)  + "include/c++/"
            + mVersion + "/backward");
        addExistingDirectory(mCppIncludeDirs,
            includeTrailingPathDelimiter(folder)  + "include/c++/"
            + mVersion + "/" + mDumpMachine);
    }
}

void Settings::CompilerSet::setUserInput()
{
    mUseCustomCompileParams = false;
    mUseCustomLinkParams = false;
    mAutoAddCharsetParams = true;
}

inline QString tr(const char* str) {
    return QObject::tr(str);
}

void Settings::CompilerSet::setOptions()
{
    // C options
    QString groupName = QObject::tr("C options");
    addOption(tr("Support all ANSI standard C programs (-ansi)"), groupName, true, true, false, 0, "-ansi");
    addOption(tr("Do not recognize asm,inline or typeof as a keyword (-fno-asm)"), groupName, true, true, false, 0, "-fno-asm");
    addOption(tr("Imitate traditional C preprocessors (-traditional-cpp)"), groupName, true, true, false, 0, "-traditional-cpp");

    // Optimization for cpu type
    groupName = QObject::tr("Code Generation");
    QStringList sl;
    sl.append(""); // /!\ Must contain a starting empty value in order to do not have always to pass the parameter
    sl.append("This CPU=native");
    sl.append("i386=i386");
    sl.append("i486=i486");
    sl.append("i586=i586");
    sl.append("i686=i686");
    sl.append("Pentium=pentium");
    sl.append("Pentium MMX=pentium-mmx");
    sl.append("Pentium Pro=pentiumpro");
    sl.append("Pentium 2=pentium2");
    sl.append("Pentium 3=pentium3");
    sl.append("Pentium 4=pentium4");
    sl.append("Conroe=core2");
    sl.append("Nehalem=corei7");
    sl.append("Sandy=corei7-avx");
    sl.append("K6=k6");
    sl.append("K6-2=k6-2");
    sl.append("K6-3=k6-3");
    sl.append("Athlon=athlon");
    sl.append("Athlon Tbird=athlon-tbird");
    sl.append("Athlon 4=athlon-4");
    sl.append("Athlon XP=athlon-xp");
    sl.append("Athlon MP=athlon-mp");
    sl.append("K8=k8");
    sl.append("K8 Rev.E=k8-sse3");
    sl.append("K10=barcelona");
    sl.append("Bulldozer=bdver1");
    addOption(tr("Optimize for the following machine (-march)"), groupName, true, true, false, 0, "-march=", sl);
    addOption(tr("Optimize less, while maintaining full compatibility (-tune)"), groupName, true, true, false, 0, "-mtune=", sl);

    // Enable use of the specific instructions
    sl.clear();
    sl.append(""); // /!\ Must contain a starting empty value in order to do not have always to pass the parameter
    sl.append("MMX=mmx");
    sl.append("3D Now=3dnow");
    sl.append("SSE=sse");
    sl.append("SSE2=sse2");
    sl.append("SSE3=sse3");
    sl.append("SSSE3=ssse3");
    sl.append("SSE4=sse4");
    sl.append("SSE4A=sse4a");
    sl.append("SSE4.1=sse4.1");
    sl.append("SSE4.2=sse4.2");
    sl.append("AVX=avx");
    sl.append("AVX2=avx2");
    sl.append("FMA4=fma4");
    sl.append("XOP=xop");
    sl.append("AES=aes");
    addOption(tr("Enable use of specific instructions (-mx)"), groupName, true, true, false, 0, "-m", sl);

    // Optimization
    sl.clear();
    sl.append("");
    sl.append("Low=1");
    sl.append("Med=2");
    sl.append("High=3");
    sl.append("Highest (fast)=fast");
    sl.append("Size (s)=s");
    sl.append("Debug (g)=g");
    addOption(tr("Optimization level (-Ox)"), groupName, true, true, false, 0, "-O", sl);

    // 32bit/64bit
    sl.clear();
    sl.append("");
    sl.append("32bit=m32");
    sl.append("64bit=m64");
    addOption(tr("Compile with the following pointer size (-mx)"), groupName, true, true, true, 0, "-", sl);

    // Language Standards
    sl.clear();
    sl.append(""); // Passing nothing effectively lets the compiler decide
    sl.append("ISO C90=c90");
    sl.append("ISO C99=c99");
    sl.append("ISO C11=c11");
    sl.append("ISO C17=c17");
    sl.append("ISO C++=c++98");
    sl.append("ISO C++11=c++11");
    sl.append("ISO C++14=c++14");
    sl.append("ISO C++17=c++17");
    sl.append("ISO C++20=c++2a");
    sl.append("GNU C90=gnu90");
    sl.append("GNU C99=gnu99");
    sl.append("GNU C11=gnu11");
    sl.append("GNU C17=gnu17");
    sl.append("GNU C++=gnu++98");
    sl.append("GNU C++11=gnu++11");
    sl.append("GNU C++14=gnu++14");
    sl.append("GNU C++17=gnu++17");
    sl.append("GNU C++20=gnu++20");
    addOption(tr("Language standard (-std)"), groupName, true, true, false, 0, "-std=", sl);
    addOption(tr("Generate debugging information (-g3)"), groupName, true, true, false, 0, "-g3");
    addOption(tr("Generate profiling info for analysis (-pg)"), groupName, true, true, true, 0, "-pg");

    // Warnings
    groupName = tr("Warnings");
    addOption(tr("Inhibit all warning messages (-w)"), groupName, true, true, false, 0, "-w");
    addOption(tr("Show most warnings (-Wall)"), groupName, true, true, false, 0, "-Wall");
    addOption(tr("Show some more warnings (-Wextra)"), groupName, true, true, false, 0, "-Wextra");
    addOption(tr("Check ISO C/C++/C++0x conformance (-pedantic)"), groupName, true, true, false, 0, "-pedantic");
    addOption(tr("Only check the code for syntax errors (-fsyntax-only)"), groupName, true, true, false, 0, "-fsyntax-only");
    addOption(tr("Make all warnings into errors (-Werror)"), groupName, true, true, false, 0, "-Werror");
    addOption(tr("Abort compilation on first error (-Wfatal-errors)"), groupName, true, true, false, 0, "-Wfatal-errors");

    // Linker
    groupName = tr("Linker");
    addOption(tr("Link an Objective C program (-lobjc)"), groupName, false, false, true, 0, "-lobjc");
    addOption(tr("Do not use standard system libraries (-nostdlib)"), groupName, false, false, true, 0, "-nostdlib");
    addOption(tr("Do not create a console window (-mwindows)"), groupName,false, false, true, 0, "-mwindows");
    addOption(tr("Strip executable (-s)"), groupName, false, false, true, 0, "-s");
    addOption(tr("Link libraries statically (-static)"), groupName, false, false, true, 0, "-static");

    // Output
    groupName = tr("Output");
    addOption(tr("-fverbose-asm"), groupName, true, true, false, 0, "-fverbose-asm");
    addOption(tr("Use pipes instead of temporary files during compilation (-pipe)"), groupName, true, true, false, 0, "-pipe");
    addOption(tr("Do not assemble, compile and generate the assemble code (-S)"), groupName, true, true, false, 0, "-S");
}

QString Settings::CompilerSet::findProgramInBinDirs(const QString name)
{
    for (const QString& dir : mBinDirs) {
        QFileInfo f(includeTrailingPathDelimiter(dir) + name);
        if (f.exists() && f.isExecutable()) {
            return f.absoluteFilePath();
        }
    }
    return QString();
}

QByteArray Settings::CompilerSet::iniOptions() const
{
    QByteArray result;
    for (PCompilerOption p:mOptions) {
        result.append(ValueToChar[p->value]);
    }
    return result;
}

void Settings::CompilerSet::setIniOptions(const QByteArray &value)
{
   int i=0;
   for (PCompilerOption p:mOptions) {
       if (i>=value.length()) {
           break;
       }
       p->value = charToValue(value[i]);
       i++;
   }
}

QByteArray Settings::CompilerSet::getCompilerOutput(const QString &binDir, const QString &binFile, const QStringList &arguments)
{
    QByteArray result = runAndGetOutput(includeTrailingPathDelimiter(binDir)+binFile, binDir, arguments);
    return result.trimmed();
}

bool Settings::CompilerSet::useCustomCompileParams()
{
    return mUseCustomCompileParams;
}

Settings::CompilerSets::CompilerSets(Settings *settings):
    mSettings(settings),
    mDefaultIndex(-1)
{

}

Settings::PCompilerSet Settings::CompilerSets::addSet(const Settings::CompilerSet& set)
{
    PCompilerSet p=std::make_shared<CompilerSet>(set);
    mList.push_back(p);
    return p;
}

Settings::PCompilerSet Settings::CompilerSets::addSet(const QString &folder)
{
    PCompilerSet p=std::make_shared<CompilerSet>(folder);
    mList.push_back(p);
    return p;
}

static void setReleaseOptions(Settings::PCompilerSet pSet) {
    PCompilerOption pOption = pSet->findOption("-O");
    if (pOption) {
        pSet->setOption(pOption,'a');
    }

    pOption = pSet->findOption("-s");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }

    pOption = pSet->findOption("-static");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
}

static void setDebugOptions(Settings::PCompilerSet pSet) {
    PCompilerOption pOption = pSet->findOption("-g3");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
    pOption = pSet->findOption("-Wall");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
    pOption = pSet->findOption("-Wextra");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
    pOption = pSet->findOption("-static");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
}

static void setProfileOptions(Settings::PCompilerSet pSet) {
    PCompilerOption pOption = pSet->findOption("-pg");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }

    pOption = pSet->findOption("-static");
    if (pOption) {
        pSet->setOption(pOption,'1');
    }
}

void Settings::CompilerSets::addSets(const QString &folder)
{
    if (!directoryExists(folder))
        return;
    if (!fileExists(includeTrailingPathDelimiter(folder)+"bin"+QDir::separator()+GCC_PROGRAM)) {
        return;
    }
    // Default, release profile
    PCompilerSet baseSet = addSet(folder);
    QString baseName = baseSet->name();
    QString platformName;
    if (baseSet->target() == "x86_64") {
        platformName = "64-bit";
    } else {
        platformName = "32-bit";
    }
    baseSet->setName(baseName + " " + platformName + " Release");
    setReleaseOptions(baseSet);

    baseSet = addSet(folder);
    baseSet->setName(baseName + " " + platformName + " Debug");
    setDebugOptions(baseSet);

    baseSet = addSet(folder);
    baseSet->setName(baseName + " " + platformName + " Profiling");
    setProfileOptions(baseSet);

    mDefaultIndex = mList.size() - 2;
}

void Settings::CompilerSets::clearSets()
{
    for (int i=0;i<mList.size();i++) {
        mSettings->mSettings.beginGroup(QString(SETTING_COMPILTER_SET).arg(i));
        mSettings->mSettings.remove("");
        mSettings->mSettings.endGroup();
    }
    mList.clear();
    mDefaultIndex = -1;
}

void Settings::CompilerSets::findSets()
{
    clearSets();
    addSets(includeTrailingPathDelimiter(mSettings->dirs().app())+"MinGW32");
    addSets(includeTrailingPathDelimiter(mSettings->dirs().app())+"MinGW64");
}

void Settings::CompilerSets::saveSets()
{
    for (int i=0;i<mList.size();i++) {
        saveSet(i);
    }
    if (mDefaultIndex>=mList.size()) {
        mDefaultIndex = mList.size()-1;
    }
    mSettings->mSettings.beginGroup(SETTING_COMPILTER_SETS);
    mSettings->mSettings.setValue(SETTING_COMPILTER_SETS_DEFAULT_INDEX,mDefaultIndex);
    mSettings->mSettings.setValue(SETTING_COMPILTER_SETS_COUNT,mList.size());
    mSettings->mSettings.endGroup();
}

void Settings::CompilerSets::loadSets()
{
    mList.clear();
    mSettings->mSettings.beginGroup(SETTING_COMPILTER_SETS);
    mDefaultIndex =mSettings->mSettings.value(SETTING_COMPILTER_SETS_DEFAULT_INDEX,-1).toInt();
    int listSize = mSettings->mSettings.value(SETTING_COMPILTER_SETS_COUNT,0).toInt();
    mSettings->mSettings.endGroup();
    for (int i=0;i<listSize;i++) {
        PCompilerSet pSet=loadSet(i);
        mList.push_back(pSet);
    }

    PCompilerSet pCurrentSet = defaultSet();
    if (pCurrentSet) {
        QString msg;
        if (!pCurrentSet->dirsValid(msg) || !pCurrentSet->validateExes(msg)) {
            if (QMessageBox::warning(nullptr,tr("Confirm"),
                       QObject::tr("The following problems were found during validation of compiler set \"%1\":")
                                     .arg(pCurrentSet->name())
                                     +"<br /><br />"
                                     +msg
                                     +"Would you like Dev-C++ to remove them for you and add the default paths to the valid paths?<br /><br />Leaving those directories will lead to problems during compilation.<br /><br />Unless you know exactly what you're doing, it is recommended that you click Yes.",
                                     QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
                findSets();
                saveSets();
                if ( mList.size() <= mDefaultIndex)
                    mDefaultIndex =  mList.size()-1;
            } else {
                return;
            }
            pCurrentSet = defaultSet();
            if (!pCurrentSet) {
                return;
            }
            saveSet(mDefaultIndex);
            if (pCurrentSet->binDirs().count()>0) {
                pCurrentSet->setProperties(pCurrentSet->binDirs()[0]);
            }
        }
    }
}

void Settings::CompilerSets::saveDefaultIndex()
{
    mSettings->mSettings.beginGroup(SETTING_COMPILTER_SETS);
    mSettings->mSettings.setValue(SETTING_COMPILTER_SETS_DEFAULT_INDEX,mDefaultIndex);
    mSettings->mSettings.endGroup();
}

void Settings::CompilerSets::deleteSet(int index)
{
    // Erase all sections at and above from disk
    for (size_t i=index;i<mList.size();i++) {
        mSettings->mSettings.beginGroup(QString(SETTING_COMPILTER_SET).arg(i));
        mSettings->mSettings.remove("");
        mSettings->mSettings.endGroup();
    }
    mList.erase(std::begin(mList)+index);
    for (size_t i=index;i<mList.size();i++) {
        saveSet(i);
    }
    if (mDefaultIndex>=mList.size()) {
        mDefaultIndex = mList.size()-1;
    }
}

Settings::CompilerSetList &Settings::CompilerSets::list()
{
    return mList;
}

int Settings::CompilerSets::size() const
{
    return mList.size();
}

int Settings::CompilerSets::defaultIndex() const
{
    return mDefaultIndex;
}

void Settings::CompilerSets::setDefaultIndex(int value)
{
    mDefaultIndex = value;
}

Settings::PCompilerSet Settings::CompilerSets::defaultSet()
{
    if (mDefaultIndex>=0 && mDefaultIndex<mList.size()) {
        return mList[mDefaultIndex];
    }
    return PCompilerSet();
}

void Settings::CompilerSets::savePath(const QString& name, const QString& path) {
    QString s;
    QString prefix1 = excludeTrailingPathDelimiter(mSettings->mDirs.app()) + "/";
    QString prefix2 = excludeTrailingPathDelimiter(mSettings->mDirs.app()) + QDir::separator();
    if (path.startsWith(prefix1, PATH_SENSITIVITY)) {
        s = "%AppPath%/"+ path.mid(prefix1.length());
    } else if (path.startsWith(prefix2, PATH_SENSITIVITY)) {
        s = "%AppPath%/"+ path.mid(prefix2.length());
    } else {
        s= path;
    }
    mSettings->mSettings.setValue(name,s);
}

void Settings::CompilerSets::savePathList(const QString& name, const QStringList& pathList) {
    QStringList sl;
    for (const QString& path: pathList) {
        QString s;
        QString prefix1 = excludeTrailingPathDelimiter(mSettings->mDirs.app()) + "/";
        QString prefix2 = excludeTrailingPathDelimiter(mSettings->mDirs.app()) + QDir::separator();
        if (path.startsWith(prefix1, PATH_SENSITIVITY)) {
            s = "%AppPath%/"+ path.mid(prefix1.length());
        } else if (path.startsWith(prefix2, PATH_SENSITIVITY)) {
            s = "%AppPath%/" + path.mid(prefix2.length());
        } else {
            s= path;
        }
        sl.append(s);
    }
    mSettings->mSettings.setValue(name,sl);
}

void Settings::CompilerSets::saveSet(int index)
{
    PCompilerSet pSet = mList[index];
    mSettings->mSettings.beginGroup(QString(SETTING_COMPILTER_SET).arg(index));

    savePath("ccompiler", pSet->CCompiler());
    savePath("cppcompiler", pSet->cppCompiler());
    savePath("debugger", pSet->debugger());
    savePath("make", pSet->make());
    savePath("windres", pSet->resourceCompiler());
    savePath("profiler", pSet->profiler());

    // Save option string
    mSettings->mSettings.setValue("Options", pSet->iniOptions());

    // Save extra 'general' options
    mSettings->mSettings.setValue("useCustomCompileParams", pSet->useCustomCompileParams());
    mSettings->mSettings.setValue("customCompileParams", pSet->customCompileParams());
    mSettings->mSettings.setValue("useCustomLinkParams", pSet->useCustomLinkParams());
    mSettings->mSettings.setValue("customLinkParams", pSet->customLinkParams());
    mSettings->mSettings.setValue("AddCharset", pSet->autoAddCharsetParams());

    // Misc. properties
    mSettings->mSettings.setValue("DumpMachine", pSet->dumpMachine());
    mSettings->mSettings.setValue("Version", pSet->version());
    mSettings->mSettings.setValue("Type", pSet->type());
    mSettings->mSettings.setValue("Name", pSet->name());
    mSettings->mSettings.setValue("Target", pSet->target());

    // Paths
    savePathList("Bins",pSet->binDirs());
    savePathList("C",pSet->CIncludeDirs());
    savePathList("Cpp",pSet->CppIncludeDirs());
    savePathList("Libs",pSet->libDirs());

    mSettings->mSettings.endGroup();
}

QString Settings::CompilerSets::loadPath(const QString &name)
{
    QString s =  mSettings->mSettings.value(name).toString();
    QString prefix = "%AppPath%/";
    if (s.startsWith(prefix)) {
        s = includeTrailingPathDelimiter(mSettings->mDirs.app()) + s.mid(prefix.length());
    }
    return QFileInfo(s).absoluteFilePath();
}

void Settings::CompilerSets::loadPathList(const QString &name, QStringList& list)
{
    list.clear();
    QStringList sl = mSettings->mSettings.value(name).toStringList();
    QString prefix = "%AppPath%/";
    for (QString& s:sl) {
        if (s.startsWith(prefix)) {
            s = includeTrailingPathDelimiter(mSettings->mDirs.app()) + s.mid(prefix.length());
        }
        list.append(QFileInfo(s).absoluteFilePath());
    }
}

Settings::PCompilerSet Settings::CompilerSets::loadSet(int index)
{
    PCompilerSet pSet = std::make_shared<CompilerSet>();
    mSettings->mSettings.beginGroup(QString(SETTING_COMPILTER_SET).arg(index));

    pSet->setCCompiler(loadPath("ccompiler"));
    pSet->setCppCompiler(loadPath("cppcompiler"));
    pSet->setDebugger(loadPath("debugger"));
    pSet->setMake(loadPath("make"));
    pSet->setResourceCompiler(loadPath("windres"));
    pSet->setProfiler(loadPath("profiler"));

    // Save option string
    pSet->setIniOptions(mSettings->mSettings.value("Options").toByteArray());

    // Save extra 'general' options
    pSet->setUseCustomCompileParams(mSettings->mSettings.value("useCustomCompileParams").toBool());
    pSet->setCustomCompileParams(mSettings->mSettings.value("customCompileParams").toString());
    pSet->setUseCustomLinkParams(mSettings->mSettings.value("useCustomLinkParams").toBool());
    pSet->setCustomLinkParams(mSettings->mSettings.value("customLinkParams").toString());
    pSet->setAutoAddCharsetParams(mSettings->mSettings.value("AddCharset").toBool());

    pSet->setDumpMachine(mSettings->mSettings.value("DumpMachine").toString());
    pSet->setVersion(mSettings->mSettings.value("Version").toString());
    pSet->setType(mSettings->mSettings.value("Type").toString());
    pSet->setName(mSettings->mSettings.value("Name").toString());
    pSet->setTarget(mSettings->mSettings.value("Target").toString());


    // Paths
    loadPathList("Bins",pSet->binDirs());
    loadPathList("C",pSet->CIncludeDirs());
    loadPathList("Cpp",pSet->CppIncludeDirs());
    loadPathList("Libs",pSet->libDirs());

    mSettings->mSettings.endGroup();

    pSet->setDefines();
    return pSet;
}

Settings::Environment::Environment(Settings *settings):_Base(settings, SETTING_ENVIRONMENT)
{

}

void Settings::Environment::doLoad()
{
    //Appearence
    mTheme = stringValue("theme","dark");
    mInterfaceFont = stringValue("interface font","Segoe UI");
    mInterfaceFontSize = intValue("interface font size",10);
    mLanguage = stringValue("language", QLocale::system().name());
}

int Settings::Environment::interfaceFontSize() const
{
    return mInterfaceFontSize;
}

void Settings::Environment::setInterfaceFontSize(int interfaceFontSize)
{
    mInterfaceFontSize = interfaceFontSize;
}

QString Settings::Environment::language() const
{
    return mLanguage;
}

void Settings::Environment::setLanguage(const QString &language)
{
    mLanguage = language;
}

void Settings::Environment::doSave()
{
    //Appearence
    saveValue("theme", mTheme);
    saveValue("interface font", mInterfaceFont);
    saveValue("interface font size", mInterfaceFontSize);
    saveValue("language", mLanguage);
}

QString Settings::Environment::interfaceFont() const
{
    return mInterfaceFont;
}

void Settings::Environment::setInterfaceFont(const QString &interfaceFont)
{
    mInterfaceFont = interfaceFont;
}

QString Settings::Environment::theme() const
{
    return mTheme;
}

void Settings::Environment::setTheme(const QString &theme)
{
    mTheme = theme;
}

Settings::Executor::Executor(Settings *settings):_Base(settings, SETTING_EXECUTOR)
{

}

bool Settings::Executor::minimizeOnRun() const
{
    return mMinimizeOnRun;
}

void Settings::Executor::setMinimizeOnRun(bool minimizeOnRun)
{
    mMinimizeOnRun = minimizeOnRun;
}

void Settings::Executor::doSave()
{
    saveValue("pause_console", mPauseConsole);
    saveValue("minimize_on_run", mMinimizeOnRun);
}

bool Settings::Executor::pauseConsole() const
{
    return mPauseConsole;
}

void Settings::Executor::setPauseConsole(bool pauseConsole)
{
    mPauseConsole = pauseConsole;
}

void Settings::Executor::doLoad()
{
    mPauseConsole = boolValue("pause_console",true);
    mMinimizeOnRun = boolValue("minimize_on_run",false);
}


Settings::Debugger::Debugger(Settings *settings):_Base(settings, SETTING_DEBUGGER)
{

}

bool Settings::Debugger::showCommandLog() const
{
    return mShowCommandLog;
}

void Settings::Debugger::setShowCommandLog(bool showCommandLog)
{
    mShowCommandLog = showCommandLog;
}

bool Settings::Debugger::showAnnotations() const
{
    return mShowAnnotations;
}

void Settings::Debugger::setShowAnnotations(bool showAnnotations)
{
    mShowAnnotations = showAnnotations;
}

QString Settings::Debugger::fontName() const
{
    return mFontName;
}

void Settings::Debugger::setFontName(const QString &fontName)
{
    mFontName = fontName;
}

bool Settings::Debugger::blendMode() const
{
    return mBlendMode;
}

void Settings::Debugger::setBlendMode(bool blendMode)
{
    mBlendMode = blendMode;
}

bool Settings::Debugger::useIntelStyle() const
{
    return mUseIntelStyle;
}

void Settings::Debugger::setUseIntelStyle(bool useIntelStyle)
{
    mUseIntelStyle = useIntelStyle;
}

int Settings::Debugger::fontSize() const
{
    return mFontSize;
}

void Settings::Debugger::setFontSize(int fontSize)
{
    mFontSize = fontSize;
}

bool Settings::Debugger::onlyShowMono() const
{
    return mOnlyShowMono;
}

void Settings::Debugger::setOnlyShowMono(bool onlyShowMono)
{
    mOnlyShowMono = onlyShowMono;
}

void Settings::Debugger::doSave()
{
    saveValue("show_command_log", mShowCommandLog);
    saveValue("show_annotations", mShowAnnotations);
    saveValue("font_name",mFontName);
    saveValue("only_show_mono",mOnlyShowMono);
    saveValue("font_size",mFontSize);
    saveValue("use_intel_style",mUseIntelStyle);
    saveValue("blend_mode",mBlendMode);
}

void Settings::Debugger::doLoad()
{
    mShowCommandLog = boolValue("show_command_log",true);
    mShowAnnotations = boolValue("show_annotations",false);
    mFontName = stringValue("font_name","Consolas");
    mOnlyShowMono = boolValue("only_show_mono",true);
    mFontSize = intValue("font_size",10);
    mUseIntelStyle = boolValue("use_intel_style",true);
    mBlendMode = boolValue("blend_mode",true);
}

Settings::History::History(Settings *settings):_Base(settings, SETTING_HISTORY)
{

}

QStringList &Settings::History::openedFiles()
{
    return mOpenedFiles;
}

QStringList &Settings::History::openedProjects()
{
    return mOpenedProjects;
}

bool Settings::History::addToOpenedFiles(const QString &filename)
{
    if (!QFile(filename).exists())
        return false;
    int index = mOpenedFiles.indexOf(filename);
    if (index>=0) {
        mOpenedFiles.removeAt(index);
    }
    if (mOpenedFiles.size()>=15) {
        mOpenedFiles.pop_back();
    }
    mOpenedFiles.push_front(filename);
    save();
    return true;

}

void Settings::History::doSave()
{
    saveValue("opened_files", mOpenedFiles);
    saveValue("opened_projects", mOpenedProjects);
}

void Settings::History::doLoad()
{
    mOpenedFiles = stringListValue("opened_files");
    mOpenedProjects =stringListValue("opened_projects");
}