/*****************************************************************************
 * Copyright (c) 2014-2025 OpenRCT2 developers
 *
 * For a complete list of all authors, please refer to contributors.md
 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
 *
 * OpenRCT2 is licensed under the GNU General Public License version 3.
 *****************************************************************************/

#include "MapChangeSizeAction.h"

#include "../Context.h"
#include "../GameState.h"
#include "../drawing/IDrawingEngine.h"
#include "../ui/UiContext.h"
#include "../ui/WindowManager.h"
#include "../windows/Intent.h"
#include "../world/Park.h"

using namespace OpenRCT2;

MapChangeSizeAction::MapChangeSizeAction(const TileCoordsXY& targetSize)
    : MapChangeSizeAction(targetSize, TileCoordsXY())
{
}

MapChangeSizeAction::MapChangeSizeAction(const TileCoordsXY& targetSize, const TileCoordsXY& shift)
    : _targetSize(targetSize)
    , _shift(shift)
{
}

uint16_t MapChangeSizeAction::GetActionFlags() const
{
    return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}

void MapChangeSizeAction::Serialise(DataSerialiser& stream)
{
    GameAction::Serialise(stream);
    stream << DS_TAG(_targetSize);
    stream << DS_TAG(_shift);
}

GameActions::Result MapChangeSizeAction::Query() const
{
    if (_targetSize.x > kMaximumMapSizeTechnical || _targetSize.y > kMaximumMapSizeTechnical)
    {
        return GameActions::Result(
            GameActions::Status::InvalidParameters, STR_CANT_INCREASE_MAP_SIZE_ANY_FURTHER, STR_ERR_VALUE_OUT_OF_RANGE);
    }
    if (_targetSize.x < kMinimumMapSizeTechnical || _targetSize.y < kMinimumMapSizeTechnical)
    {
        return GameActions::Result(
            GameActions::Status::InvalidParameters, STR_CANT_DECREASE_MAP_SIZE_ANY_FURTHER, STR_ERR_VALUE_OUT_OF_RANGE);
    }
    return GameActions::Result();
}

GameActions::Result MapChangeSizeAction::Execute() const
{
    auto& gameState = getGameState();
    // Expand map
    while (_targetSize.x > gameState.mapSize.x)
    {
        gameState.mapSize.x++;
        MapExtendBoundarySurfaceX();
    }
    while (_targetSize.y > gameState.mapSize.y)
    {
        gameState.mapSize.y++;
        MapExtendBoundarySurfaceY();
    }

    // Shift the map (allows increasing the map at the 0,0 position
    ShiftMap(_shift);

    // Shrink map
    if (_targetSize.x < gameState.mapSize.x || _targetSize.y < gameState.mapSize.y)
    {
        gameState.mapSize = _targetSize;
        MapRemoveOutOfRangeElements();
    }

    auto* ctx = OpenRCT2::GetContext();
    auto uiContext = ctx->GetUiContext();
    auto* windowManager = uiContext->GetWindowManager();
    OpenRCT2::Park::UpdateSize(gameState);

    windowManager->BroadcastIntent(Intent(INTENT_ACTION_MAP));
    GfxInvalidateScreen();
    return GameActions::Result();
}

void MapChangeSizeAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
    visitor.Visit("targetSizeX", _targetSize.x);
    visitor.Visit("targetSizeY", _targetSize.y);
    visitor.Visit("shiftX", _shift.x);
    visitor.Visit("shiftY", _shift.y);
}
