#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include "renderive/render_graph/Render_Plan.hpp"

enum class Node_Execution_Status : std::uint8_t {
    pending,
    running,
    complete,
    failed
};

enum class Node_Metric_Kind : std::uint8_t {
    input_count,
    chunk_size,
    prepared_cells,
    pixel_count,
    primitive_count,
    count
};

struct Node_Execution_Metrics {
    void set(Node_Metric_Kind kind, std::uint64_t value) noexcept;
    [[nodiscard]] bool contains(Node_Metric_Kind kind) const noexcept;
    [[nodiscard]] std::uint64_t get(Node_Metric_Kind kind) const noexcept;
    std::array<std::uint64_t, static_cast<std::size_t>(Node_Metric_Kind::count)> values{};
    std::uint32_t present{};
};

struct Node_Execution {
    Render_Node_Id node_id{};
    std::uint64_t start_time_ns{};
    std::uint64_t end_time_ns{};
    std::uint32_t worker_id{};
    Node_Execution_Status status{Node_Execution_Status::pending};
    Node_Execution_Metrics metrics;
    [[nodiscard]] std::uint64_t duration_ns() const noexcept;
};

struct Frame_Snapshot {
    std::uint64_t frame_id{};
    Render_Plan_Version render_plan_version{};
    std::uint64_t render_start_ns{};
    std::uint64_t render_end_ns{};
    std::vector<Node_Execution> node_executions;
    [[nodiscard]] std::uint64_t render_duration_ns() const noexcept;
};

class Scene_Base;

class Abstract_Frame {
public:
    Abstract_Frame() = default;
    Abstract_Frame(const Abstract_Frame&) = delete;
    Abstract_Frame& operator=(const Abstract_Frame&) = delete;
    virtual ~Abstract_Frame() = default;
    [[nodiscard]] std::shared_ptr<const Frame_Snapshot> completed_snapshot() const noexcept;
private:
    friend class Scene_Base;
    void begin_render(std::uint64_t frame_id, const Render_Plan& plan, bool capture,
                      std::uint64_t render_start_ns);
    [[nodiscard]] bool capture_this_frame() const noexcept;
    [[nodiscard]] Node_Execution* execution_slot(std::size_t index) noexcept;
    [[nodiscard]] std::shared_ptr<const Frame_Snapshot> complete_render(
        std::uint64_t render_end_ns);
    void discard_render() noexcept;
    std::uint64_t frame_id_{};
    Render_Plan_Version plan_version_{};
    std::uint64_t render_start_ns_{};
    std::vector<Node_Execution> executions_;
    std::shared_ptr<const Frame_Snapshot> completed_snapshot_;
    bool capture_{};
    bool rendering_{};
};

[[nodiscard]] std::uint64_t render_clock_now_ns() noexcept;
