#pragma once
#include <list>
#include "thread.hpp"
#include "vertex.hpp"


class Object;
class Texture;
class PointLight;


/**
 * @brief Translate raster/pixel coordinates into rays from camera through viewport onto scene.
 *
 * Camera/viewport rays are precomputed and only need to be translated to the current camera position.
 * The viewport determines the FOV. The z-distance camera and viewport determines the DOV.
 */
class ZViewport {
  public:
    /** @brief Convenience aggregate to pass along the relevant basic settings. */
    struct viewport_spec_t {
        vertex_t raster;
        vertex_t viewport; // width, height, z dist
        fcoord_t camera_z;
        res_t resolution;
    };

  private:
    const viewport_spec_t config;
    Buffer2D<ray_t> ray_cache; ///< precomputed rays for (camera ->) viewport -> z-axis
    vertex_t camera; // updated upon movement

    ray_t precompute_ray(fcoord_t u, fcoord_t v) const;

  public:
    ZViewport(const ZViewport::viewport_spec_t&);
    const ZViewport::viewport_spec_t& get_config() const;

    void update(vertex_t camera);
    const vertex_t& get_camera() const;

    /** @brief Raster coordinates to scene tracing ray. */
    ray_t get_projected_ray(dcoord_t x, dcoord_t y) const;

    /** @brief Volume that could intersect any projected ray according to current camera and viewport. */
    box_t get_viewbox(const area_t&) const;
};


/** @brief Per-pixel @ref Object intersection hit as @ref Tracer result. */
struct trace_px_t {
    const Object* obj;
    vertex_t hit;
    vertex_t nrm;
    const Texture* tex;
    uv_t uv;
};


/**
 * @brief Current position, radius, and (possibly reduced) volume of a @ref PointLight.
 * @see LightStack
 * @see box_t::cut_down()
 */
struct lightbox_t {
    const PointLight* light;
    disc_t rad;
    box_t box;
};


/** @brief Part of the scene for local parallel processing using the @ref ThreadPool. */
struct sub_frame_t {
    area_t viewport; // start/end box
    std::list<const Object*> visible_scene;
    std::list<const Object*> effective_scene;
    std::list<lightbox_t> effective_lights; //< dynamic lights
};


/** @brief Main frame datastructure, passed along the tracer pipeline.*/
struct context_t {
    Timer timer; ///< measure time it takes to pass the pipeline
    ZViewport viewport; ///< camera, viewport, rays
    std::array<sub_frame_t, 4> corners; ///< local information for each @ref ThreadPool thread
    Buffer2D<trace_px_t> trace_info; ///< filled by @ref Tracer, consumed by @ref LightTracer and @ref Renderer
    Buffer2D<vertex_t> light_info; ///< filled by @ref LightTracer, consumed by @ref Renderer

    context_t(const ZViewport::viewport_spec_t&);
};


/** @brief Single @ref context_t factory for caching and ownership. */
class ContextFactory : public PtrCache<context_t> {
  private:
    const ZViewport::viewport_spec_t viewport;

  protected:
    std::unique_ptr<context_t> make() override;

  public:
    ContextFactory(const ZViewport::viewport_spec_t& viewport);
};