/***************************************************************************
 *   fheroes2: https://github.com/ihhub/fheroes2                           *
 *   Copyright (C) 2025 - 2026                                             *
 *                                                                         *
 *   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_random_map_generator.h"

#include <algorithm>
#include <cassert>
#include <string>
#include <utility>

#include "agg_image.h"
#include "dialog.h"
#include "game_hotkeys.h"
#include "icn.h"
#include "image.h"
#include "localevent.h"
#include "map_random_generator.h"
#include "math_base.h"
#include "pal.h"
#include "screen.h"
#include "settings.h"
#include "translations.h"
#include "ui_button.h"
#include "ui_dialog.h"
#include "ui_slider.h"
#include "ui_text.h"
#include "ui_window.h"

namespace
{
    constexpr int32_t sliderWidth{ 187 };

    class TextRestorer final
    {
    public:
        TextRestorer( fheroes2::Image & output, const int32_t positionX, const int32_t positionY )
            : _restorer( output, positionX, positionY, 150, 40 )
        {
            // Do nothing.
        }

        void render( std::string content, fheroes2::Image & output )
        {
            const fheroes2::Rect & roi = _restorer.rect();
            _restorer.restore();
            const fheroes2::Text text{ std::move( content ), fheroes2::FontType::normalYellow() };
            text.drawInRoi( roi.x, roi.y + 2, roi.width, output, roi );
        }

    private:
        fheroes2::ImageRestorer _restorer;
    };
}

bool fheroes2::randomMapGeneratorDialog( Maps::Random_Generator::Configuration & configuration, const int32_t mapWidth )
{
    if ( mapWidth < 1 ) {
        // What are you trying to achieve?!
        assert( 0 );
        return false;
    }

    // Verify the configuration parameters.
    configuration.playerCount = std::max<int32_t>( 2, configuration.playerCount );
    configuration.playerCount = std::min<int32_t>( 6, configuration.playerCount );

    const int32_t originalWaterPercentageLimit{ Maps::Random_Generator::calculateMaximumWaterPercentage( configuration.playerCount, mapWidth ) };
    int32_t currentWaterPercentageLimit = originalWaterPercentageLimit;
    configuration.waterPercentage = std::min( configuration.waterPercentage, originalWaterPercentageLimit );

    Display & display = Display::instance();
    const bool isDefaultScreenSize = display.isDefaultSize();

    StandardWindow window( Display::DEFAULT_WIDTH, Display::DEFAULT_HEIGHT, !isDefaultScreenSize, display );
    const Rect activeArea( window.activeArea() );

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

    if ( isDefaultScreenSize ) {
        const Sprite & backgroundImage = AGG::GetICN( isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK, 0 );
        Copy( backgroundImage, 0, 0, display, activeArea );
    }

    // Dialog title.
    const Sprite & titleBox = AGG::GetICN( isEvilInterface ? ICN::METALLIC_BORDERED_TEXTBOX_EVIL : ICN::METALLIC_BORDERED_TEXTBOX_GOOD, 0 );
    const Rect titleBoxRoi( activeArea.x + ( activeArea.width - titleBox.width() ) / 2, activeArea.y + 10, titleBox.width(), titleBox.height() );
    const Rect titleTextRoi( titleBoxRoi.x + 6, titleBoxRoi.y + 5, titleBoxRoi.width - 12, titleBoxRoi.height - 11 );

    Copy( titleBox, 0, 0, display, titleBoxRoi );
    addGradientShadow( titleBox, display, titleBoxRoi.getPosition(), { -5, 5 } );

    Text text( _( "Random Map Generator" ), FontType::normalWhite() );
    text.fitToOneRow( titleTextRoi.width );
    text.drawInRoi( titleTextRoi.x, titleTextRoi.y + 3, titleTextRoi.width, display, titleTextRoi );

    const int32_t positionX = activeArea.x + 12;
    const int32_t settingDescriptionWidth = activeArea.width / 3;
    const int32_t inputPositionX = positionX + settingDescriptionWidth;
    const int32_t valuePositionX = inputPositionX + 230;
    const int32_t ySpacing = 45;
    int32_t positionY = activeArea.y + 70;

    // Map configuration options.
    text.set( _( "rmg|Player count:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );
    fheroes2::HorizontalSlider playerCountSlider{ sliderWidth, { inputPositionX, positionY }, 2, 6, configuration.playerCount };
    TextRestorer playerCountValue{ display, valuePositionX, positionY };
    playerCountValue.render( std::to_string( configuration.playerCount ), display );

    positionY += ySpacing;

    text.set( _( "rmg|Map layout:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );

    // Dropdown with map layout selection.
    const int dropListIcn = isEvilInterface ? ICN::DROPLISL_EVIL : ICN::DROPLISL;
    const Sprite & itemBackground = AGG::GetICN( dropListIcn, 0 );
    const int32_t layoutBackgroundWidth = 200;
    const int32_t layoutBackgroundHeight = itemBackground.height();

    Copy( itemBackground, 0, 0, display, inputPositionX + 6, positionY - 5, layoutBackgroundWidth, layoutBackgroundHeight );
    text.set( Maps::Random_Generator::layoutToString( configuration.mapLayout ), FontType::normalWhite() );
    text.draw( inputPositionX + 12, positionY, display );

    // TODO: remove the next line when the dropdown is operational.
    ApplyPalette( display, inputPositionX + 6, positionY - 5, display, inputPositionX + 6, positionY - 5, layoutBackgroundWidth, layoutBackgroundHeight,
                  PAL::GetPalette( PAL::PaletteType::DARKENING ) );

    const Sprite & dropListButtonSprite = AGG::GetICN( dropListIcn, 1 );
    const Sprite & dropListButtonPressedSprite = AGG::GetICN( dropListIcn, 2 );

    ButtonSprite layoutDroplistButton( inputPositionX + layoutBackgroundWidth + 6, positionY - 5, dropListButtonSprite, dropListButtonPressedSprite );
    // TODO: remove the next line when the dropdown is operational.
    layoutDroplistButton.disable();
    layoutDroplistButton.draw();

    positionY += ySpacing;

    text.set( _( "rmg|Water percentage:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );

    fheroes2::HorizontalSlider waterSlider{ sliderWidth, { inputPositionX, positionY }, 0, originalWaterPercentageLimit, configuration.waterPercentage };
    TextRestorer waterValue{ display, valuePositionX, positionY };
    waterValue.render( std::to_string( configuration.waterPercentage ) + '/' + std::to_string( currentWaterPercentageLimit ) + '%', display );

    positionY += ySpacing;

    text.set( _( "rmg|Monster strength:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );
    fheroes2::HorizontalSlider monsterSlider{ sliderWidth, { inputPositionX, positionY }, 0, 3, static_cast<int>( configuration.monsterStrength ) };
    TextRestorer monsterValue{ display, valuePositionX, positionY };
    monsterValue.render( Maps::Random_Generator::monsterStrengthToString( configuration.monsterStrength ), display );

    positionY += ySpacing;

    text.set( _( "rmg|Resource availability:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );
    fheroes2::HorizontalSlider resourceSlider{ sliderWidth, { inputPositionX, positionY }, 0, 2, static_cast<int>( configuration.resourceDensity ) };
    TextRestorer resourceValue{ display, valuePositionX, positionY };
    resourceValue.render( Maps::Random_Generator::resourceDensityToString( configuration.resourceDensity ), display );

    positionY += ySpacing + 10;

    text.set( _( "rmg|Map seed:" ), FontType::normalWhite() );
    text.draw( positionX + ( settingDescriptionWidth - text.width() ) / 2, positionY, display );

    ValueSelectionDialogElement mapSeedSelection{ 0, 999999, configuration.seed, 1, { positionX + settingDescriptionWidth + 4, positionY - 5 } };
    mapSeedSelection.draw( display );

    Button buttonCancel;
    const int buttonCancelIcn = isEvilInterface ? ICN::BUTTON_SMALL_CANCEL_EVIL : ICN::BUTTON_SMALL_CANCEL_GOOD;
    window.renderButton( buttonCancel, buttonCancelIcn, 0, 1, { 30, 10 }, StandardWindow::Padding::BOTTOM_RIGHT );

    Button buttonOk;
    const int buttonOkIcn = isEvilInterface ? ICN::BUTTON_SMALL_OKAY_EVIL : ICN::BUTTON_SMALL_OKAY_GOOD;
    window.renderButton( buttonOk, buttonOkIcn, 0, 1, { 30, 10 }, StandardWindow::Padding::BOTTOM_LEFT );

    display.render( window.totalArea() );

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

        if ( Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_CANCEL ) || le.MouseClickLeft( buttonCancel.area() ) ) {
            return false;
        }

        if ( Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_OKAY ) || le.MouseClickLeft( buttonOk.area() ) ) {
            break;
        }

        if ( le.isMouseRightButtonPressedInArea( buttonOk.area() ) ) {
            showStandardTextMessage( _( "Okay" ), _( "Click to generate a new map." ), Dialog::ZERO );
        }
        else if ( le.isMouseRightButtonPressedInArea( buttonCancel.area() ) ) {
            showStandardTextMessage( _( "Cancel" ), _( "Return to the previous menu." ), Dialog::ZERO );
        }
        else if ( playerCountSlider.processEvents( le ) ) {
            configuration.playerCount = playerCountSlider.getCurrentValue();

            const int32_t newLimit = Maps::Random_Generator::calculateMaximumWaterPercentage( configuration.playerCount, mapWidth );
            configuration.waterPercentage = std::min( configuration.waterPercentage, newLimit );
            waterSlider.setRange( 0, newLimit );
            currentWaterPercentageLimit = newLimit;

            playerCountValue.render( std::to_string( configuration.playerCount ), display );
            waterValue.render( std::to_string( configuration.waterPercentage ) + '/' + std::to_string( currentWaterPercentageLimit ) + '%', display );
            display.render( window.activeArea() );
        }
        else if ( waterSlider.processEvents( le ) ) {
            configuration.waterPercentage = waterSlider.getCurrentValue();
            waterValue.render( std::to_string( configuration.waterPercentage ) + '/' + std::to_string( currentWaterPercentageLimit ) + '%', display );
            display.render( window.activeArea() );
        }
        else if ( monsterSlider.processEvents( le ) ) {
            configuration.monsterStrength = static_cast<Maps::Random_Generator::MonsterStrength>( monsterSlider.getCurrentValue() );
            monsterValue.render( Maps::Random_Generator::monsterStrengthToString( configuration.monsterStrength ), display );
            display.render( window.activeArea() );
        }
        else if ( resourceSlider.processEvents( le ) ) {
            configuration.resourceDensity = static_cast<Maps::Random_Generator::ResourceDensity>( resourceSlider.getCurrentValue() );
            resourceValue.render( Maps::Random_Generator::resourceDensityToString( configuration.resourceDensity ), display );
            display.render( window.activeArea() );
        }
        else if ( mapSeedSelection.processEvents() ) {
            mapSeedSelection.draw( display );

            configuration.seed = mapSeedSelection.getValue();
            display.render( window.activeArea() );
        }
    }

    return true;
}
