#pragma once

#include "../base/Types.h"
#include "../scene/Scene.h"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace renderive {

struct Performance_Log_Options {
    bool enabled{};
    std::string directory;
    std::string session_name = "renderive";
};

struct Performance_Overlay_Style {
    Font font;
    int top_margin = 4;
    int left_margin = 6;
    int right_margin = 12;
    int bottom_margin = 6;
    Color background = Color::white();
    Color section_color{64, 142, 255};
    Color label_color{190, 190, 190};
    Color value_color{235, 218, 130};
};

struct Performance_Overlay_Options {
    Performance_Overlay_Style style;
    Performance_Log_Options log;
};

struct Performance_Display_Snapshot {
    Performance_Overlay_Style style;
    Size viewport_size;
    std::string plot_name;
    std::vector<std::string> lines;
    std::uint64_t frame_count{};
};

class LIB_DECL Performance_Overlay {
public:
    Performance_Overlay();
    explicit Performance_Overlay(Performance_Overlay_Options options);
    ~Performance_Overlay();

    [[nodiscard]] bool enabled() const noexcept;
    [[nodiscard]] std::shared_ptr<const Performance_Display_Snapshot> display_snapshot() const;

private:
    template <detail::Scene_Frame_Control Frame_Control>
    friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>&);
    template <detail::Scene_Frame_Control Frame_Control>
    friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>&, const Performance_Overlay_Options&);
    template <detail::Scene_Frame_Control Frame_Control>
    friend void set_performance_plot_name(Basic_Scene2D<Frame_Control>&, std::string);
    template <detail::Scene_Frame_Control Frame_Control>
    friend void set_performance_overlay_enabled(Basic_Scene2D<Frame_Control>&, bool);

    struct Impl;
    std::unique_ptr<Impl> impl_;

    void set_enabled(bool enabled);
    void set_plot_name(std::string name);
    void set_options(Performance_Overlay_Options options);
    friend void detail::record_performance_frame(Performance_Overlay&, double, const Low_Latency_Diagnostics&, std::size_t, Size);
    void record_frame(double render_duration_ms,
                      const Low_Latency_Diagnostics& diagnostics,
                      std::size_t renderable_count,
                      Size viewport);
};

template <detail::Scene_Frame_Control Frame_Control>
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>& scene) {
    if (auto existing = scene.performance_overlay())
        return existing;
    auto overlay = std::make_shared<Performance_Overlay>();
    scene.set_performance_overlay(overlay);
    return overlay;
}
template <detail::Scene_Frame_Control Frame_Control>
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>& scene, const Performance_Overlay_Options& options) {
    if (auto existing = scene.performance_overlay()) {
        existing->set_options(options);
        scene.notify_model_dirty();
        return existing;
    }
    auto overlay = std::make_shared<Performance_Overlay>(options);
    scene.set_performance_overlay(overlay);
    return overlay;
}
template <detail::Scene_Frame_Control Frame_Control>
void set_performance_plot_name(Basic_Scene2D<Frame_Control>& scene, std::string name) {
    auto overlay = scene.performance_overlay();
    if (!overlay)
        overlay = attach_performance_overlay(scene);
    overlay->set_plot_name(std::move(name));
    scene.notify_model_dirty();
}
template <detail::Scene_Frame_Control Frame_Control>
void set_performance_overlay_enabled(Basic_Scene2D<Frame_Control>& scene, bool enabled) {
    auto overlay = scene.performance_overlay();
    if (!overlay && !enabled)
        return;
    if (!overlay)
        overlay = attach_performance_overlay(scene);
    overlay->set_enabled(enabled);
    scene.notify_model_dirty();
}

} // namespace renderive
