/*
    This file is part of Nori, a simple educational ray tracer
    Copyright (c) 2015 by Wenzel Jakob
    Nori is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.
    Nori is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program. If not, see .
*/
#if !defined(__NORI_BVH_H)
#define __NORI_BVH_H
#include 
NORI_NAMESPACE_BEGIN
/**
 * \brief Bounding Volume Hierarchy for fast ray intersection queries
 *
 * This class builds a Bounding Volume Hierarchy (BVH) using a greedy
 * divide and conquer build strategy, which locally maximizes a criterion
 * known as the Surface Area Heuristic (SAH) to obtain a tree that is
 * particularly well-suited for ray intersection queries.
 *
 * Construction of a BVH is generally slow; the implementation here runs
 * in parallel to accelerate this process much as possible. For details
 * on how this works, refer to the paper
 *
 * "Fast and Parallel Construction of SAH-based Bounding Volume Hierarchies"
 * by Ingo Wald (Proc. IEEE/EG Symposium on Interactive Ray Tracing, 2007)
 *
 * \author Wenzel Jakob
 */
class Accel {
    friend class BVHBuildTask;
public:
    /// Create a new and empty BVH
    Accel() { m_meshOffset.push_back(0u); }
    /// Release all resources
    virtual ~Accel() { clear(); };
    /// Release all resources
    void clear();
    /**
     * \brief Register a triangle mesh for inclusion in the BVH.
     *
     * This function can only be used before \ref build() is called
     */
    void addMesh(Mesh *mesh);
    /// Build the BVH
    void build();
    /**
     * \brief Intersect a ray against all triangle meshes registered
     * with the BVH
     *
     * Detailed information about the intersection, if any, will be
     * stored in the provided \ref Intersection data record. 
     *
     * The shadowRay parameter specifies whether this detailed
     * information is really needed. When set to \c true, the 
     * function just checks whether or not there is occlusion, but without
     * providing any more detail (i.e. \c its will not be filled with
     * contents). This is usually much faster.
     *
     * \return \c true If an intersection was found
     */
    bool rayIntersect(const Ray3f &ray, Intersection &its, 
        bool shadowRay = false) const;
    /// Return the total number of meshes registered with the BVH
    uint32_t getMeshCount() const { return (uint32_t) m_meshes.size(); }
    /// Return the total number of internally represented triangles 
    uint32_t getTriangleCount() const { return m_meshOffset.back(); }
    /// Return one of the registered meshes
    Mesh *getMesh(uint32_t idx) { return m_meshes[idx]; }
    
    /// Return one of the registered meshes (const version)
    const Mesh *getMesh(uint32_t idx) const { return m_meshes[idx]; }
    //// Return an axis-aligned bounding box containing the entire tree
    const BoundingBox3f &getBoundingBox() const {
        return m_bbox;
    }
protected:
    /**
     * \brief Compute the mesh and triangle indices corresponding to 
     * a primitive index used by the underlying generic BVH implementation. 
     */
    uint32_t findMesh(uint32_t &idx) const {
        auto it = std::lower_bound(m_meshOffset.begin(), m_meshOffset.end(), idx+1) - 1;
        idx -= *it;
        return (uint32_t) (it - m_meshOffset.begin());
    }
    //// Return an axis-aligned bounding box containing the given triangle
    BoundingBox3f getBoundingBox(uint32_t index) const {
        uint32_t meshIdx = findMesh(index);
        return m_meshes[meshIdx]->getBoundingBox(index);
    }
    
    //// Return the centroid of the given triangle
    Point3f getCentroid(uint32_t index) const {
        uint32_t meshIdx = findMesh(index);
        return m_meshes[meshIdx]->getCentroid(index);
    }
    /// Compute internal tree statistics
    std::pair statistics(uint32_t index = 0) const;
    /* BVH node in 32 bytes */
    struct BVHNode {
        union {
            struct {
                unsigned flag : 1;
                uint32_t size : 31;
                uint32_t start;
            } leaf;
            struct {
                unsigned flag : 1;
                uint32_t axis : 31;
                uint32_t rightChild;
            } inner;
            uint64_t data;
        };
        BoundingBox3f bbox;
        bool isLeaf() const {
            return leaf.flag == 1;
        }
        bool isInner() const {
            return leaf.flag == 0;
        }
        bool isUnused() const {
            return data == 0;
        }
        uint32_t start() const {
            return leaf.start;
        }
        uint32_t end() const {
            return leaf.start + leaf.size;
        }
    };
private:
    std::vector m_meshes;       ///< List of meshes registered with the BVH
    std::vector m_meshOffset; ///< Index of the first triangle for each shape
    std::vector m_nodes;       ///< BVH nodes
    std::vector m_indices;    ///< Index references by BVH nodes
    BoundingBox3f m_bbox;               ///< Bounding box of the entire BVH
};
NORI_NAMESPACE_END
#endif /* __NORI_BVH_H */