/*****************************************************************************
 * 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 <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/ParkSetResearchFundingAction.h>
#include <openrct2/localisation/Formatter.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/management/Finance.h>
#include <openrct2/management/NewsItem.h>
#include <openrct2/management/Research.h>
#include <openrct2/ride/RideData.h>
#include <openrct2/sprites.h>
#include <openrct2/world/Park.h>
#include <openrct2/world/Scenery.h>

static constexpr const int32_t WH_DEVELOPMENT = 196;
static constexpr const int32_t WW_DEVELOPMENT = 300;
static constexpr const int32_t WH_FUNDING = 207;
static constexpr const int32_t WW_FUNDING = 320;

// clang-format off
enum {
    WINDOW_RESEARCH_PAGE_DEVELOPMENT,
    WINDOW_RESEARCH_PAGE_FUNDING,
    WINDOW_RESEARCH_PAGE_COUNT
};

enum {
    WIDX_BACKGROUND,
    WIDX_TITLE,
    WIDX_CLOSE,
    WIDX_PAGE_BACKGROUND,
    WIDX_TAB_1,
    WIDX_TAB_2,

    WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP,
    WIDX_LAST_DEVELOPMENT_GROUP,
    WIDX_LAST_DEVELOPMENT_BUTTON,

    WIDX_FUNDING_GROUP = 6,
    WIDX_RESEARCH_FUNDING,
    WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON,
    WIDX_PRIORITIES_GROUP,
    WIDX_TRANSPORT_RIDES,
    WIDX_GENTLE_RIDES,
    WIDX_ROLLER_COASTERS,
    WIDX_THRILL_RIDES,
    WIDX_WATER_RIDES,
    WIDX_SHOPS_AND_STALLS,
    WIDX_SCENERY_AND_THEMING,
};

#pragma region Widgets

static rct_widget window_research_development_widgets[] = {
    WINDOW_SHIM(STR_RESEARCH_AND_DEVELOPMENT, WW_DEVELOPMENT, WH_DEVELOPMENT),
    MakeWidget({  0,  43}, {     WW_DEVELOPMENT, 153}, WindowWidgetType::Resize,   WindowColour::Secondary                                                                ),
    MakeTab   ({  3,  17},                                                                                                  STR_RESEARCH_AND_DEVELOPMENT_TIP),
    MakeTab   ({ 34,  17},                                                                                                  STR_FINANCES_RESEARCH_TIP       ),
    MakeWidget({  3,  47}, {WW_DEVELOPMENT - 10,  70}, WindowWidgetType::Groupbox, WindowColour::Tertiary , STR_CURRENTLY_IN_DEVELOPMENT                                  ),
    MakeWidget({  3, 124}, {WW_DEVELOPMENT - 10,  65}, WindowWidgetType::Groupbox, WindowColour::Tertiary , STR_LAST_DEVELOPMENT                                          ),
    MakeWidget({265, 161}, {                 24,  24}, WindowWidgetType::FlatBtn,  WindowColour::Tertiary , 0xFFFFFFFF,                   STR_RESEARCH_SHOW_DETAILS_TIP   ),
    WIDGETS_END,
};

static rct_widget window_research_funding_widgets[] = {
    WINDOW_SHIM(STR_RESEARCH_FUNDING, WW_FUNDING, WH_FUNDING),
    MakeWidget({  0,  43}, {     WW_FUNDING, 164}, WindowWidgetType::Resize,   WindowColour::Secondary                                                                                    ),
    MakeTab   ({  3,  17},                                                                                                      STR_RESEARCH_AND_DEVELOPMENT_TIP            ),
    MakeTab   ({ 34,  17},                                                                                                      STR_FINANCES_RESEARCH_TIP                   ),
    MakeWidget({  3,  47}, { WW_FUNDING - 6,  45}, WindowWidgetType::Groupbox, WindowColour::Tertiary , STR_RESEARCH_FUNDING_                                                             ),
    MakeWidget({  8,  59}, {            160,  14}, WindowWidgetType::DropdownMenu, WindowColour::Tertiary , 0xFFFFFFFF,                           STR_SELECT_LEVEL_OF_RESEARCH_AND_DEVELOPMENT),
    MakeWidget({156,  60}, {             11,  12}, WindowWidgetType::Button,   WindowColour::Tertiary , STR_DROPDOWN_GLYPH,                   STR_SELECT_LEVEL_OF_RESEARCH_AND_DEVELOPMENT),
    MakeWidget({  3,  96}, { WW_FUNDING - 6, 107}, WindowWidgetType::Groupbox, WindowColour::Tertiary , STR_RESEARCH_PRIORITIES                                                           ),
    MakeWidget({  8, 108}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_TRANSPORT_RIDES,     STR_RESEARCH_NEW_TRANSPORT_RIDES_TIP        ),
    MakeWidget({  8, 121}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_GENTLE_RIDES,        STR_RESEARCH_NEW_GENTLE_RIDES_TIP           ),
    MakeWidget({  8, 134}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_ROLLER_COASTERS,     STR_RESEARCH_NEW_ROLLER_COASTERS_TIP        ),
    MakeWidget({  8, 147}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_THRILL_RIDES,        STR_RESEARCH_NEW_THRILL_RIDES_TIP           ),
    MakeWidget({  8, 160}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_WATER_RIDES,         STR_RESEARCH_NEW_WATER_RIDES_TIP            ),
    MakeWidget({  8, 173}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_SHOPS_AND_STALLS,    STR_RESEARCH_NEW_SHOPS_AND_STALLS_TIP       ),
    MakeWidget({  8, 186}, {WW_FUNDING - 16,  12}, WindowWidgetType::Checkbox, WindowColour::Tertiary , STR_RESEARCH_NEW_SCENERY_AND_THEMING, STR_RESEARCH_NEW_SCENERY_AND_THEMING_TIP    ),
    WIDGETS_END,
};

static rct_widget *window_research_page_widgets[] = {
    window_research_development_widgets,
    window_research_funding_widgets,
};

#pragma endregion

#pragma region Events

static void WindowResearchDevelopmentMouseup(rct_window *w, rct_widgetindex widgetIndex);
static void WindowResearchDevelopmentUpdate(rct_window *w);
static void WindowResearchDevelopmentInvalidate(rct_window *w);
static void WindowResearchDevelopmentPaint(rct_window *w, rct_drawpixelinfo *dpi);

static void WindowResearchFundingMouseup(rct_window *w, rct_widgetindex widgetIndex);
static void WindowResearchFundingMousedown(rct_window *w, rct_widgetindex widgetIndex, rct_widget* widget);
static void WindowResearchFundingDropdown(rct_window *w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
static void WindowResearchFundingUpdate(rct_window *w);
static void WindowResearchFundingInvalidate(rct_window *w);
static void WindowResearchFundingPaint(rct_window *w, rct_drawpixelinfo *dpi);

//
static rct_window_event_list window_research_development_events([](auto& events)
{
    events.mouse_up = &WindowResearchDevelopmentMouseup;
    events.update = &WindowResearchDevelopmentUpdate;
    events.invalidate = &WindowResearchDevelopmentInvalidate;
    events.paint = &WindowResearchDevelopmentPaint;
});

// 0x009890E8
static rct_window_event_list window_research_funding_events([](auto& events)
{
    events.mouse_up = &WindowResearchFundingMouseup;
    events.mouse_down = &WindowResearchFundingMousedown;
    events.dropdown = &WindowResearchFundingDropdown;
    events.update = &WindowResearchFundingUpdate;
    events.invalidate = &WindowResearchFundingInvalidate;
    events.paint = &WindowResearchFundingPaint;
});

static rct_window_event_list *window_research_page_events[] = {
    &window_research_development_events,
    &window_research_funding_events,
};

#pragma endregion

// clang-format on

const int32_t window_research_tab_animation_loops[] = {
    16,
    16,
};

static constexpr const rct_string_id ResearchStageNames[] = {
    STR_RESEARCH_STAGE_INITIAL_RESEARCH,
    STR_RESEARCH_STAGE_DESIGNING,
    STR_RESEARCH_STAGE_COMPLETING_DESIGN,
    STR_RESEARCH_STAGE_UNKNOWN,
};

static void WindowResearchSetPage(rct_window* w, int32_t page);
static void WindowResearchSetPressedTab(rct_window* w);
static void WindowResearchDrawTabImages(rct_drawpixelinfo* dpi, rct_window* w);

rct_window* WindowResearchOpen()
{
    rct_window* w;

    w = window_bring_to_front_by_class(WC_RESEARCH);
    if (w == nullptr)
    {
        w = WindowCreateAutoPos(WW_FUNDING, WH_FUNDING, window_research_page_events[0], WC_RESEARCH, WF_10);
        w->widgets = window_research_page_widgets[0];
        w->number = 0;
        w->page = 0;
        w->frame_no = 0;
        w->disabled_widgets = 0;
        ResearchUpdateUncompletedTypes();
    }

    w->page = 0;
    w->Invalidate();
    w->width = WW_DEVELOPMENT;
    w->height = WH_DEVELOPMENT;
    w->Invalidate();

    w->widgets = window_research_page_widgets[0];
    w->hold_down_widgets = 0;
    w->event_handlers = window_research_page_events[0];
    w->pressed_widgets = 0;
    w->disabled_widgets = 0;
    WindowInitScrollWidgets(w);

    return w;
}

#pragma region Development page

/**
 *
 *  rct2: 0x006B6B38
 */
static void WindowResearchDevelopmentMouseup(rct_window* w, rct_widgetindex widgetIndex)
{
    switch (widgetIndex)
    {
        case WIDX_CLOSE:
            window_close(w);
            break;
        case WIDX_TAB_1:
        case WIDX_TAB_2:
            WindowResearchSetPage(w, widgetIndex - WIDX_TAB_1);
            break;
        case WIDX_LAST_DEVELOPMENT_BUTTON:
            News::OpenSubject(News::ItemType::Research, gResearchLastItem->rawValue);
            break;
    }
}

/**
 *
 *  rct2: 0x0069CBA6
 */
static void WindowResearchDevelopmentUpdate(rct_window* w)
{
    // Tab animation
    if (++w->frame_no >= window_research_tab_animation_loops[w->page])
        w->frame_no = 0;
    widget_invalidate(w, WIDX_TAB_1);
}

/**
 *
 *  rct2: 0x006B6819
 */
static void WindowResearchDevelopmentInvalidate(rct_window* w)
{
    if (w->widgets != window_research_page_widgets[WINDOW_RESEARCH_PAGE_DEVELOPMENT])
    {
        w->widgets = window_research_page_widgets[WINDOW_RESEARCH_PAGE_DEVELOPMENT];
        WindowInitScrollWidgets(w);
    }

    WindowResearchSetPressedTab(w);

    window_research_development_widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::Empty;
    if (gResearchLastItem.has_value())
    {
        auto type = gResearchLastItem->type;
        window_research_development_widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::FlatBtn;
        window_research_development_widgets[WIDX_LAST_DEVELOPMENT_BUTTON].image = type == Research::EntryType::Ride
            ? SPR_NEW_RIDE
            : SPR_NEW_SCENERY;
    }
}

/**
 *
 *  rct2: 0x006B689B
 */
static void WindowResearchDevelopmentPaint(rct_window* w, rct_drawpixelinfo* dpi)
{
    WindowDrawWidgets(w, dpi);
    WindowResearchDrawTabImages(dpi, w);

    WindowResearchDevelopmentPagePaint(w, dpi, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP);
}

void WindowResearchDevelopmentPagePaint(rct_window* w, rct_drawpixelinfo* dpi, rct_widgetindex baseWidgetIndex)
{
    baseWidgetIndex = baseWidgetIndex - WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP;

    auto screenCoords = w->windowPos
        + ScreenCoordsXY{ 10, w->widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP + baseWidgetIndex].top + 12 };

    if (gResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL)
    {
        // Research type
        auto ft = Formatter();
        ft.Add<rct_string_id>(STR_RESEARCH_UNKNOWN);
        DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_TYPE_LABEL, ft);
        screenCoords.y += 25;

        // Progress
        ft = Formatter();
        ft.Add<rct_string_id>(STR_RESEARCH_COMPLETED_AL);
        DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft);
        screenCoords.y += 15;

        // Expected
        ft = Formatter();
        ft.Add<rct_string_id>(STR_RESEARCH_STAGE_UNKNOWN);
        DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft);
    }
    else
    {
        // Research type
        auto ft = Formatter();
        rct_string_id label = STR_RESEARCH_TYPE_LABEL;
        if (gResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH)
        {
            ft.Add<rct_string_id>(STR_RESEARCH_UNKNOWN);
        }
        else if (gResearchProgressStage == RESEARCH_STAGE_DESIGNING)
        {
            ft.Add<rct_string_id>(gResearchNextItem->GetCategoryName());
        }
        else if (gResearchNextItem->type == Research::EntryType::Ride)
        {
            const auto& rtd = GetRideTypeDescriptor(gResearchNextItem->baseRideType);
            if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY))
            {
                ft.Add<rct_string_id>(gResearchNextItem->GetName());
            }
            else if (gResearchNextItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE)
            {
                ft.Add<rct_string_id>(rtd.Naming.Name);
            }
            else
            {
                ft.Add<rct_string_id>(gResearchNextItem->GetName());
                ft.Add<rct_string_id>(rtd.Naming.Name);
                label = STR_RESEARCH_TYPE_LABEL_VEHICLE;
            }
        }
        else
        {
            ft.Add<rct_string_id>(gResearchNextItem->GetName());
        }
        DrawTextWrapped(dpi, screenCoords, 296, label, ft);
        screenCoords.y += 25;

        // Progress
        ft = Formatter();
        ft.Add<rct_string_id>(ResearchStageNames[gResearchProgressStage]);
        DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft);
        screenCoords.y += 15;

        // Expected
        ft = Formatter();
        if (gResearchProgressStage != RESEARCH_STAGE_INITIAL_RESEARCH && gResearchExpectedDay != 255)
        {
            // TODO: Should probably use game date format setting
            ft.Add<rct_string_id>(STR_RESEARCH_EXPECTED_FORMAT);
            ft.Add<rct_string_id>(DateDayNames[gResearchExpectedDay]);
            ft.Add<rct_string_id>(DateGameMonthNames[gResearchExpectedMonth]);
        }
        else
        {
            ft.Add<rct_string_id>(STR_RESEARCH_STAGE_UNKNOWN);
        }
        DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft);
    }

    // Last development
    screenCoords = w->windowPos + ScreenCoordsXY{ 10, w->widgets[WIDX_LAST_DEVELOPMENT_GROUP + baseWidgetIndex].top + 12 };

    if (gResearchLastItem.has_value())
    {
        rct_string_id lastDevelopmentFormat = STR_EMPTY;
        auto ft = Formatter();
        if (gResearchLastItem->type == Research::EntryType::Scenery)
        {
            lastDevelopmentFormat = STR_RESEARCH_SCENERY_LABEL;
            ft.Add<rct_string_id>(gResearchLastItem->GetName());
        }
        else
        {
            lastDevelopmentFormat = STR_RESEARCH_RIDE_LABEL;
            const auto& rtd = GetRideTypeDescriptor(gResearchLastItem->baseRideType);
            if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY))
            {
                ft.Add<rct_string_id>(gResearchLastItem->GetName());
            }
            else if (gResearchLastItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE)
            {
                ft.Add<rct_string_id>(rtd.Naming.Name);
            }
            else
            {
                ft.Add<rct_string_id>(gResearchLastItem->GetName());
                ft.Add<rct_string_id>(rtd.Naming.Name);
                lastDevelopmentFormat = STR_RESEARCH_VEHICLE_LABEL;
            }
        }

        DrawTextWrapped(dpi, screenCoords, 266, lastDevelopmentFormat, ft);
    }
}

#pragma endregion

#pragma region Funding page

/**
 *
 *  rct2: 0x0069DB3F
 */
static void WindowResearchFundingMouseup(rct_window* w, rct_widgetindex widgetIndex)
{
    switch (widgetIndex)
    {
        case WIDX_CLOSE:
            window_close(w);
            break;
        case WIDX_TAB_1:
        case WIDX_TAB_2:
            WindowResearchSetPage(w, widgetIndex - WIDX_TAB_1);
            break;
        case WIDX_TRANSPORT_RIDES:
        case WIDX_GENTLE_RIDES:
        case WIDX_ROLLER_COASTERS:
        case WIDX_THRILL_RIDES:
        case WIDX_WATER_RIDES:
        case WIDX_SHOPS_AND_STALLS:
        case WIDX_SCENERY_AND_THEMING:
        {
            auto activeResearchTypes = gResearchPriorities;
            activeResearchTypes ^= 1 << (widgetIndex - WIDX_TRANSPORT_RIDES);
            auto gameAction = ParkSetResearchFundingAction(activeResearchTypes, gResearchFundingLevel);
            GameActions::Execute(&gameAction);
            break;
        }
    }
}

/**
 *
 *  rct2: 0x0069DB66
 */
static void WindowResearchFundingMousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
    rct_widget* dropdownWidget;
    int32_t i;

    if (widgetIndex != WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON)
        return;

    dropdownWidget = widget - 1;

    for (i = 0; i < 4; i++)
    {
        gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
        gDropdownItems[i].Args = ResearchFundingLevelNames[i];
    }
    WindowDropdownShowTextCustomWidth(
        { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
        w->colours[1], 0, Dropdown::Flag::StayOpen, 4, dropdownWidget->width() - 3);

    int32_t currentResearchLevel = gResearchFundingLevel;
    Dropdown::SetChecked(currentResearchLevel, true);
}

/**
 *
 *  rct2: 0x0069DB6D
 */
static void WindowResearchFundingDropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
    if (widgetIndex != WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON || dropdownIndex == -1)
        return;

    auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, dropdownIndex);
    GameActions::Execute(&gameAction);
}

/**
 *
 *  rct2: 0x0069DC23
 */
static void WindowResearchFundingUpdate(rct_window* w)
{
    // Tab animation
    if (++w->frame_no >= window_research_tab_animation_loops[w->page])
        w->frame_no = 0;
    widget_invalidate(w, WIDX_TAB_2);
}

/**
 *
 *  rct2: 0x0069DA64
 */
static void WindowResearchFundingInvalidate(rct_window* w)
{
    if (w->widgets != window_research_page_widgets[WINDOW_RESEARCH_PAGE_FUNDING])
    {
        w->widgets = window_research_page_widgets[WINDOW_RESEARCH_PAGE_FUNDING];
        WindowInitScrollWidgets(w);
    }

    WindowResearchSetPressedTab(w);

    if ((gParkFlags & PARK_FLAGS_NO_MONEY) || (gResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL))
    {
        window_research_funding_widgets[WIDX_RESEARCH_FUNDING].type = WindowWidgetType::Empty;
        window_research_funding_widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON].type = WindowWidgetType::Empty;
    }
    else
    {
        window_research_funding_widgets[WIDX_FUNDING_GROUP].type = WindowWidgetType::Groupbox;
        window_research_funding_widgets[WIDX_RESEARCH_FUNDING].type = WindowWidgetType::DropdownMenu;
        window_research_funding_widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON].type = WindowWidgetType::Button;

        // Current funding
        int32_t currentResearchLevel = gResearchFundingLevel;
        window_research_funding_widgets[WIDX_RESEARCH_FUNDING].text = ResearchFundingLevelNames[currentResearchLevel];
    }

    // Checkboxes
    uint8_t activeResearchTypes = gResearchPriorities;
    for (int32_t i = 0; i < 7; i++)
    {
        int32_t mask = 1 << i;
        int32_t widgetMask = 1 << (i + WIDX_TRANSPORT_RIDES);

        // Set checkbox disabled if research type is complete
        if (gResearchUncompletedCategories & mask)
        {
            w->disabled_widgets &= ~widgetMask;

            // Set checkbox ticked if research type is active
            if (activeResearchTypes & mask)
                w->pressed_widgets |= widgetMask;
            else
                w->pressed_widgets &= ~widgetMask;
        }
        else
        {
            w->disabled_widgets |= widgetMask;
            w->pressed_widgets &= ~widgetMask;
        }
    }
}

/**
 *
 *  rct2: 0x0069DAF0
 */
static void WindowResearchFundingPaint(rct_window* w, rct_drawpixelinfo* dpi)
{
    WindowDrawWidgets(w, dpi);
    WindowResearchDrawTabImages(dpi, w);

    WindowResearchFundingPagePaint(w, dpi, WIDX_RESEARCH_FUNDING);
}

void WindowResearchFundingPagePaint(rct_window* w, rct_drawpixelinfo* dpi, rct_widgetindex baseWidgetIndex)
{
    if (gParkFlags & PARK_FLAGS_NO_MONEY)
        return;

    int32_t currentResearchLevel = gResearchFundingLevel;
    auto ft = Formatter();
    ft.Add<money64>(research_cost_table[currentResearchLevel]);
    DrawTextBasic(dpi, w->windowPos + ScreenCoordsXY{ 10, 77 }, STR_RESEARCH_COST_PER_MONTH, ft);
}

#pragma endregion

#pragma region Common

/**
 *
 *  rct2: 0x0069CAC5
 */
static void WindowResearchSetPage(rct_window* w, int32_t page)
{
    w->page = page;
    w->frame_no = 0;
    w->RemoveViewport();

    w->hold_down_widgets = 0;
    w->event_handlers = window_research_page_events[page];
    w->widgets = window_research_page_widgets[page];
    w->disabled_widgets = 0;
    w->pressed_widgets = 0;

    w->Invalidate();
    if (w->page == WINDOW_RESEARCH_PAGE_DEVELOPMENT)
    {
        w->width = WW_DEVELOPMENT;
        w->height = WH_DEVELOPMENT;
    }
    else
    {
        w->width = WW_FUNDING;
        w->height = WH_FUNDING;
    }
    window_event_resize_call(w);
    window_event_invalidate_call(w);

    WindowInitScrollWidgets(w);
    w->Invalidate();
}

static void WindowResearchSetPressedTab(rct_window* w)
{
    int32_t i;
    for (i = 0; i < WINDOW_RESEARCH_PAGE_COUNT; i++)
        w->pressed_widgets &= ~(1 << (WIDX_TAB_1 + i));
    w->pressed_widgets |= 1LL << (WIDX_TAB_1 + w->page);
}

static void WindowResearchDrawTabImage(rct_drawpixelinfo* dpi, rct_window* w, int32_t page, int32_t spriteIndex)
{
    rct_widgetindex widgetIndex = WIDX_TAB_1 + page;

    if (!WidgetIsDisabled(w, widgetIndex))
    {
        if (w->page == page)
        {
            int32_t frame = w->frame_no / 2;
            if (page == WINDOW_RESEARCH_PAGE_DEVELOPMENT)
                frame %= 8;
            spriteIndex += frame;
        }

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

static void WindowResearchDrawTabImages(rct_drawpixelinfo* dpi, rct_window* w)
{
    WindowResearchDrawTabImage(dpi, w, WINDOW_RESEARCH_PAGE_DEVELOPMENT, SPR_TAB_FINANCES_RESEARCH_0);
    WindowResearchDrawTabImage(dpi, w, WINDOW_RESEARCH_PAGE_FUNDING, SPR_TAB_FINANCES_SUMMARY_0);
}

#pragma endregion
