/*****************************************************************************
 * Copyright (c) 2014-2020 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 "FootpathRemoveAction.h"

#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Wall.h"
#include "BannerRemoveAction.h"

FootpathRemoveAction::FootpathRemoveAction(const CoordsXYZ& location)
    : _loc(location)
{
}

void FootpathRemoveAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
    visitor.Visit(_loc);
}

uint16_t FootpathRemoveAction::GetActionFlags() const
{
    return GameAction::GetActionFlags();
}

void FootpathRemoveAction::Serialise(DataSerialiser& stream)
{
    GameAction::Serialise(stream);

    stream << DS_TAG(_loc);
}

GameActions::Result::Ptr FootpathRemoveAction::Query() const
{
    GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
    res->Cost = 0;
    res->Expenditure = ExpenditureType::Landscaping;
    res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };

    if (!LocationValid(_loc))
    {
        return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
    }

    if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
    {
        return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
    }

    TileElement* footpathElement = GetFootpathElement();
    if (footpathElement == nullptr)
    {
        return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_NONE);
    }

    res->Cost = GetRefundPrice(footpathElement);

    return res;
}

GameActions::Result::Ptr FootpathRemoveAction::Execute() const
{
    GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
    res->Cost = 0;
    res->Expenditure = ExpenditureType::Landscaping;
    res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };

    if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
    {
        footpath_interrupt_peeps(_loc);
        footpath_remove_litter(_loc);
    }

    TileElement* footpathElement = GetFootpathElement();
    if (footpathElement != nullptr)
    {
        footpath_queue_chain_reset();
        auto bannerRes = RemoveBannersAtElement(_loc, footpathElement);
        if (bannerRes->Error == GameActions::Status::Ok)
        {
            res->Cost += bannerRes->Cost;
        }
        footpath_remove_edges_at(_loc, footpathElement);
        map_invalidate_tile_full(_loc);
        tile_element_remove(footpathElement);
        footpath_update_queue_chains();

        // Remove the spawn point (if there is one in the current tile)
        gPeepSpawns.erase(
            std::remove_if(
                gPeepSpawns.begin(), gPeepSpawns.end(),
                [this](const CoordsXYZ& spawn) {
                    {
                        return spawn.ToTileStart() == _loc.ToTileStart();
                    }
                }),
            gPeepSpawns.end());
    }
    else
    {
        return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_NONE);
    }

    res->Cost += GetRefundPrice(footpathElement);

    return res;
}

TileElement* FootpathRemoveAction::GetFootpathElement() const
{
    bool getGhostPath = GetFlags() & GAME_COMMAND_FLAG_GHOST;

    TileElement* tileElement = map_get_footpath_element(_loc);
    TileElement* footpathElement = nullptr;
    if (tileElement != nullptr)
    {
        if (getGhostPath && !tileElement->IsGhost())
        {
            while (!(tileElement++)->IsLastForTile())
            {
                if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH && !tileElement->IsGhost())
                {
                    continue;
                }
                footpathElement = tileElement;
                break;
            }
        }
        else
        {
            footpathElement = tileElement;
        }
    }

    return footpathElement;
}

money32 FootpathRemoveAction::GetRefundPrice(TileElement* footpathElement) const
{
    money32 cost = -MONEY(10, 00);
    return cost;
}

/**
 *
 *  rct2: 0x006BA23E
 */
GameActions::Result::Ptr FootpathRemoveAction::RemoveBannersAtElement(const CoordsXY& loc, TileElement* tileElement) const
{
    auto result = MakeResult();
    while (!(tileElement++)->IsLastForTile())
    {
        if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
            return result;

        if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
            continue;

        auto bannerRemoveAction = BannerRemoveAction({ loc, tileElement->GetBaseZ(), tileElement->AsBanner()->GetPosition() });
        bool isGhost = tileElement->IsGhost();
        auto bannerFlags = GetFlags() | (isGhost ? static_cast<uint32_t>(GAME_COMMAND_FLAG_GHOST) : 0);
        bannerRemoveAction.SetFlags(bannerFlags);
        auto res = GameActions::ExecuteNested(&bannerRemoveAction);
        // Ghost removal is free
        if (res->Error == GameActions::Status::Ok && !isGhost)
        {
            result->Cost += res->Cost;
        }
        tileElement--;
    }
    return result;
}
