#include "df/global_objects.h"
#include "modules/Gui.h"
#include "modules/Screen.h"
#include "df/enabler.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_titlest.h"

struct dwarfmode_pausing_fps_counter_hook : df::viewscreen_dwarfmodest {
    typedef df::viewscreen_dwarfmodest interpose_base;

    static const uint32_t history_length = 3;

    // whether init.txt have [FPS:YES]
    static bool init_have_fps_yes()
    {
        static bool first = true;
        static bool init_have_fps_yes;

        if (first && df::global::gps)
        {
            // if first time called, then display_frames is set iff init.txt have [FPS:YES]
            first = false;
            init_have_fps_yes = (df::global::gps->display_frames == 1);
        }
        return init_have_fps_yes;
    }

    DEFINE_VMETHOD_INTERPOSE(void, render, ())
    {
        INTERPOSE_NEXT(render)();

        if (!df::global::pause_state || !df::global::enabler || !df::global::world
            || !df::global::gps || !df::global::pause_state)
            return;

        // if init.txt does not have [FPS:YES] then dont show this FPS counter
        if (!dwarfmode_pausing_fps_counter_hook::init_have_fps_yes())
            return;

        static bool prev_paused = true;
        static uint32_t prev_clock = 0;
        static int32_t prev_frames = 0;
        static uint32_t elapsed_clock = 0;
        static uint32_t elapsed_frames = 0;
        static double history[history_length];

        if (prev_clock == 0)
        {
            // init
            for (uint32_t i = 0; i < history_length; i++)
                history[i] = 0.0;
        }

        // disable default FPS counter because it is rendered on top of this FPS counter.
        if (df::global::gps->display_frames == 1)
            df::global::gps->display_frames = 0;

        if (*df::global::pause_state)
            prev_paused = true;
        else
        {
            uint32_t clock = df::global::enabler->clock;
            int32_t frames = df::global::world->frame_counter;

            if (!prev_paused && prev_clock != 0
                && clock >= prev_clock && frames >= prev_frames)
            {
                // if we were previously paused, then dont add clock/frames,
                // but wait for the next time render is called.
                elapsed_clock += clock - prev_clock;
                elapsed_frames += frames - prev_frames;
            }

            prev_paused = false;
            prev_clock = clock;
            prev_frames = frames;

            // add FPS to history every second or after at least one frame.
            if (elapsed_clock >= 1000 && elapsed_frames >= 1)
            {
                double fps = elapsed_frames / (elapsed_clock / 1000.0);
                for (int i = history_length - 1; i >= 1; i--)
                    history[i] = history[i - 1];
                history[0] = fps;

                elapsed_clock = 0;
                elapsed_frames = 0;
            }
        }

        // average fps over a few seconds to stabilize the counter.
        double fps_sum = 0.0;
        int fps_count = 0;
        for (uint32_t i = 0; i < history_length; i++)
        {
            if (history[i] > 0.0)
            {
                fps_sum += history[i];
                fps_count++;
            }
        }

        double fps = fps_count == 0 ? 1.0 : fps_sum / fps_count;
        double gfps = df::global::enabler->calculated_gfps;

        std::stringstream fps_counter;
        fps_counter << "FPS:"
                    << setw(4) << fixed << setprecision(fps >= 1.0 ? 0 : 2) << fps
                    << " (" << gfps << ")";

        // show this FPS counter same as the default counter.
        int x = 10;
        int y = 0;
        OutputString(COLOR_WHITE, x, y, fps_counter.str(),
                     false, 0, COLOR_CYAN, false);
    }
};

struct title_pausing_fps_counter_hook : df::viewscreen_titlest {
    typedef df::viewscreen_titlest interpose_base;

    DEFINE_VMETHOD_INTERPOSE(void, render, ())
    {
        INTERPOSE_NEXT(render)();

        // if init.txt have FPS:YES then enable default FPS counter when exiting dwarf mode.
        // So it is enabled if starting adventure mode without exiting dwarf fortress.
        if (dwarfmode_pausing_fps_counter_hook::init_have_fps_yes()
            && df::global::gps && df::global::gps->display_frames == 0)
            df::global::gps->display_frames = 1;
    }
};


IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_pausing_fps_counter_hook, render);

IMPLEMENT_VMETHOD_INTERPOSE(title_pausing_fps_counter_hook, render);