#pragma once
#include "texture.hpp"


/** @brief Base class for any drawable object. */
class Object {
  protected:
    const box_t bounding;
    const Texture* texture;

  public:
    Object(box_t, const Texture*);
    virtual ~Object() = default;

    virtual bool intersect(const ray_t& ray, vertex_t& hit, vertex_t& nrm, const Texture*& tex, uv_t& uv) const = 0;
    virtual bool intersect(const ray_t& ray, vertex_t& hit, vertex_t& nrm) const = 0;
    virtual bool intersect(const ray_t& ray) const = 0;

    /** @brief possibly on visible side from given position? */
    virtual bool can_intersect(const vertex_t& camera) const = 0;

    /** @brief always shadows other volume from given position? */
    virtual bool intersect_full(const vertex_t& pos, const box_t& dst) const = 0;

    /** @brief maximum bounding volume */
    INLINE const box_t& box() const {
        return bounding;
    }
};


/** @brief Currently the only @ref Object primitive, building the whole scene. */
class SidedAxisPlane final : public Object {
  private:
    const vertex_t pos;
    const vertex_t dir;
    const bool positive;

    struct {
        int n, a, b;
    } os{}; ///< null axis offset, other axises
    vertex_t nrm{};

    INLINE bool intersect(const ray_t& ray, vertex_t& hit) const;
    INLINE bool can_intersect(const ray_t& ray) const;
    INLINE bool can_intersect_any(const ray_t& ray) const;

  public:
    SidedAxisPlane(vertex_t pos, vertex_t dir, bool positive, const Texture*);
    SidedAxisPlane(ray_t ray, bool positive, const Texture* tex) : SidedAxisPlane(ray.pos, ray.dir, positive, tex){};

    bool intersect(const ray_t& ray, vertex_t& hit, vertex_t& nrm, const Texture*& tex, uv_t& uv) const override;
    bool intersect(const ray_t& ray, vertex_t& hit, vertex_t& nrm) const override;
    bool intersect(const ray_t& ray) const override;

    bool can_intersect(const vertex_t& camera) const override;
    bool intersect_full(const vertex_t& pos, const box_t& dst) const override;
};