/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "GeneralOptions.hpp"
#include "Configuration/Configuration.hpp"
#include "Engines/Resources/FontManager.hpp"
#include "Engines/Resources/TextureManager.hpp"
#include "Engines/Sound/SoundEngine.hpp"

#include "GUI/Widget/Layout.hpp"
#include "Tools/ScaledPixel.hpp"
#include <cstddef>
#include <sstream>

namespace
{
    /// Display mode chooser values
    std::vector<std::string> makeDisplayChoices()
    {
        return
        {
            _(OptionsDisplayModeFullscreenBorderless),
            _(OptionsDisplayModeFullscreenNative),
            _(OptionsDisplayModeWindowed)
        };
    }

    /// Returns the chooser selection index associated with a display mode
    std::size_t getSelectionFromMode(Display::DisplayMode mode)
    {
        if (mode == Display::DisplayMode::FullscreenDesktop)
            return 0;
        else if (mode == Display::DisplayMode::Fullscreen)
            return 1;

        return 2;
    }

    /// Returns the display mode associated with a chooser selection index
    Display::DisplayMode getModeFromSelection(std::size_t selection)
    {
        if (selection == 0)
            return Display::DisplayMode::FullscreenDesktop;
        else if (selection == 1)
            return Display::DisplayMode::Fullscreen;

        return Display::DisplayMode::Windowed;
    }

    /// Extract the locale codes from \p locales in the same order
    std::vector<std::string> getLocaleCodes(
        const std::vector<I18n::Locale> &locales)
    {
        std::vector<std::string> localeCodes;
        localeCodes.reserve(locales.size());

        for (const I18n::Locale &localeData : locales)
            localeCodes.push_back(localeData.code);

        return localeCodes;
    }

    /// Extract the locale avatars and names from \p locales in the same order
    std::vector<std::pair<Sprite, std::string>> getLocaleChoices(
        const std::vector<I18n::Locale> &locales,
        LanguageFlag &languageFlag)
    {
        std::vector<std::pair<Sprite, std::string>> localeChoices;
        localeChoices.reserve(locales.size());

        for (const I18n::Locale &localeData : locales)
            localeChoices.emplace_back(
                languageFlag.getFlagSprite(localeData.code), localeData.name);

        return localeChoices;
    }

    /// Return the index of \p localeCode in \p locales
    /// Will return 0 (a valid index) on failure
    /// but that is supported by the locale chooser
    std::size_t getLocaleIndex(const std::vector<I18n::Locale> &locales,
                               const std::string &localeCode)
    {
        for (std::size_t i = 0; i < locales.size(); i++)
        {
            if (locales[i].code == localeCode)
                return i;
        }

        return 0;
    }

    /// Get the locale code in \p locales for index \p localeIx,
    /// or "en" as a fallback
    std::string getLocaleCode(const std::vector<I18n::Locale> &locales,
                              std::size_t localeIx)
    {
        return (localeIx < locales.size()) ? locales[localeIx].code : "en";
    }
}

GeneralOptions::GeneralOptions(Configuration &configuration,
                               FontManager &fontManager,
                               TextureManager &textureManager,
                               SoundEngine &soundEngine,
                               const Display::DisplayMode &activeMode,
                               float viewWidth) :
Tab(ScaledPixel{viewWidth}(1120.f),
    ScaledPixel{viewWidth}(380.f),
    Color(66, 112, 174)),
m_displayModeApplied(false), m_refreshRateUpdated(false),
m_volumeUpdated(false), m_localeUpdated(false),
m_configuration(&configuration), m_localeList(I18n::getLocales()),
m_languageFlags(textureManager, getLocaleCodes(m_localeList))
{
    const ScaledPixel sp(viewWidth);

    Font &headerFont = fontManager.getDisplay(sp(20));
    Font &labelFont = fontManager.getDefault(sp(15));

    //Dynamic camera checkbox
    m_dynamicCamera = Checkbox((configuration.getCameraMode() ==
                               FightCameraHandler::CameraMode::Dynamic),
                               sp(20.f), sp(2.f));
    m_dynamicCamera.setSoundEngine(soundEngine);
    m_dynamicCamera.setClickSound("audio/ui/menuClick.ogg");

    //Camera shake checkbox
    m_cameraShake = Checkbox(configuration.getEnableCameraShake(),
                             sp(20.f), sp(2.f));
    m_cameraShake.setSoundEngine(soundEngine);
    m_cameraShake.setClickSound("audio/ui/menuClick.ogg");

    //Show fps checkbox
    m_showFps = Checkbox(configuration.getShowFps(), sp(20.f), sp(2.f));
    m_showFps.setSoundEngine(soundEngine);
    m_showFps.setClickSound("audio/ui/menuClick.ogg");

    //Show hitboxes checkbox
    m_showHitboxes = Checkbox(configuration.getShowHitboxes(),
                              sp(20.f), sp(2.f));
    m_showHitboxes.setSoundEngine(soundEngine);
    m_showHitboxes.setClickSound("audio/ui/menuClick.ogg");

    //Display mode & Resolution chooser
    Sprite chooser_default(textureManager.getTexture("gfx/ui/ui.png",
                                                Texture::MinMagFilter::Nearest),
        Box(20.f / 512.f, 128.f / 512.f, 1.f / 512.f, 1.f / 512.f)),
    chooser_hover(textureManager.getTexture("gfx/ui/ui.png"),
        Box(0.f, 128.f / 512.f, 1.f / 512.f, 1.f / 512.f)),
    chooser_selected(textureManager.getTexture("gfx/ui/ui.png"),
        Box(0.f, 128.f / 512.f, 1.f / 512.f, 1.f / 512.f));

    SkinSound sounds(soundEngine, {}, "audio/ui/menuClick.ogg");
    SkinBox skin(sp(120.f), sp(30.f),
        chooser_default, chooser_hover, chooser_selected);
    SkinBox skinWide(sp(165.f), sp(30.f),
        chooser_default, chooser_hover, chooser_selected);

    TextButtonSkin chooserSkin(labelFont, Color(255, 255, 255),
                               skinWide, sounds);

    Display::DisplayMode mode = configuration.getDisplayLayout().mode;
    m_displayMode.setSkin(chooserSkin);
    m_displayMode.setData(makeDisplayChoices(), getSelectionFromMode(mode));

    m_resolution.setSkin(m_displayMode.getExpandedSkin());

    m_locale.setSkin(chooserSkin);
    m_locale.setIllustratedData(
        getLocaleChoices(m_localeList, m_languageFlags),
        getLocaleIndex(m_localeList, configuration.getLocale()));

    //Text shown if the display mode changes will be applied on restart
    if (mode == Display::DisplayMode::Fullscreen ||
        activeMode == Display::DisplayMode::Fullscreen)
    {
        m_fullscreenOnRestart.setFont(labelFont);
        m_fullscreenOnRestart.setText(_(OptionsDisplayAppliedAtNextRestart));
        m_fullscreenOnRestart.setColor(Color(255, 255, 255));
    }

    //Restore default windowed display button
    m_restoreDefaultDisplay = TextButton(_(OptionsDisplayRevertToDefault),
                                         skin, sounds);
    m_restoreDefaultDisplay.setTextFont(labelFont);
    m_restoreDefaultDisplay.setTextColor(Color(255, 255, 255));

    //Apply display mode button
    m_applyDisplayMode = TextButton(_(OptionsDisplayApply), skin, sounds);
    m_applyDisplayMode.setTextFont(labelFont);
    m_applyDisplayMode.setTextColor(Color(255, 255, 255));

    //Framerate & vertical sync
    m_vsync = Checkbox(configuration.getVSyncMode() != Display::VSyncMode::Off,
                       sp(20.f), sp(2.f));
    m_vsync.setSoundEngine(soundEngine);
    m_vsync.setClickSound("audio/ui/menuClick.ogg");

    ContinuousChooserSkin sliderSkin(labelFont, Color(255, 255, 255));
    sliderSkin.barSize = Vector(sp(100.f), sp(1.f));
    sliderSkin.handleSize = Vector(sp(8.f), sp(20.f));
    sliderSkin.borderSize = sp(2.f);
    sliderSkin.hitMargin = sp(10.f);
    sliderSkin.disabledHandleColor = Color(100, 100, 100);

    m_framerate = ContinuousChooser(sliderSkin, 30.f, 360.f,
                        static_cast<float>(configuration.getFrameRate()));
    m_framerate.setEnabled(!m_vsync.isChecked());

    //Volume
    m_masterVolume = ContinuousChooser(sliderSkin, 0.f, 100.f,
                        static_cast<float>(configuration.getMasterVolume()));
    m_musicVolume = ContinuousChooser(sliderSkin, 0.f, 100.f,
                        static_cast<float>(configuration.getMusicVolume()));
    m_ambientVolume = ContinuousChooser(sliderSkin, 0.f, 100.f,
                        static_cast<float>(configuration.getAmbientVolume()));
    m_effectsVolume = ContinuousChooser(sliderSkin, 0.f, 100.f,
                        static_cast<float>(configuration.getSoundVolume()));

    //Headers
    m_headerVideo = Text(_(OptionsDisplay), headerFont);
    m_headerRefresh = Text(_(OptionsRefreshRate), headerFont);
    m_headerAudio = Text(_(OptionsAudio), headerFont);
    m_headerLocale = Text(_(OptionsLocale), headerFont);
    m_headerMisc = Text(_(OptionsMiscellaneous), headerFont);

    m_headerVideo.setColor(Color(255, 255, 255));
    m_headerRefresh.setColor(Color(255, 255, 255));
    m_headerAudio.setColor(Color(255, 255, 255));
    m_headerLocale.setColor(Color(255, 255, 255));
    m_headerMisc.setColor(Color(255, 255, 255));

    //Labels
    Text labelDisplayMode(_(OptionsDisplayMode), labelFont);
    Text labelResolution(_(OptionsDisplayResolution), labelFont);

    const float displayLabelWidth =
        Layout::largest(labelDisplayMode, labelResolution) + sp(15.f);
    const float displaySectionWidth =
        displayLabelWidth + Layout::largest(m_displayMode, m_resolution);

    Text labelMasterVolume(_(OptionsAudioMaster), labelFont);
    Text labelMusicVolume(_(OptionsAudioMusic), labelFont);
    Text labelAmbientVolume(_(OptionsAudioAmbient), labelFont);
    Text labelEffectsVolume(_(OptionsAudioSoundEffects), labelFont);

    const float audioLabelWidth =
        Layout::largest(labelMasterVolume, labelMusicVolume,
                        labelAmbientVolume, labelEffectsVolume) + sp(10.f);
    const float audioSectionWidth = audioLabelWidth +
        Layout::largest(m_masterVolume, m_musicVolume,
                        m_ambientVolume, m_effectsVolume) +
                        sp(30.f); //Take the chooser label into account

    Text labelDynamicCamera(_(OptionsDynamicCamera), labelFont);
    Text labelShakingCamera(_(OptionsShakingCamera), labelFont);
    Text labelShowFrameRate(_(OptionsShowFrameRate), labelFont);
    Text labelShowHitboxes(_(OptionsShowHitboxes), labelFont);

    const float miscLabelWidth =
        Layout::largest(labelDynamicCamera, labelShakingCamera,
                        labelShowFrameRate, labelShowHitboxes) + sp(20.f);
    const float miscSectionWidth = miscLabelWidth +
        Layout::largest(m_dynamicCamera, m_cameraShake,
                        m_showFps, m_showHitboxes);

    Text labelVerticalSync(_(OptionsRefreshRateVerticalSync), labelFont);
    Text labelFramesPerSecond(_(OptionsRefreshRateFramesPerSecond), labelFont);

    const float refreshLabelWidth =
        Layout::largest(labelVerticalSync, labelFramesPerSecond) + sp(20.f);

    const float refreshSectionWidth = Layout::largest(m_vsync, m_framerate) +
        sp(30.f); //Take the chooser label into account

    Text labelLocale(_(OptionsLocaleLanguage), labelFont);

    const float localeLabelWidth = labelLocale.getWidth() + sp(20.f);

    //Append and position option items
    const float sectionSpacing = (Tab::getWidth() -
        displaySectionWidth - audioSectionWidth - miscSectionWidth) / 4.f;
    const float headerIndent = sp(10.f);

    float labelX = sectionSpacing, labelY = sp(50.f);
    m_headerVideo.setPosition(labelX - headerIndent, labelY);

    positionWidget(LabelParams(std::move(labelDisplayMode),
        displayLabelWidth, labelX, (labelY += sp(40.f))), m_displayMode);
    positionWidget(LabelParams(std::move(labelResolution),
        displayLabelWidth, labelX, (labelY += sp(32.f))), m_resolution);

    m_restoreDefaultDisplay.setPosition(labelX, (labelY += sp(45.f)));
    m_applyDisplayMode.setPosition(
        labelX + m_restoreDefaultDisplay.getWidth() + sp(10.f), labelY);
    m_fullscreenOnRestart.setPosition(labelX, labelY + sp(37.f));

    //Refresh rate
    const float vsyncWidth = labelVerticalSync.getWidth() + sp(20.f);
    const float fpsWidth = labelFramesPerSecond.getWidth() + sp(15.f);

    m_headerRefresh.setPosition(labelX - headerIndent, (labelY += sp(65.f)));

    positionWidget(LabelParams(std::move(labelVerticalSync),
        vsyncWidth, labelX, (labelY += sp(40.f))), m_vsync);
    positionWidget(LabelParams(std::move(labelFramesPerSecond),
        fpsWidth, labelX, (labelY += sp(27.f))), m_framerate);

    //Audio
    labelX = displaySectionWidth + sectionSpacing * 2.f;
    labelY = sp(50.f);
    m_headerAudio.setPosition(labelX - headerIndent, labelY);

    positionWidget(LabelParams(std::move(labelMasterVolume),
        audioLabelWidth, labelX, (labelY += sp(40.f))), m_masterVolume);
    positionWidget(LabelParams(std::move(labelMusicVolume),
        audioLabelWidth, labelX, (labelY += sp(25.f))), m_musicVolume);
    positionWidget(LabelParams(std::move(labelAmbientVolume),
        audioLabelWidth, labelX, (labelY += sp(25.f))), m_ambientVolume);
    positionWidget(LabelParams(std::move(labelEffectsVolume),
        audioLabelWidth, labelX, (labelY += sp(25.f))), m_effectsVolume);

    //Locale
    if (sectionSpacing + refreshLabelWidth + refreshSectionWidth > labelX)
        labelX = sectionSpacing + refreshLabelWidth + refreshSectionWidth;

    m_headerLocale.setPosition(labelX - headerIndent, (labelY += sp(65.f)));

    positionWidget(LabelParams(std::move(labelLocale),
        localeLabelWidth, labelX, (labelY += sp(40.f))), m_locale);

    //Misc
    labelX = displaySectionWidth + audioSectionWidth + sectionSpacing * 3.f;
    labelY = sp(50.f);
    m_headerMisc.setPosition(labelX - headerIndent, labelY);

    positionWidget(LabelParams(std::move(labelDynamicCamera),
        miscLabelWidth, labelX, (labelY += sp(40.f))), m_dynamicCamera);
    positionWidget(LabelParams(std::move(labelShakingCamera),
        miscLabelWidth, labelX, (labelY += sp(25.f))), m_cameraShake);
    positionWidget(LabelParams(std::move(labelShowFrameRate),
        miscLabelWidth, labelX, (labelY += sp(25.f))), m_showFps);
    positionWidget(LabelParams(std::move(labelShowHitboxes),
        miscLabelWidth, labelX, (labelY += sp(25.f))), m_showHitboxes);
}

void GeneralOptions::draw()
{
    //Update matrix
    Drawable::pushMatrix();
    Drawable::updateMatrix();

    m_background.draw();

    //Headers
    m_headerVideo.draw();
    m_headerRefresh.draw();
    m_headerAudio.draw();
    m_headerLocale.draw();
    m_headerMisc.draw();

    //Labels
    for (Text &label : m_labels)
        label.draw();

    m_restoreDefaultDisplay.draw();
    m_applyDisplayMode.draw();
    m_fullscreenOnRestart.draw();

    m_vsync.draw();
    m_framerate.draw();

    m_masterVolume.draw();
    m_musicVolume.draw();
    m_ambientVolume.draw();
    m_effectsVolume.draw();

    m_locale.draw();

    m_dynamicCamera.draw();
    m_cameraShake.draw();
    m_showFps.draw();
    m_showHitboxes.draw();

    //Chooser: should be rendered last
    if (m_displayMode.isOpened())
    {
        m_resolution.draw();
        m_displayMode.draw();
    }
    else
    {
        m_displayMode.draw();
        m_resolution.draw();
    }

    Drawable::popMatrix();
}

bool GeneralOptions::mouseRelease(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);

    const bool localeWasOpened = m_locale.isOpened();
    const bool displayModeWasOpened = m_displayMode.isOpened();
    const bool resolutionWasOpened = m_resolution.isOpened();

    if (m_locale.mouseRelease(localMouse) && localeWasOpened)
    {
        //Do not update other widgets if the locale chooser
        //was opened in order to avoid clicking on widgets "under" the options
        m_localeUpdated = true;
        apply();
        return true; //The choosers may exceed the bounds of the general options
    }
    //Allow only one of the resolution and the display mode
    //chooser to be opened at a time to avoid overlapping
    else if ((!resolutionWasOpened &&
              m_displayMode.mouseRelease(localMouse) && displayModeWasOpened) ||
             (!displayModeWasOpened &&
              m_resolution.mouseRelease(localMouse) && resolutionWasOpened))
    {
        apply();
        return true; //The choosers may exceed the bounds of the general options
    }


    //Audio
    m_volumeUpdated = (m_masterVolume.isFocused() ||
                       m_musicVolume.isFocused() ||
                       m_ambientVolume.isFocused() ||
                       m_effectsVolume.isFocused());

    m_masterVolume.mouseRelease(localMouse);
    m_musicVolume.mouseRelease(localMouse);
    m_ambientVolume.mouseRelease(localMouse);
    m_effectsVolume.mouseRelease(localMouse);

    if (!m_vsync.isChecked())
    {
        m_refreshRateUpdated = m_framerate.isFocused();
        m_framerate.mouseRelease(localMouse);
    }

    if (m_vsync.mouseRelease(localMouse))
    {
        m_refreshRateUpdated = true;
        m_framerate.setEnabled(!m_vsync.isChecked());
    }

    m_dynamicCamera.mouseRelease(localMouse);
    m_cameraShake.mouseRelease(localMouse);
    m_showFps.mouseRelease(localMouse);
    m_showHitboxes.mouseRelease(localMouse);

    if (m_applyDisplayMode.mouseRelease(localMouse))
    {
        apply(true);
        m_displayModeApplied = true;
        return true;
    }
    else if (m_restoreDefaultDisplay.mouseRelease(localMouse))
    {
        apply(true, true);
        m_displayModeApplied = true;
        return true;
    }

    apply();
    return Widget::contains(localMouse);
}

bool GeneralOptions::mouseClick(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);

    //Ignore click events on other widgets that may be covered by the choosers
    if (m_displayMode.mouseClick(localMouse) ||
        m_resolution.mouseClick(localMouse) ||
        m_locale.mouseClick(localMouse))
        return true; //The choosers may exceed the bounds of the general options

    m_vsync.mouseClick(localMouse);
    m_framerate.mouseClick(localMouse);
    m_masterVolume.mouseClick(localMouse);
    m_musicVolume.mouseClick(localMouse);
    m_ambientVolume.mouseClick(localMouse);
    m_effectsVolume.mouseClick(localMouse);
    m_dynamicCamera.mouseClick(localMouse);
    m_cameraShake.mouseClick(localMouse);
    m_showFps.mouseClick(localMouse);
    m_showHitboxes.mouseClick(localMouse);
    m_applyDisplayMode.mouseClick(localMouse);
    m_restoreDefaultDisplay.mouseClick(localMouse);

    return Widget::contains(localMouse);
}

bool GeneralOptions::mouseMove(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);

    //Ignore move events on other widgets that may be covered by the choosers
    if (m_displayMode.mouseMove(localMouse) ||
        m_resolution.mouseMove(localMouse))
    {
        m_applyDisplayMode.setFocus(false);
        return true; //The choosers may exceed the bounds of the general options
    }
    else if (m_locale.mouseMove(localMouse))
        return true; //The choosers may exceed the bounds of the general options

    m_vsync.mouseMove(localMouse);
    m_framerate.mouseMove(localMouse);
    m_applyDisplayMode.mouseMove(localMouse);
    m_restoreDefaultDisplay.mouseMove(localMouse);
    m_masterVolume.mouseMove(localMouse);
    m_musicVolume.mouseMove(localMouse);
    m_ambientVolume.mouseMove(localMouse);
    m_effectsVolume.mouseMove(localMouse);
    m_dynamicCamera.mouseMove(localMouse);
    m_cameraShake.mouseMove(localMouse);
    m_showFps.mouseMove(localMouse);
    m_showHitboxes.mouseMove(localMouse);

    return Widget::contains(localMouse);
}

void GeneralOptions::setDisplaySizes(
    const std::vector<std::pair<int, int>> &sizes)
{
    if (m_configuration != nullptr)
    {
        //Resolution chooser - display sizes
        const Display::DisplayLayout &layout =
            m_configuration->getDisplayLayout();

        std::vector<std::string> items;
        items.reserve(sizes.size());

        std::size_t defaultItem = 0;
        for (std::size_t i = 0; i < sizes.size(); i++)
        {
            if (sizes[i].first == layout.width &&
                sizes[i].second == layout.height)
                defaultItem = i;

            std::ostringstream itemText;
            itemText << sizes[i].first << 'x' << sizes[i].second;

            items.push_back(itemText.str());
        }

        m_resolution.setData(items, defaultItem);
    }
}

bool GeneralOptions::displayModeApplied() const
{
    if (m_displayModeApplied)
    {
        m_displayModeApplied = false;
        return true;
    }

    return false;
}

bool GeneralOptions::refreshRateUpdated() const
{
    if (m_refreshRateUpdated)
    {
        m_refreshRateUpdated = false;
        return true;
    }

    return false;
}

bool GeneralOptions::volumeUpdated() const
{
    if (m_volumeUpdated)
    {
        m_volumeUpdated = false;
        return true;
    }

    return false;
}

bool GeneralOptions::localeUpdated() const
{
    if (m_localeUpdated)
    {
        m_localeUpdated = false;
        return true;
    }

    return false;
}

void GeneralOptions::apply(bool display, bool defaultDisplay)
{
    if (m_configuration == nullptr)
        return;

    //Audio
    m_configuration->setMasterVolume(
        static_cast<int>(m_masterVolume.getValue()));
    m_configuration->setMusicVolume(
        static_cast<int>(m_musicVolume.getValue()));
    m_configuration->setAmbientVolume(
        static_cast<int>(m_ambientVolume.getValue()));
    m_configuration->setSoundVolume(
        static_cast<int>(m_effectsVolume.getValue()));

    //Locale
    m_configuration->setLocale(getLocaleCode(m_localeList,
                                             m_locale.getSelection()));

    //Misc
    m_configuration->setCameraMode(m_dynamicCamera.isChecked() ?
                                   FightCameraHandler::CameraMode::Dynamic :
                                   FightCameraHandler::CameraMode::Static);
    m_configuration->setEnableCameraShake(m_cameraShake.isChecked());
    m_configuration->setShowFps(m_showFps.isChecked());
    m_configuration->setShowHitboxes(m_showHitboxes.isChecked());

    //Frame rate and Vsync
    m_configuration->setFrameRate(static_cast<int>(m_framerate.getValue()));
    m_configuration->setVSyncMode(m_vsync.isChecked() ?
                                  Display::VSyncMode::OnAdaptive :
                                  Display::VSyncMode::Off);

    //Display
    if (display)
    {
        //Revert to default windowed display mode
        if (defaultDisplay)
            m_configuration->setDisplayLayout(Display::DisplayLayout());
        else
        {
            const std::string result = m_resolution.getSelectionStr();
            std::istringstream splitResult(result);

            std::string str_width, str_height;
            std::getline(splitResult, str_width, 'x');
            std::getline(splitResult, str_height, 'x');

            int width, height;
            std::istringstream convertWidth(str_width),
                               convertHeight(str_height);
            convertWidth >> width;
            convertHeight >> height;

            m_configuration->setDisplayLayout(Display::DisplayLayout(
                getModeFromSelection(m_displayMode.getSelection()),
                width, height));
        }
    }
}

void GeneralOptions::positionWidget(LabelParams &&label, Widget &widget)
{
    m_labels.push_back(std::move(label.text));
    Text &labelText = m_labels.back();

    labelText.setColor(Color(200, 200, 200));
    labelText.setPosition(label.xPosition, label.yPosition +
        (widget.getHeight() - labelText.getOriginHeight()) / 2.f);

    widget.setPosition(label.xPosition + label.width, label.yPosition);
}
