#pragma once
#include "object.hpp"
#include "renderer.hpp"
#include "tracer.hpp"


/**
 * @file
 * @brief First entrypoint of the main loop, processing user input and maintaining state for a new frame.
 */


typedef std::vector<std::unique_ptr<const Object>> scene_t;
typedef std::vector<std::pair<vertex_t, const PointLight*>> lights_t;


/** @brief Player can collect lights and drop them again. */
class LightStack {
  public:
  private:
    std::list<lightbox_t> lightboxes{}; ///< active dynamic lights
    std::vector<const PointLight*> stack{}; ///< picked and thus disabled lights

    void push(const PointLight*, const vertex_t&, const scene_t&);

  public:
    LightStack(const scene_t& scene, const lights_t& lights);

    bool push(const vertex_t&, const scene_t&); ///< place at given position, if any available
    const PointLight* pop_nearest(const vertex_t&, fcoord_t); ///< take a light, if nearby
    size_t size() const; ///< number of collected lights

    const std::list<lightbox_t>& get() const; ///< all lights to be put into the current scene
};


/**
 * @brief Main coordinator, getting user input and starting new frame pipeline.
 *
 * Sets up and holds the overall processing pipeline (@ref Tracer, @ref LightTracer, @ref Renderer).
 * Update player position and preprocess an optimized scene.
 * The result should only contain the subset of objects and lights that might contribute.
 * A new frame render is triggered by pushing a @ref context_t to the @ref Tracer @ref ThreadQueue.
 */
class State {
  private:
    Sampler sampler; ///< final interpolation, i.e., when subsampling is enabled
    ContextFactory contexts;
    const scene_t& scene;
    const lights_t& lights;
    LightStack lightboxes;
    vertex_t camera; ///< last position, moving along ticks
    const fcoord_t sensitivity;
    unsigned congestion_count{};

    PointLight player;
    Renderer renderer;
    LightTracer light_tracer;
    Tracer tracer;

    vertex_t clip(const vertex_t& pos, const vertex_t& off) const; ///< intersect player movement with scene

  public:
    State(const scene_t& scene, const lights_t& lights, vertex_t raster, vertex_t viewport, vertex_t camera,
          res_t resolution, bool subsample, fcoord_t sensitivity);

    bool tick(const Renderer::input_t&); ///< process input, update state, enqueue new frame context
    Renderer::input_t get_input(); ///< wait for pressed keys

    unsigned congestion_hint(); ///< number of queue overflow events, reset counter afterwards
    const SlidingAverage<msec_t>& duration_hint(); ///< overall frame pipeline delay, as observed by @ref Renderer
};