/*****************************************************************************
 * 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 <algorithm>
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Context.h>
#include <openrct2/Input.h>
#include <openrct2/audio/audio.h>
#include <openrct2/core/Guard.hpp>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/management/Research.h>
#include <openrct2/network/network.h>
#include <openrct2/object/ObjectList.h>
#include <openrct2/sprites.h>
#include <openrct2/world/LargeScenery.h>
#include <openrct2/world/Park.h>
#include <openrct2/world/Scenery.h>
#include <openrct2/world/SmallScenery.h>

static constexpr const rct_string_id WINDOW_TITLE = STR_NONE;
constexpr int32_t WINDOW_SCENERY_WIDTH = 634;
constexpr int32_t WINDOW_SCENERY_HEIGHT = 180;
constexpr int32_t SCENERY_BUTTON_WIDTH = 66;
constexpr int32_t SCENERY_BUTTON_HEIGHT = 80;

// clang-format off
static void WindowSceneryClose(rct_window *w);
static void WindowSceneryMouseup(rct_window *w, rct_widgetindex widgetIndex);
static void WindowSceneryResize(rct_window *w);
static void WindowSceneryMousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget* widget);
static void WindowSceneryDropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
static void WindowSceneryUpdate(rct_window *w);
static void WindowSceneryPeriodicUpdate(rct_window *w);
static void WindowSceneryScrollgetsize(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height);
static void WindowSceneryScrollmousedown(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
static void WindowSceneryScrollmouseover(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
static OpenRCT2String WindowSceneryTooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback);
static void WindowSceneryInvalidate(rct_window *w);
static void WindowSceneryPaint(rct_window *w, rct_drawpixelinfo *dpi);
static void WindowSceneryScrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex);

static rct_window_event_list window_scenery_events([](auto& events)
{
    events.close = &WindowSceneryClose;
    events.mouse_up = &WindowSceneryMouseup;
    events.resize = &WindowSceneryResize;
    events.mouse_down = &WindowSceneryMousedown;
    events.dropdown = &WindowSceneryDropdown;
    events.update = &WindowSceneryUpdate;
    events.periodic_update = &WindowSceneryPeriodicUpdate;
    events.get_scroll_size = &WindowSceneryScrollgetsize;
    events.scroll_mousedown = &WindowSceneryScrollmousedown;
    events.scroll_mouseover = &WindowSceneryScrollmouseover;
    events.tooltip = &WindowSceneryTooltip;
    events.invalidate = &WindowSceneryInvalidate;
    events.paint = &WindowSceneryPaint;
    events.scroll_paint = &WindowSceneryScrollpaint;
});


enum WINDOW_SCENERY_LIST_WIDGET_IDX {
    WIDX_SCENERY_BACKGROUND,
    WIDX_SCENERY_TITLE,
    WIDX_SCENERY_CLOSE,
    WIDX_SCENERY_TAB_CONTENT_PANEL,
    WIDX_SCENERY_LIST,
    WIDX_SCENERY_ROTATE_OBJECTS_BUTTON,
    WIDX_SCENERY_REPAINT_SCENERY_BUTTON,
    WIDX_SCENERY_PRIMARY_COLOUR_BUTTON,
    WIDX_SCENERY_SECONDARY_COLOUR_BUTTON,
    WIDX_SCENERY_TERTIARY_COLOUR_BUTTON,
    WIDX_SCENERY_EYEDROPPER_BUTTON,
    WIDX_SCENERY_BUILD_CLUSTER_BUTTON,
    WIDX_SCENERY_TAB_1,
};

validate_global_widx(WC_SCENERY, WIDX_SCENERY_TAB_1);
validate_global_widx(WC_SCENERY, WIDX_SCENERY_ROTATE_OBJECTS_BUTTON);
validate_global_widx(WC_SCENERY, WIDX_SCENERY_EYEDROPPER_BUTTON);

static rct_widget WindowSceneryBaseWidgets[] = {
    WINDOW_SHIM(WINDOW_TITLE, WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT),
    MakeWidget     ({  0,  43}, {634, 99}, WindowWidgetType::Resize,    WindowColour::Secondary                                                  ), // 8         0x009DE2C8
    MakeWidget     ({  2,  47}, {607, 80}, WindowWidgetType::Scroll,    WindowColour::Secondary, SCROLL_VERTICAL                                 ), // 1000000   0x009DE418
    MakeWidget     ({609,  44}, { 24, 24}, WindowWidgetType::FlatBtn,   WindowColour::Secondary, SPR_ROTATE_ARROW,    STR_ROTATE_OBJECTS_90      ), // 2000000   0x009DE428
    MakeWidget     ({609,  68}, { 24, 24}, WindowWidgetType::FlatBtn,   WindowColour::Secondary, SPR_PAINTBRUSH,      STR_SCENERY_PAINTBRUSH_TIP ), // 4000000   0x009DE438
    MakeWidget     ({615,  93}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF,          STR_SELECT_COLOUR          ), // 8000000   0x009DE448
    MakeWidget     ({615, 105}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF,          STR_SELECT_SECONDARY_COLOUR), // 10000000  0x009DE458
    MakeWidget     ({615, 117}, { 12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF,          STR_SELECT_TERNARY_COLOUR  ), // 20000000  0x009DE468
    MakeWidget     ({609, 130}, { 24, 24}, WindowWidgetType::FlatBtn,   WindowColour::Secondary, SPR_G2_EYEDROPPER,   STR_SCENERY_EYEDROPPER_TIP ), // 40000000  0x009DE478
    MakeWidget     ({609, 154}, { 24, 24}, WindowWidgetType::FlatBtn,   WindowColour::Secondary, SPR_SCENERY_CLUSTER, STR_SCENERY_CLUSTER_TIP    ), // 40000000  0x009DE478
    WIDGETS_END,
};
// clang-format on

void WindowSceneryUpdateScroll(rct_window* w);

struct SceneryTabInfo
{
    ObjectEntryIndex SceneryGroupIndex = OBJECT_ENTRY_INDEX_NULL;
    std::vector<ScenerySelection> Entries;

    bool IsMisc() const
    {
        return SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL;
    }

    bool Contains(const ScenerySelection& entry) const
    {
        return std::find(std::begin(Entries), std::end(Entries), entry) != std::end(Entries);
    }

    void AddEntry(const ScenerySelection& entry)
    {
        if (!Contains(entry))
        {
            Entries.push_back(entry);
        }
    }

    const rct_scenery_group_entry* GetSceneryGroupEntry() const
    {
        return get_scenery_group_entry(SceneryGroupIndex);
    }
};

std::vector<ScenerySelection> gWindowSceneryTabSelections;
size_t gWindowSceneryActiveTabIndex;
uint8_t gWindowSceneryPaintEnabled;
uint8_t gWindowSceneryRotation;
colour_t gWindowSceneryPrimaryColour;
colour_t gWindowScenerySecondaryColour;
colour_t gWindowSceneryTertiaryColour;
bool gWindowSceneryEyedropperEnabled;

static std::vector<SceneryTabInfo> _tabEntries;
static std::vector<rct_widget> _widgets;
static ScenerySelection _selectedScenery;
static int16_t _hoverCounter;

static const ScenerySelection GetSelectedScenery(const size_t tabIndex)
{
    if (gWindowSceneryTabSelections.size() > tabIndex)
    {
        return gWindowSceneryTabSelections[tabIndex];
    }
    return {};
}

static void SetSelectedScenery(const size_t tabIndex, const ScenerySelection& value)
{
    if (gWindowSceneryTabSelections.size() <= tabIndex)
    {
        gWindowSceneryTabSelections.resize(tabIndex + 1);
    }
    gWindowSceneryTabSelections[tabIndex] = value;
}

static SceneryTabInfo* GetSceneryTabInfoForGroup(const ObjectEntryIndex sceneryGroupIndex)
{
    if (sceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL)
    {
        return &_tabEntries[_tabEntries.size() - 1];
    }

    for (auto& tabEntry : _tabEntries)
    {
        if (tabEntry.SceneryGroupIndex == sceneryGroupIndex)
            return &tabEntry;
    }

    return nullptr;
}

static std::optional<size_t> WindowSceneryFindTabWithScenery(const ScenerySelection& scenery)
{
    for (size_t i = 0; i < _tabEntries.size(); i++)
    {
        const auto& tabInfo = _tabEntries[i];
        if (tabInfo.Contains(scenery))
        {
            return i;
        }
    }
    return std::nullopt;
}

static void InitSceneryEntry(const ScenerySelection& selection, const ObjectEntryIndex sceneryGroupIndex)
{
    Guard::ArgumentInRange<int32_t>(selection.EntryIndex, 0, OBJECT_ENTRY_INDEX_NULL);

    if (IsSceneryAvailableToBuild(selection))
    {
        // Get current tab
        const auto tabIndex = WindowSceneryFindTabWithScenery(selection);

        // Add scenery to primary group (usually trees or path additions)
        if (sceneryGroupIndex != OBJECT_ENTRY_INDEX_NULL)
        {
            auto* tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex);
            if (tabInfo != nullptr)
            {
                tabInfo->AddEntry(selection);
                return;
            }
        }

        // If scenery is no tab, add it to misc
        if (!tabIndex.has_value())
        {
            auto* tabInfo = GetSceneryTabInfoForGroup(OBJECT_ENTRY_INDEX_NULL);
            if (tabInfo != nullptr)
            {
                tabInfo->AddEntry(selection);
            }
        }
    }
}

static void WindowScenerySortTabs()
{
    std::sort(_tabEntries.begin(), _tabEntries.end(), [](const SceneryTabInfo& a, const SceneryTabInfo& b) {
        if (a.SceneryGroupIndex == b.SceneryGroupIndex)
            return false;

        if (a.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL)
            return false;
        if (b.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL)
            return true;

        const auto* entryA = a.GetSceneryGroupEntry();
        const auto* entryB = b.GetSceneryGroupEntry();
        return entryA->priority < entryB->priority;
    });
}

static void WindowSceneryPrepareWidgets(rct_window* w)
{
    // Add the base widgets
    _widgets.clear();
    for (const auto& widget : WindowSceneryBaseWidgets)
    {
        _widgets.push_back(widget);
    }

    // Remove WWT_LAST
    auto lastWidget = _widgets.back();
    _widgets.pop_back();

    // Add tabs
    ScreenCoordsXY pos = { 3, 17 };
    for (const auto& tabInfo : _tabEntries)
    {
        auto widget = MakeTab(pos, STR_STRING_DEFINED_TOOLTIP);
        pos.x += 31;

        if (tabInfo.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL)
        {
            widget.image = SPR_TAB_QUESTION | IMAGE_TYPE_REMAP;
        }
        else
        {
            const auto* sceneryGroupEntry = get_scenery_group_entry(tabInfo.SceneryGroupIndex);
            if (sceneryGroupEntry != nullptr)
            {
                widget.image = sceneryGroupEntry->image | IMAGE_TYPE_REMAP;
            }
        }

        _widgets.push_back(widget);
    }

    _widgets.push_back(lastWidget);

    w->widgets = _widgets.data();

    w->enabled_widgets = (1 << WIDX_SCENERY_CLOSE) | (1 << WIDX_SCENERY_ROTATE_OBJECTS_BUTTON)
        | (1 << WIDX_SCENERY_PRIMARY_COLOUR_BUTTON) | (1 << WIDX_SCENERY_SECONDARY_COLOUR_BUTTON)
        | (1 << WIDX_SCENERY_REPAINT_SCENERY_BUTTON) | (1 << WIDX_SCENERY_TERTIARY_COLOUR_BUTTON)
        | (1 << WIDX_SCENERY_EYEDROPPER_BUTTON) | (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON);
    for (size_t i = 0; i < _tabEntries.size(); i++)
    {
        w->enabled_widgets |= (1ULL << (WIDX_SCENERY_TAB_1 + i));
    }
}

/**
 *
 *  rct2: 0x006DFA00
 */
static void WindowSceneryInit(rct_window* w)
{
    _tabEntries.clear();

    auto maxTabs = 32;
    for (ObjectEntryIndex scenerySetIndex = 0; scenerySetIndex < maxTabs - 1; scenerySetIndex++)
    {
        const auto* sceneryGroupEntry = get_scenery_group_entry(scenerySetIndex);
        if (sceneryGroupEntry != nullptr && scenery_group_is_invented(scenerySetIndex))
        {
            SceneryTabInfo tabInfo;
            tabInfo.SceneryGroupIndex = scenerySetIndex;
            for (size_t i = 0; i < sceneryGroupEntry->entry_count; i++)
            {
                const auto& sceneryEntry = sceneryGroupEntry->scenery_entries[i];
                if (IsSceneryAvailableToBuild(sceneryEntry))
                {
                    tabInfo.Entries.push_back(sceneryEntry);
                }
            }
            if (tabInfo.Entries.size() > 0)
            {
                _tabEntries.push_back(std::move(tabInfo));
            }
        }
    }

    // Add misc tab
    _tabEntries.emplace_back();

    // small scenery
    for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_SMALL_SCENERY_OBJECTS; sceneryId++)
    {
        const auto* sceneryEntry = get_small_scenery_entry(sceneryId);
        if (sceneryEntry != nullptr)
        {
            InitSceneryEntry({ SCENERY_TYPE_SMALL, sceneryId }, sceneryEntry->scenery_tab_id);
        }
    }

    // large scenery
    for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_LARGE_SCENERY_OBJECTS; sceneryId++)
    {
        const auto* sceneryEntry = get_large_scenery_entry(sceneryId);
        if (sceneryEntry != nullptr)
        {
            InitSceneryEntry({ SCENERY_TYPE_LARGE, sceneryId }, sceneryEntry->scenery_tab_id);
        }
    }

    // walls
    for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_WALL_SCENERY_OBJECTS; sceneryId++)
    {
        const auto* sceneryEntry = get_wall_entry(sceneryId);
        if (sceneryEntry != nullptr)
        {
            InitSceneryEntry({ SCENERY_TYPE_WALL, sceneryId }, sceneryEntry->scenery_tab_id);
        }
    }

    // banners
    for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_BANNER_OBJECTS; sceneryId++)
    {
        const auto* sceneryEntry = get_banner_entry(sceneryId);
        if (sceneryEntry != nullptr)
        {
            InitSceneryEntry({ SCENERY_TYPE_BANNER, sceneryId }, sceneryEntry->scenery_tab_id);
        }
    }

    // path bits
    for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_PATH_ADDITION_OBJECTS; sceneryId++)
    {
        const auto* sceneryEntry = get_footpath_item_entry(sceneryId);
        if (sceneryEntry != nullptr)
        {
            InitSceneryEntry({ SCENERY_TYPE_PATH_ITEM, sceneryId }, sceneryEntry->scenery_tab_id);
        }
    }

    // Remove misc tab if empty
    if (_tabEntries.back().Entries.size() == 0)
    {
        _tabEntries.pop_back();
    }

    WindowScenerySortTabs();
    WindowSceneryPrepareWidgets(w);
    window_invalidate_by_class(WC_SCENERY);
}

void WindowSceneryInit()
{
    auto* w = window_find_by_class(WC_SCENERY);
    if (w != nullptr)
    {
        WindowSceneryInit(w);
    }
}

/**
 *
 *  rct2: 0x006DFEE4
 */
void WindowScenerySetDefaultPlacementConfiguration()
{
    gWindowSceneryRotation = 3;
    gWindowSceneryPrimaryColour = COLOUR_BORDEAUX_RED;
    gWindowScenerySecondaryColour = COLOUR_YELLOW;
    gWindowSceneryTertiaryColour = COLOUR_DARK_BROWN;

    WindowSceneryInit();

    gWindowSceneryTabSelections.clear();
    gWindowSceneryActiveTabIndex = 0;
}

/**
 *
 *  rct2: 0x006E0FEF
 */
rct_window* WindowSceneryOpen()
{
    // Check if window is already open
    auto* window = window_bring_to_front_by_class(WC_SCENERY);
    if (window != nullptr)
        return window;

    window = WindowCreate(
        ScreenCoordsXY(context_get_width() - WINDOW_SCENERY_WIDTH, 0x1D), WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT,
        &window_scenery_events, WC_SCENERY, WF_NO_SCROLLING);

    WindowSceneryInit(window);

    WindowInitScrollWidgets(window);
    WindowSceneryUpdateScroll(window);
    show_gridlines();
    gWindowSceneryRotation = 3;
    gSceneryCtrlPressed = false;
    gSceneryShiftPressed = false;
    _selectedScenery = {};
    _hoverCounter = 0;
    window_push_others_below(window);
    gSceneryGhostType = 0;
    gSceneryPlaceCost = MONEY32_UNDEFINED;
    gSceneryPlaceRotation = 0;
    gWindowSceneryPaintEnabled = 0; // repaint coloured scenery tool state
    gWindowSceneryEyedropperEnabled = false;

    window->min_width = WINDOW_SCENERY_WIDTH;
    window->max_width = WINDOW_SCENERY_WIDTH;
    window->min_height = WINDOW_SCENERY_HEIGHT;
    window->max_height = WINDOW_SCENERY_HEIGHT;

    return window;
}

static int32_t WindowSceneryGetNumColumns(const rct_window* w)
{
    const auto* listWidget = &w->widgets[WIDX_SCENERY_LIST];
    const auto contentWidth = listWidget->width() - SCROLLBAR_WIDTH;
    return contentWidth / SCENERY_BUTTON_WIDTH;
}

/*
 *
 *  rct2: 0x006E1A73
 */
void WindowSceneryClose(rct_window* w)
{
    scenery_remove_ghost_tool_placement();
    hide_gridlines();
    viewport_set_visibility(0);

    if (gWindowSceneryScatterEnabled)
        window_close_by_class(WC_SCENERY_SCATTER);

    if (scenery_tool_is_active())
        tool_cancel();
}

template<typename T> constexpr static T WindowSceneryCountRows(const rct_window* w, T items)
{
    const auto rows = items / WindowSceneryGetNumColumns(w);
    return rows;
}

static size_t WindowSceneryCountRows(const rct_window* w)
{
    const auto tabIndex = gWindowSceneryActiveTabIndex;
    if (tabIndex >= _tabEntries.size())
    {
        return 0;
    }

    const auto totalItems = _tabEntries[tabIndex].Entries.size();
    const auto numColumns = WindowSceneryGetNumColumns(w);
    const auto rows = WindowSceneryCountRows(w, totalItems + numColumns - 1);
    return rows;
}

struct scenery_item
{
    int32_t allRows;
    int32_t selected_item;
    ScenerySelection scenerySelection;
};

static scenery_item WindowSceneryCountRowsWithSelectedItem(rct_window* w, const size_t tabIndex)
{
    scenery_item sceneryItem = { 0, 0, ScenerySelection() };
    const auto scenerySelection = GetSelectedScenery(tabIndex);
    const auto& tabInfo = _tabEntries[tabIndex];
    for (size_t i = 0; i < tabInfo.Entries.size(); i++)
    {
        const auto& currentEntry = tabInfo.Entries[i];
        if (currentEntry == scenerySelection)
        {
            sceneryItem.selected_item = static_cast<int32_t>(i);
            sceneryItem.scenerySelection = scenerySelection;
        }
    }
    sceneryItem.allRows = static_cast<int32_t>(WindowSceneryCountRows(w, tabInfo.Entries.size() + 8));
    return sceneryItem;
}

static int32_t WindowSceneryRowsHeight(const size_t rows)
{
    return static_cast<int32_t>(rows * SCENERY_BUTTON_HEIGHT);
}

/**
 *
 *  rct2: 0x006BD94C
 */
static void WindowSceneryMouseup(rct_window* w, rct_widgetindex widgetIndex)
{
    switch (widgetIndex)
    {
        case WIDX_SCENERY_CLOSE:
            if (gWindowSceneryScatterEnabled)
                window_close_by_class(WC_SCENERY_SCATTER);
            window_close(w);
            break;
        case WIDX_SCENERY_ROTATE_OBJECTS_BUTTON:
            gWindowSceneryRotation++;
            gWindowSceneryRotation = gWindowSceneryRotation % 4;
            scenery_remove_ghost_tool_placement();
            w->Invalidate();
            break;
        case WIDX_SCENERY_REPAINT_SCENERY_BUTTON:
            gWindowSceneryPaintEnabled ^= 1;
            gWindowSceneryEyedropperEnabled = false;
            if (gWindowSceneryScatterEnabled)
                window_close_by_class(WC_SCENERY_SCATTER);
            w->Invalidate();
            break;
        case WIDX_SCENERY_EYEDROPPER_BUTTON:
            gWindowSceneryPaintEnabled = 0;
            gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled;
            if (gWindowSceneryScatterEnabled)
                window_close_by_class(WC_SCENERY_SCATTER);
            scenery_remove_ghost_tool_placement();
            w->Invalidate();
            break;
        case WIDX_SCENERY_BUILD_CLUSTER_BUTTON:
            gWindowSceneryPaintEnabled = 0;
            gWindowSceneryEyedropperEnabled = false;
            if (gWindowSceneryScatterEnabled)
                window_close_by_class(WC_SCENERY_SCATTER);
            else if (
                network_get_mode() != NETWORK_MODE_CLIENT
                || network_can_perform_command(network_get_current_player_group_index(), -2))
            {
                WindowSceneryScatterOpen();
            }
            else
            {
                context_show_error(STR_CANT_DO_THIS, STR_PERMISSION_DENIED, {});
            }
            w->Invalidate();
            break;
    }
}

/**
 *
 *  rct2: 0x006E1EB4
 */
void WindowSceneryUpdateScroll(rct_window* w)
{
    const auto tabIndex = gWindowSceneryActiveTabIndex;
    if (tabIndex >= _tabEntries.size())
    {
        return;
    }

    const int32_t listHeight = w->height - 14 - w->widgets[WIDX_SCENERY_LIST].top - 1;

    const auto sceneryItem = WindowSceneryCountRowsWithSelectedItem(w, tabIndex);
    w->scrolls[0].v_bottom = WindowSceneryRowsHeight(sceneryItem.allRows) + 1;

    const int32_t maxTop = std::max(0, w->scrolls[0].v_bottom - listHeight);
    auto rowSelected = WindowSceneryCountRows(w, sceneryItem.selected_item);
    if (sceneryItem.scenerySelection.IsUndefined())
    {
        rowSelected = 0;
        const auto& scenery = _tabEntries[tabIndex].Entries[0];
        if (!scenery.IsUndefined())
        {
            SetSelectedScenery(tabIndex, scenery);
        }
    }

    w->scrolls[0].v_top = WindowSceneryRowsHeight(rowSelected);
    w->scrolls[0].v_top = std::min<int32_t>(maxTop, w->scrolls[0].v_top);

    WidgetScrollUpdateThumbs(w, WIDX_SCENERY_LIST);
}

/**
 *
 *  rct2: 0x006E1E48
 */
static void WindowSceneryResize(rct_window* w)
{
    if (w->width < w->min_width)
    {
        w->Invalidate();
        w->width = w->min_width;
        w->Invalidate();
    }

    if (w->width > w->max_width)
    {
        w->Invalidate();
        w->width = w->max_width;
        w->Invalidate();
    }

    if (w->height < w->min_height)
    {
        w->Invalidate();
        w->height = w->min_height;
        w->Invalidate();
        // HACK: For some reason invalidate has not been called
        window_event_invalidate_call(w);
        WindowSceneryUpdateScroll(w);
    }

    if (w->height > w->max_height)
    {
        w->Invalidate();
        w->height = w->max_height;
        w->Invalidate();
        // HACK: For some reason invalidate has not been called
        window_event_invalidate_call(w);
        WindowSceneryUpdateScroll(w);
    }
}

/**
 *
 *  rct2: 0x006E1A25
 */
static void WindowSceneryMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
    switch (widgetIndex)
    {
        case WIDX_SCENERY_PRIMARY_COLOUR_BUTTON:
            WindowDropdownShowColour(w, widget, w->colours[1], gWindowSceneryPrimaryColour);
            break;
        case WIDX_SCENERY_SECONDARY_COLOUR_BUTTON:
            WindowDropdownShowColour(w, widget, w->colours[1], gWindowScenerySecondaryColour);
            break;
        case WIDX_SCENERY_TERTIARY_COLOUR_BUTTON:
            WindowDropdownShowColour(w, widget, w->colours[1], gWindowSceneryTertiaryColour);
            break;
    }

    if (widgetIndex >= WIDX_SCENERY_TAB_1)
    {
        gWindowSceneryActiveTabIndex = widgetIndex - WIDX_SCENERY_TAB_1;
        w->Invalidate();
        gSceneryPlaceCost = MONEY32_UNDEFINED;

        WindowSceneryUpdateScroll(w);
    }
}

/**
 *
 *  rct2: 0x006E1A54
 */
static void WindowSceneryDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
    if (dropdownIndex == -1)
        return;

    if (widgetIndex == WIDX_SCENERY_PRIMARY_COLOUR_BUTTON)
    {
        gWindowSceneryPrimaryColour = static_cast<colour_t>(dropdownIndex);
    }
    else if (widgetIndex == WIDX_SCENERY_SECONDARY_COLOUR_BUTTON)
    {
        gWindowScenerySecondaryColour = static_cast<colour_t>(dropdownIndex);
    }
    else if (widgetIndex == WIDX_SCENERY_TERTIARY_COLOUR_BUTTON)
    {
        gWindowSceneryTertiaryColour = static_cast<colour_t>(dropdownIndex);
    }

    w->Invalidate();
}

static ScenerySelection GetSceneryIdByCursorPos(rct_window* w, const ScreenCoordsXY& screenCoords);

/**
 *
 *  rct2: 0x006E1B9F
 */
static void WindowSceneryPeriodicUpdate(rct_window* w)
{
    if (!_selectedScenery.IsUndefined())
    {
        // Find out what scenery the cursor is over
        const CursorState* state = context_get_cursor_state();
        rct_widgetindex widgetIndex = window_find_widget_from_point(w, state->position);
        if (widgetIndex == WIDX_SCENERY_LIST)
        {
            ScreenCoordsXY scrollPos = {};
            int32_t scrollArea = 0;
            int32_t scrollId = 0;
            WidgetScrollGetPart(w, &w->widgets[WIDX_SCENERY_LIST], state->position, scrollPos, &scrollArea, &scrollId);
            if (scrollArea == SCROLL_PART_VIEW)
            {
                const ScenerySelection scenery = GetSceneryIdByCursorPos(w, scrollPos);
                if (scenery == _selectedScenery)
                {
                    return;
                }
            }
        }

        // Cursor was not over the currently hover selected scenery so reset hover selection.
        // This will happen when the mouse leaves the scroll window and is required so that the cost and description switch to
        // the tool scenery selection.
        _selectedScenery = {};
    }
}

/**
 *
 *  rct2: 0x006E1CD3
 */
static void WindowSceneryUpdate(rct_window* w)
{
    const CursorState* state = context_get_cursor_state();
    rct_window* other = window_find_from_point(state->position);
    if (other == w)
    {
        ScreenCoordsXY window = state->position - ScreenCoordsXY{ w->windowPos.x - 26, w->windowPos.y };

        if (window.y < 44 || window.x <= w->width)
        {
            rct_widgetindex widgetIndex = window_find_widget_from_point(w, state->position);
            if (widgetIndex >= WIDX_SCENERY_TAB_CONTENT_PANEL)
            {
                _hoverCounter++;
                if (_hoverCounter < 8)
                {
                    if (input_get_state() != InputState::ScrollLeft)
                    {
                        w->min_width = WINDOW_SCENERY_WIDTH;
                        w->max_width = WINDOW_SCENERY_WIDTH;
                        w->min_height = WINDOW_SCENERY_HEIGHT;
                        w->max_height = WINDOW_SCENERY_HEIGHT;
                    }
                }
                else
                {
                    const auto& listWidget = w->widgets[WIDX_SCENERY_LIST];
                    const auto nonListHeight = w->height - listWidget.height() + 2;

                    const auto numRows = static_cast<int32_t>(WindowSceneryCountRows(w));
                    const auto maxContentHeight = numRows * SCENERY_BUTTON_HEIGHT;
                    const auto maxWindowHeight = maxContentHeight + nonListHeight;
                    const auto windowHeight = std::clamp(maxWindowHeight, WINDOW_SCENERY_HEIGHT, 463);

                    w->min_width = WINDOW_SCENERY_WIDTH;
                    w->max_width = WINDOW_SCENERY_WIDTH;
                    w->min_height = windowHeight;
                    w->max_height = windowHeight;
                }
            }
        }
    }
    else
    {
        _hoverCounter = 0;
        if (input_get_state() != InputState::ScrollLeft)
        {
            w->min_width = WINDOW_SCENERY_WIDTH;
            w->max_width = WINDOW_SCENERY_WIDTH;
            w->min_height = WINDOW_SCENERY_HEIGHT;
            w->max_height = WINDOW_SCENERY_HEIGHT;
        }
    }

    w->Invalidate();

    if (!scenery_tool_is_active())
    {
        window_close(w);
        return;
    }

    if (gWindowSceneryEyedropperEnabled)
    {
        gCurrentToolId = Tool::Crosshair;
    }
    else if (gWindowSceneryPaintEnabled == 1)
    {
        gCurrentToolId = Tool::PaintDown;
    }
    else
    {
        const auto tabIndex = gWindowSceneryActiveTabIndex;
        const auto tabSelectedScenery = GetSelectedScenery(tabIndex);
        if (!tabSelectedScenery.IsUndefined())
        {
            if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER)
            {
                gCurrentToolId = Tool::EntranceDown;
            }
            else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE)
            {
                gCurrentToolId = static_cast<Tool>(get_large_scenery_entry(tabSelectedScenery.EntryIndex)->tool_id);
            }
            else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL)
            {
                gCurrentToolId = static_cast<Tool>(get_wall_entry(tabSelectedScenery.EntryIndex)->tool_id);
            }
            else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_PATH_ITEM)
            { // path bit
                gCurrentToolId = static_cast<Tool>(get_footpath_item_entry(tabSelectedScenery.EntryIndex)->tool_id);
            }
            else
            { // small scenery
                gCurrentToolId = static_cast<Tool>(get_small_scenery_entry(tabSelectedScenery.EntryIndex)->tool_id);
            }
        }
    }
}

/**
 *
 *  rct2: 0x006E1A91
 */
void WindowSceneryScrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
{
    auto rows = WindowSceneryCountRows(w);
    *height = WindowSceneryRowsHeight(rows);
}

static ScenerySelection GetSceneryIdByCursorPos(rct_window* w, const ScreenCoordsXY& screenCoords)
{
    ScenerySelection scenery{};

    const auto numColumns = WindowSceneryGetNumColumns(w);
    const auto colIndex = screenCoords.x / SCENERY_BUTTON_WIDTH;
    const auto rowIndex = screenCoords.y / SCENERY_BUTTON_HEIGHT;
    if (colIndex >= 0 && colIndex < numColumns && rowIndex >= 0)
    {
        const auto tabSceneryIndex = static_cast<size_t>((rowIndex * numColumns) + colIndex);
        const auto tabIndex = gWindowSceneryActiveTabIndex;
        if (tabIndex < _tabEntries.size())
        {
            auto& tabInfo = _tabEntries[tabIndex];
            if (tabSceneryIndex < tabInfo.Entries.size())
            {
                return tabInfo.Entries[tabSceneryIndex];
            }
        }
    }
    return scenery;
}

/**
 *
 *  rct2: 0x006E1C4A
 */
void WindowSceneryScrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
{
    const auto scenery = GetSceneryIdByCursorPos(w, screenCoords);
    if (scenery.IsUndefined())
        return;

    SetSelectedScenery(gWindowSceneryActiveTabIndex, scenery);

    gWindowSceneryPaintEnabled &= 0xFE;
    gWindowSceneryEyedropperEnabled = false;
    OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, w->windowPos.x + (w->width / 2));
    _hoverCounter = -16;
    gSceneryPlaceCost = MONEY32_UNDEFINED;
    w->Invalidate();
}

/**
 *
 *  rct2: 0x006E1BB8
 */
void WindowSceneryScrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
{
    ScenerySelection scenery = GetSceneryIdByCursorPos(w, screenCoords);
    if (!scenery.IsUndefined())
    {
        _selectedScenery = scenery;
        w->Invalidate();
    }
}

/**
 *
 *  rct2: 0x006E1C05
 */
OpenRCT2String WindowSceneryTooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback)
{
    if (widgetIndex >= WIDX_SCENERY_TAB_1)
    {
        const auto tabIndex = static_cast<size_t>(widgetIndex - WIDX_SCENERY_TAB_1);
        if (_tabEntries.size() > tabIndex)
        {
            const auto& tabInfo = _tabEntries[tabIndex];
            if (tabInfo.IsMisc())
            {
                auto ft = Formatter();
                ft.Add<rct_string_id>(STR_MISCELLANEOUS);
                return { fallback, ft };
            }

            const auto* sceneryEntry = tabInfo.GetSceneryGroupEntry();
            if (sceneryEntry != nullptr)
            {
                auto ft = Formatter();
                ft.Add<rct_string_id>(sceneryEntry->name);
                return { fallback, ft };
            }
        }
    }
    return { STR_NONE, Formatter() };
}

/**
 *
 *  rct2: 0x006E118B
 */
void WindowSceneryInvalidate(rct_window* w)
{
    // Set the window title
    rct_string_id titleStringId = STR_MISCELLANEOUS;
    const auto tabIndex = gWindowSceneryActiveTabIndex;
    if (tabIndex < _tabEntries.size())
    {
        const auto& tabInfo = _tabEntries[tabIndex];
        const auto* sgEntry = tabInfo.GetSceneryGroupEntry();
        if (sgEntry != nullptr)
        {
            titleStringId = sgEntry->name;
        }
    }
    w->widgets[WIDX_SCENERY_TITLE].text = titleStringId;

    w->pressed_widgets = 0;
    w->pressed_widgets |= 1ULL << (tabIndex + WIDX_SCENERY_TAB_1);
    if (gWindowSceneryPaintEnabled == 1)
        w->pressed_widgets |= (1ULL << WIDX_SCENERY_REPAINT_SCENERY_BUTTON);
    if (gWindowSceneryEyedropperEnabled)
        w->pressed_widgets |= (1ULL << WIDX_SCENERY_EYEDROPPER_BUTTON);
    if (gWindowSceneryScatterEnabled)
        w->pressed_widgets |= (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON);

    w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty;
    w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::Empty;
    w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::Empty;

    if (!(gWindowSceneryPaintEnabled & 1))
    {
        w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::FlatBtn;
    }

    const auto tabSelectedScenery = GetSelectedScenery(tabIndex);
    if (!tabSelectedScenery.IsUndefined())
    {
        if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL)
        {
            if (!(gWindowSceneryPaintEnabled & 1))
            {
                w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::FlatBtn;
            }

            auto* sceneryEntry = get_small_scenery_entry(tabSelectedScenery.EntryIndex);
            if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE))
            {
                w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn;
            }
        }
        else if (tabSelectedScenery.SceneryType >= SCENERY_TYPE_LARGE)
        {
            w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn;
        }
    }

    w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryPrimaryColour)
        | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN;
    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowScenerySecondaryColour)
        | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN;
    w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryTertiaryColour)
        | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN;

    w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty;
    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::Empty;
    w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::Empty;

    if (gWindowSceneryPaintEnabled & 1)
    { // repaint coloured scenery tool is on
        w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
        w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
        w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
        w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty;
    }
    else if (!tabSelectedScenery.IsUndefined())
    {
        if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER)
        {
            auto* bannerEntry = get_banner_entry(tabSelectedScenery.EntryIndex);
            if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR)
            {
                w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
            }
        }
        else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE)
        {
            auto* sceneryEntry = get_large_scenery_entry(tabSelectedScenery.EntryIndex);

            if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR)
                w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
            if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR)
                w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
        }
        else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL)
        {
            auto* wallEntry = get_wall_entry(tabSelectedScenery.EntryIndex);
            if (wallEntry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS))
            {
                w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;

                if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR)
                {
                    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;

                    if (wallEntry->flags2 & WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR)
                        w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty;
                    if (wallEntry->flags & WALL_SCENERY_HAS_TERNARY_COLOUR)
                        w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
                }
            }
        }
        else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL)
        {
            auto* sceneryEntry = get_small_scenery_entry(tabSelectedScenery.EntryIndex);

            if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS))
            {
                w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;

                if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR))
                    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn;
            }
        }
    }

    auto windowWidth = w->width;
    if (_tabEntries.size() > 0)
    {
        const auto lastTabIndex = _tabEntries.size() - 1;
        const auto lastTabWidget = &w->widgets[WIDX_SCENERY_TAB_1 + lastTabIndex];
        windowWidth = std::max<int32_t>(windowWidth, lastTabWidget->right + 3);
    }
    w->min_width = windowWidth;
    w->max_width = windowWidth;

    w->widgets[WIDX_SCENERY_BACKGROUND].right = windowWidth - 1;
    w->widgets[WIDX_SCENERY_BACKGROUND].bottom = w->height - 1;
    w->widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].right = windowWidth - 1;
    w->widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].bottom = w->height - 1;
    w->widgets[WIDX_SCENERY_TITLE].right = windowWidth - 2;
    w->widgets[WIDX_SCENERY_CLOSE].left = windowWidth - 13;
    w->widgets[WIDX_SCENERY_CLOSE].right = w->widgets[WIDX_SCENERY_CLOSE].left + 10;
    w->widgets[WIDX_SCENERY_LIST].right = windowWidth - 26;
    w->widgets[WIDX_SCENERY_LIST].bottom = w->height - 14;

    w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].left = windowWidth - 25;
    w->widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].left = windowWidth - 25;
    w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].left = windowWidth - 25;
    w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].left = windowWidth - 25;
    w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].right = windowWidth - 2;
    w->widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].right = windowWidth - 2;
    w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].right = windowWidth - 2;
    w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].right = windowWidth - 2;

    w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].left = windowWidth - 19;
    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].left = windowWidth - 19;
    w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].left = windowWidth - 19;
    w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].right = windowWidth - 8;
    w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].right = windowWidth - 8;
    w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].right = windowWidth - 8;
}

static std::pair<rct_string_id, money32> WindowSceneryGetNameAndPrice(ScenerySelection selectedScenery)
{
    rct_string_id name = STR_UNKNOWN_OBJECT_TYPE;
    money32 price = MONEY32_UNDEFINED;
    if (selectedScenery.IsUndefined() && gSceneryPlaceCost != MONEY32_UNDEFINED)
    {
        price = gSceneryPlaceCost;
    }
    else
    {
        switch (selectedScenery.SceneryType)
        {
            case SCENERY_TYPE_SMALL:
            {
                auto* sceneryEntry = get_small_scenery_entry(selectedScenery.EntryIndex);
                if (sceneryEntry != nullptr)
                {
                    price = sceneryEntry->price * 10;
                    name = sceneryEntry->name;
                }
                break;
            }
            case SCENERY_TYPE_PATH_ITEM:
            {
                auto* sceneryEntry = get_footpath_item_entry(selectedScenery.EntryIndex);
                if (sceneryEntry != nullptr)
                {
                    price = sceneryEntry->price;
                    name = sceneryEntry->name;
                }
                break;
            }
            case SCENERY_TYPE_WALL:
            {
                auto* sceneryEntry = get_wall_entry(selectedScenery.EntryIndex);
                if (sceneryEntry != nullptr)
                {
                    price = sceneryEntry->price;
                    name = sceneryEntry->name;
                }
                break;
            }
            case SCENERY_TYPE_LARGE:
            {
                auto* sceneryEntry = get_large_scenery_entry(selectedScenery.EntryIndex);
                if (sceneryEntry != nullptr)
                {
                    price = sceneryEntry->price * 10;
                    name = sceneryEntry->name;
                }
                break;
            }
            case SCENERY_TYPE_BANNER:
            {
                auto* sceneryEntry = get_banner_entry(selectedScenery.EntryIndex);
                if (sceneryEntry != nullptr)
                {
                    price = sceneryEntry->price;
                    name = sceneryEntry->name;
                }
                break;
            }
        }
    }
    return { name, price };
}

/**
 *
 *  rct2: 0x006E1462
 */
void WindowSceneryPaint(rct_window* w, rct_drawpixelinfo* dpi)
{
    WindowDrawWidgets(w, dpi);

    const auto tabIndex = gWindowSceneryActiveTabIndex;
    if (tabIndex < _tabEntries.size())
    {
        auto selectedWidgetId = static_cast<rct_widgetindex>(WIDX_SCENERY_TAB_1 + tabIndex);
        uint32_t imageId = ((w->colours[1] << 19) | w->widgets[selectedWidgetId].image) + 1ul;

        gfx_draw_sprite(
            dpi, imageId, w->windowPos + ScreenCoordsXY{ w->widgets[selectedWidgetId].left, w->widgets[selectedWidgetId].top },
            selectedWidgetId);
    }

    auto selectedSceneryEntry = _selectedScenery;
    if (selectedSceneryEntry.IsUndefined())
    {
        if (gWindowSceneryPaintEnabled & 1) // repaint coloured scenery tool is on
            return;
        if (gWindowSceneryEyedropperEnabled)
            return;

        selectedSceneryEntry = GetSelectedScenery(tabIndex);
        if (selectedSceneryEntry.IsUndefined())
            return;
    }

    auto [name, price] = WindowSceneryGetNameAndPrice(selectedSceneryEntry);
    if (price != MONEY32_UNDEFINED && !(gParkFlags & PARK_FLAGS_NO_MONEY))
    {
        auto ft = Formatter();
        ft.Add<money64>(price);

        // -14
        DrawTextBasic(
            dpi, w->windowPos + ScreenCoordsXY{ w->width - 0x1A, w->height - 13 }, STR_COST_LABEL, ft,
            { TextAlignment::RIGHT });
    }

    auto ft = Formatter();
    ft.Add<rct_string_id>(name);
    DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + w->height - 13 }, w->width - 19, STR_BLACK_STRING, ft);
}

static void WindowSceneryScrollpaintItem(rct_window* w, rct_drawpixelinfo* dpi, ScenerySelection scenerySelection)
{
    if (scenerySelection.SceneryType == SCENERY_TYPE_BANNER)
    {
        auto bannerEntry = get_banner_entry(scenerySelection.EntryIndex);
        uint32_t imageId = bannerEntry->image + gWindowSceneryRotation * 2;
        imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP;

        gfx_draw_sprite(dpi, imageId, { 0x21, 0x28 }, w->colours[1]);
        gfx_draw_sprite(dpi, imageId + 1, { 0x21, 0x28 }, w->colours[1]);
    }
    else if (scenerySelection.SceneryType == SCENERY_TYPE_LARGE)
    {
        auto sceneryEntry = get_large_scenery_entry(scenerySelection.EntryIndex);
        uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation;
        imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP;
        imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS;

        gfx_draw_sprite(dpi, imageId, { 0x21, 0 }, w->colours[1]);
    }
    else if (scenerySelection.SceneryType == SCENERY_TYPE_WALL)
    {
        auto wallEntry = get_wall_entry(scenerySelection.EntryIndex);
        uint32_t imageId = wallEntry->image;
        uint8_t tertiaryColour = w->colours[1];
        uint16_t spriteTop = (wallEntry->height * 2) + 0x32;

        if (wallEntry->flags & WALL_SCENERY_HAS_GLASS)
        {
            imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP;

            if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR)
            {
                imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS;
            }
            gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour);

            imageId = (wallEntry->image + 0x40000006) | (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19);
            gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour);
        }
        else
        {
            imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP;

            if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR)
            {
                imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS;

                if (wallEntry->flags & WALL_SCENERY_HAS_TERNARY_COLOUR)
                {
                    imageId &= 0xDFFFFFFF;
                    tertiaryColour = gWindowSceneryTertiaryColour;
                }
            }
            gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour);

            if (wallEntry->flags & WALL_SCENERY_IS_DOOR)
            {
                gfx_draw_sprite(dpi, imageId + 1, { 0x2F, spriteTop }, tertiaryColour);
            }
        }
    }
    else if (scenerySelection.SceneryType == SCENERY_TYPE_PATH_ITEM)
    {
        auto* pathBitEntry = get_footpath_item_entry(scenerySelection.EntryIndex);
        uint32_t imageId = pathBitEntry->image;

        gfx_draw_sprite(dpi, imageId, { 0x0B, 0x10 }, w->colours[1]);
    }
    else
    {
        auto sceneryEntry = get_small_scenery_entry(scenerySelection.EntryIndex);
        uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation;

        if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR))
        {
            imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP;

            if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR))
            {
                imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS;
            }
        }

        uint16_t spriteTop = (sceneryEntry->height / 4) + 0x2B;

        if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE))
        {
            spriteTop -= 0x0C;
        }

        gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]);

        if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_GLASS))
        {
            imageId = ((sceneryEntry->image + gWindowSceneryRotation) + 0x40000004)
                + (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19);

            gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]);
        }

        if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED_FG))
        {
            imageId = (sceneryEntry->image + gWindowSceneryRotation) + 4;
            gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]);
        }
    }
}

void WindowSceneryScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
{
    gfx_clear(dpi, ColourMapA[w->colours[1]].mid_light);

    auto numColumns = WindowSceneryGetNumColumns(w);
    auto tabIndex = gWindowSceneryActiveTabIndex;
    if (tabIndex > _tabEntries.size())
    {
        return;
    }

    ScreenCoordsXY topLeft{ 0, 0 };

    const auto& tabInfo = _tabEntries[tabIndex];
    for (size_t sceneryTabItemIndex = 0; sceneryTabItemIndex < tabInfo.Entries.size(); sceneryTabItemIndex++)
    {
        const auto& currentSceneryGlobal = tabInfo.Entries[sceneryTabItemIndex];
        const auto tabSelectedScenery = GetSelectedScenery(tabIndex);
        if (gWindowSceneryPaintEnabled == 1 || gWindowSceneryEyedropperEnabled)
        {
            if (_selectedScenery == currentSceneryGlobal)
            {
                gfx_fill_rect_inset(
                    dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } },
                    w->colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT);
            }
        }
        else
        {
            if (tabSelectedScenery == currentSceneryGlobal)
            {
                gfx_fill_rect_inset(
                    dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } },
                    w->colours[1], (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_MID_LIGHT));
            }
            else if (_selectedScenery == currentSceneryGlobal)
            {
                gfx_fill_rect_inset(
                    dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } },
                    w->colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT);
            }
        }

        rct_drawpixelinfo clipdpi;
        if (clip_drawpixelinfo(
                &clipdpi, dpi, topLeft + ScreenCoordsXY{ 1, 1 }, SCENERY_BUTTON_WIDTH - 2, SCENERY_BUTTON_HEIGHT - 2))
        {
            WindowSceneryScrollpaintItem(w, &clipdpi, currentSceneryGlobal);
        }

        topLeft.x += SCENERY_BUTTON_WIDTH;
        if (topLeft.x >= numColumns * SCENERY_BUTTON_WIDTH)
        {
            topLeft.y += SCENERY_BUTTON_HEIGHT;
            topLeft.x = 0;
        }
    }
}

bool WindowScenerySetSelectedItem(const ScenerySelection& scenery)
{
    bool result = false;
    rct_window* w = window_bring_to_front_by_class(WC_SCENERY);
    if (w != nullptr)
    {
        auto tabIndex = WindowSceneryFindTabWithScenery(scenery);
        if (tabIndex)
        {
            gWindowSceneryActiveTabIndex = *tabIndex;
            SetSelectedScenery(*tabIndex, scenery);

            OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, context_get_width() / 2);
            _hoverCounter = -16;
            gSceneryPlaceCost = MONEY32_UNDEFINED;
            w->Invalidate();
            result = true;
        }
    }
    return result;
}

// Used after removing objects, in order to avoid crashes.
void WindowSceneryResetSelectedSceneryItems()
{
    gWindowSceneryTabSelections.clear();
}
