/***************************************************************************
 *   fheroes2: https://github.com/ihhub/fheroes2                           *
 *   Copyright (C) 2022 - 2025                                             *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "dialog_graphics_settings.h"

#include <cstdint>
#include <string>
#include <utility>

#include "agg_image.h"
#include "cursor.h"
#include "dialog_resolution.h"
#include "game_hotkeys.h"
#include "icn.h"
#include "image.h"
#include "localevent.h"
#include "math_base.h"
#include "screen.h"
#include "settings.h"
#include "translations.h"
#include "ui_button.h"
#include "ui_dialog.h"
#include "ui_option_item.h"
#include "ui_text.h"
#include "ui_window.h"

namespace
{
    enum class SelectedWindow : int
    {
        Configuration,
        Resolution,
        Mode,
        ScreenScalingType,
        VSync,
        SystemInfo,
        Exit
    };

    const fheroes2::Rect resolutionRoi{ fheroes2::threeOptionsOffsetX, fheroes2::optionsOffsetY, fheroes2::optionIconSize, fheroes2::optionIconSize };
    const fheroes2::Rect modeRoi{ fheroes2::threeOptionsOffsetX + fheroes2::threeOptionsStepX, fheroes2::optionsOffsetY, fheroes2::optionIconSize,
                                  fheroes2::optionIconSize };
    const fheroes2::Rect screenScalingTypeRoi{ fheroes2::threeOptionsOffsetX + fheroes2::threeOptionsStepX * 2, fheroes2::optionsOffsetY, fheroes2::optionIconSize,
                                               fheroes2::optionIconSize };

    const fheroes2::Rect vSyncRoi{ fheroes2::twoOptionsOffsetX, fheroes2::optionsOffsetY + fheroes2::optionsStepY, fheroes2::optionIconSize, fheroes2::optionIconSize };
    const fheroes2::Rect systemInfoRoi{ fheroes2::twoOptionsOffsetX + fheroes2::twoOptionsStepX, fheroes2::optionsOffsetY + fheroes2::optionsStepY,
                                        fheroes2::optionIconSize, fheroes2::optionIconSize };

    void drawResolution( const fheroes2::Rect & optionRoi )
    {
        const fheroes2::Display & display = fheroes2::Display::instance();
        std::string resolutionName;
        if ( display.screenSize().width != display.width() || display.screenSize().height != display.height() ) {
            const int32_t integer = display.screenSize().width / display.width();
            const int32_t fraction = display.screenSize().width * 10 / display.width() - 10 * integer;

            resolutionName = std::to_string( display.width() ) + 'x' + std::to_string( display.height() ) + " (x" + std::to_string( integer ) + '.'
                             + std::to_string( fraction ) + ')';
        }
        else {
            resolutionName = std::to_string( display.width() ) + 'x' + std::to_string( display.height() );
        }

        fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::RESOLUTION_ICON, 0 ), _( "Resolution" ), std::move( resolutionName ),
                              fheroes2::UiOptionTextWidth::THREE_ELEMENTS_ROW );
    }

    void drawMode( const fheroes2::Rect & optionRoi )
    {
        const fheroes2::Sprite & originalIcon = fheroes2::AGG::GetICN( ICN::SPANEL, Settings::Get().isEvilInterfaceEnabled() ? 17 : 16 );

        if ( fheroes2::engine().isFullScreen() ) {
            fheroes2::Sprite icon = originalIcon;
            fheroes2::Resize( originalIcon, 6, 6, 53, 53, icon, 2, 2, 61, 61 );

            fheroes2::drawOption( optionRoi, icon, _( "window|Mode" ), _( "Fullscreen" ), fheroes2::UiOptionTextWidth::THREE_ELEMENTS_ROW );
        }
        else {
            fheroes2::drawOption( optionRoi, originalIcon, _( "window|Mode" ), _( "Windowed" ), fheroes2::UiOptionTextWidth::THREE_ELEMENTS_ROW );
        }
    }

    void drawVSync( const fheroes2::Rect & optionRoi )
    {
        const bool isVSyncEnabled = Settings::Get().isVSyncEnabled();

        fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::SPANEL, isVSyncEnabled ? 18 : 19 ), _( "V-Sync" ), isVSyncEnabled ? _( "On" ) : _( "Off" ),
                              fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW );
    }

    void drawSystemInfo( const fheroes2::Rect & optionRoi )
    {
        const bool isSystemInfoDisplayed = Settings::Get().isSystemInfoEnabled();

        fheroes2::Sprite image = fheroes2::AGG::GetICN( ICN::EMPTY_OPTION_ICON_BACKGROUND, 0 );
        fheroes2::Text info;
        if ( isSystemInfoDisplayed ) {
            info.set( _( "FPS" ), fheroes2::FontType( fheroes2::FontSize::NORMAL, fheroes2::FontColor::YELLOW ) );
        }
        else {
            info.set( _( "N/A" ), fheroes2::FontType( fheroes2::FontSize::NORMAL, fheroes2::FontColor::GRAY ) );
        }
        info.draw( ( image.width() - info.width() ) / 2, ( image.height() - info.height() ) / 2, image );

        fheroes2::drawOption( optionRoi, image, _( "System Info" ), isSystemInfoDisplayed ? _( "On" ) : _( "Off" ), fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW );
    }

    void drawScreenScalingTypeOptions( const fheroes2::Rect & optionRoi, const bool isScreenScalingTypeNearest )
    {
        if ( isScreenScalingTypeNearest ) {
            fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::GAME_OPTION_ICON, 3 ), _( "Screen Scaling Type" ), _( "Nearest" ),
                                  fheroes2::UiOptionTextWidth::THREE_ELEMENTS_ROW );
        }
        else {
            fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::GAME_OPTION_ICON, 2 ), _( "Screen Scaling Type" ), _( "Linear" ),
                                  fheroes2::UiOptionTextWidth::THREE_ELEMENTS_ROW );
        }
    }

    SelectedWindow showConfigurationWindow()
    {
        fheroes2::Display & display = fheroes2::Display::instance();

        fheroes2::StandardWindow background( 289, fheroes2::optionsStepY * 2 + 52, true, display );

        const fheroes2::Rect windowRoi = background.activeArea();

        const Settings & conf = Settings::Get();
        const bool isEvilInterface = conf.isEvilInterfaceEnabled();

        fheroes2::Button buttonOk;
        const int buttonOkIcnId = isEvilInterface ? ICN::BUTTON_SMALL_OKAY_EVIL : ICN::BUTTON_SMALL_OKAY_GOOD;
        background.renderButton( buttonOk, buttonOkIcnId, 0, 1, { 0, 11 }, fheroes2::StandardWindow::Padding::BOTTOM_CENTER );

        fheroes2::ImageRestorer emptyDialogRestorer( display, windowRoi.x, windowRoi.y, windowRoi.width, windowRoi.height );

        const fheroes2::Rect windowResolutionRoi( resolutionRoi + windowRoi.getPosition() );
        const fheroes2::Rect windowModeRoi( modeRoi + windowRoi.getPosition() );
        const fheroes2::Rect windowScreenScalingTypeRoi( screenScalingTypeRoi + windowRoi.getPosition() );
        const fheroes2::Rect windowVSyncRoi( vSyncRoi + windowRoi.getPosition() );
        const fheroes2::Rect windowSystemInfoRoi( systemInfoRoi + windowRoi.getPosition() );

        const auto drawOptions = [&conf, &windowResolutionRoi, &windowModeRoi, &windowScreenScalingTypeRoi, &windowVSyncRoi, &windowSystemInfoRoi]() {
            drawResolution( windowResolutionRoi );
            drawMode( windowModeRoi );
            drawScreenScalingTypeOptions( windowScreenScalingTypeRoi, conf.isScreenScalingTypeNearest() );
            drawVSync( windowVSyncRoi );
            drawSystemInfo( windowSystemInfoRoi );
        };

        drawOptions();

        display.render( background.totalArea() );

        bool isFullScreen = fheroes2::engine().isFullScreen();

        LocalEvent & le = LocalEvent::Get();
        while ( le.HandleEvents() ) {
            buttonOk.drawOnState( le.isMouseLeftButtonPressedAndHeldInArea( buttonOk.area() ) );

            if ( le.MouseClickLeft( buttonOk.area() ) || Game::HotKeyCloseWindow() ) {
                break;
            }
            if ( le.MouseClickLeft( windowResolutionRoi ) ) {
                return SelectedWindow::Resolution;
            }
            if ( le.MouseClickLeft( windowModeRoi ) ) {
                return SelectedWindow::Mode;
            }
            if ( le.MouseClickLeft( windowScreenScalingTypeRoi ) ) {
                return SelectedWindow::ScreenScalingType;
            }
            if ( le.MouseClickLeft( windowVSyncRoi ) ) {
                return SelectedWindow::VSync;
            }
            if ( le.MouseClickLeft( windowSystemInfoRoi ) ) {
                return SelectedWindow::SystemInfo;
            }

            if ( le.isMouseRightButtonPressedInArea( windowResolutionRoi ) ) {
                fheroes2::showStandardTextMessage( _( "Select Game Resolution" ), _( "Change the resolution of the game." ), 0 );
            }
            else if ( le.isMouseRightButtonPressedInArea( windowModeRoi ) ) {
                fheroes2::showStandardTextMessage( _( "window|Mode" ), _( "Toggle between fullscreen and windowed modes." ), 0 );
            }
            else if ( le.isMouseRightButtonPressedInArea( windowScreenScalingTypeRoi ) ) {
                fheroes2::showStandardTextMessage(
                    _( "Screen Scaling Type" ),
                    _( "Toggle the type of screen scaling you want to use. Linear scaling makes the resized screen image blurry while nearest scaling preserves the sharpness of the original game. The nearest scaling looks best on integer scales, for example 2.0x or 3.0x." ),
                    0 );
            }
            else if ( le.isMouseRightButtonPressedInArea( windowVSyncRoi ) ) {
                fheroes2::showStandardTextMessage( _( "V-Sync" ), _( "The V-Sync option can be enabled to resolve flickering issues on some monitors." ), 0 );
            }
            if ( le.isMouseRightButtonPressedInArea( windowSystemInfoRoi ) ) {
                fheroes2::showStandardTextMessage( _( "System Info" ), _( "Show extra information such as FPS and current time." ), 0 );
            }
            else if ( le.isMouseRightButtonPressedInArea( buttonOk.area() ) ) {
                fheroes2::showStandardTextMessage( _( "Okay" ), _( "Exit this menu." ), 0 );
            }

            // Fullscreen mode can be toggled using a global hotkey, we need to properly reflect this change in the UI
            if ( isFullScreen != fheroes2::engine().isFullScreen() ) {
                isFullScreen = fheroes2::engine().isFullScreen();

                emptyDialogRestorer.restore();
                drawOptions();

                display.render( emptyDialogRestorer.rect() );
            }
        }

        return SelectedWindow::Exit;
    }
}

namespace fheroes2
{
    bool openGraphicsSettingsDialog( const std::function<void()> & updateUI )
    {
        const CursorRestorer cursorRestorer( true, ::Cursor::POINTER );

        Settings & conf = Settings::Get();

        bool saveConfiguration = false;

        SelectedWindow windowType = SelectedWindow::Configuration;
        while ( windowType != SelectedWindow::Exit ) {
            switch ( windowType ) {
            case SelectedWindow::Configuration:
                windowType = showConfigurationWindow();
                break;
            case SelectedWindow::Resolution:
                if ( Dialog::SelectResolution() ) {
                    saveConfiguration = true;
                }
                updateUI();
                windowType = SelectedWindow::Configuration;
                break;
            case SelectedWindow::Mode:
                conf.setFullScreen( !conf.FullScreen() );
                saveConfiguration = true;
                windowType = SelectedWindow::Configuration;
                break;
            case SelectedWindow::ScreenScalingType:
                conf.setScreenScalingTypeNearest( !conf.isScreenScalingTypeNearest() );
                saveConfiguration = true;
                windowType = SelectedWindow::Configuration;

                fheroes2::Display::instance().resetRenderer();
                break;
            case SelectedWindow::VSync:
                conf.setVSync( !conf.isVSyncEnabled() );
                saveConfiguration = true;
                windowType = SelectedWindow::Configuration;
                break;
            case SelectedWindow::SystemInfo:
                conf.setSystemInfo( !conf.isSystemInfoEnabled() );
                saveConfiguration = true;
                windowType = SelectedWindow::Configuration;
                break;
            default:
                return saveConfiguration;
            }
        }

        return saveConfiguration;
    }
}
