#pragma once
#include "vertex.hpp"
#include "object.hpp"
#include "lightmap.hpp"
#include "common.hpp"
#include <map>
#include <set>
#include <vector>


class Light {
    public:
        const bool is_static;
        const Img::rgb_t color;

        Light(bool s): is_static(s), color(255, 255, 255) {}
        Light(bool s, Img::rgb_t c): is_static(s), color(c) {}

        virtual ~Light() {}

        virtual light_t get(std::vector<const Object*>&, const vertex_t& hit, const vertex_t* norm_ray, const vertex_t& norm) const = 0; // [0..1]

        virtual bool occlude_box(const Object*, vertex_t& bound_min, vertex_t& bound_max) const = 0;
        virtual bool reachable(const Object*) const = 0;
};


class Occlusion {
    private:
        mutable std::map<const Object*, std::vector<const Object*> > cands;

    public:
        void finalize(const std::vector<const Object*>&);
        light_t get(const Object*, const vertex_t&, const vertex_t&) const;
        void getSMPcopy(Occlusion& o) const;
        bool enabled() const;
};


class Lights {
    private:
        typedef struct {
            const Light* light;
            std::set<const Object*> occluded;
            mutable std::map<const Object*, std::vector<const Object*> > can_occlude; // lru
        } light_env_t;
        std::vector<light_env_t> lights;
        Occlusion occlusion;

        bool occludes(const Object* src, const Object* occ, const Light* light) const;
        static void set_lightmap(void*);

    public:
        void push(const Light* lo);
        void finalize(const std::vector<const Object*>& objects);
        void get_light(const Object* o, const vertex_t& hit, const vertex_t* raynrm, const vertex_t& nrm, LightValue&) const;
        void getSMPcopy(Lights& o) const;
};