/**
 @file MeshEntity.h

 Triangle mesh for fast ray tracing

 @author Morgan McGuire, morgan@cs.williams.edu
 */
#ifndef MESHENTITY_H
#define MESHENTITY_H

#include <G3D/G3DAll.h>
#include "Entity.h"

typedef ReferenceCountedPointer<class MeshEntity> MeshEntityRef;

class BSDF;
class EmissionFunction;

/**
 A triangle mesh that performs efficient ray-triangle intersections.
 */
class MeshEntity : public Entity {
public:
    class Tri {
    public:

        /** Vertex 0 */
        Vector3 v0;

        /** Edge vector v1 - v0 */
        Vector3 e1;

        /** Edge vector v2 - v0 */
        Vector3 e2;

        /** If the Tri is in a "smooth" surface then the vertex normal at v0,
            otherwise the face normal.
          */
        Vector3 n0;

        /** Vertex normal at v1 */
        Vector3 n1;

        /** Vertex normal at v2 */
        Vector3 n2;

        Tri();
        Tri(const Vector3& v0, const Vector3& v1, const Vector3& v2, 
            const Vector3& n0, const Vector3& n1 = Vector3::zero(), const Vector3& n2 = Vector3::zero());

        /** Returns a bounding box */
        inline void getBounds(AABox& box) const {
            const Vector3 v1 = v0 + e1;
            const Vector3 v2 = v0 + e2;

            box = AABox(v0.min(v1).min(v2), v0.max(v1).max(v2));
        }

        /** Returns a (relatively) unique integer for this object */
        inline uint32 hashCode() const {
            return (v0.hashCode() << 20) + (e1.hashCode() << 10) + e2.hashCode();
        }

        bool operator==(const Tri& t) const {
            return 
                (v0 == t.v0) &&
                (e1 == t.e1) &&
                (e2 == t.e2) &&
                (n0 == t.n0) &&
                (n1 == t.n1) &&
                (n2 == t.n2);
        }
    };

private:

    // Maintain the triangles in a tree for fast ray intersections.
    // Note that the MeshEntity itself is placed in the scene tree, so
    // entitys whose bounding boxes overlap will be less efficient than
    // if we placed all triangles directly in the tree.  However, this
    // saves the tracer from even looking at the entity when the whole-object
    // bounding box test fails and prevents the global tree from being overly 
    // burdened by a high-polygon model (since the AABSPTree isn't particularly
    // good at clustering; its goal is a balanced tree, which is bad when a high-polygon
    // area has low area in screen-space.)
    AABSPTree<Tri>          m_triSet;

    const BSDF*             m_bsdf;

    /** If true, the surface normals are interpolated on this object, making it appear to be 
        smoothly curved. */
    bool                    m_smooth;

    /** Bounding box on the entire object. */
    AABox                   m_bounds;

    MeshEntity(const PosedModelRef& model, const BSDF* bsdf, bool smooth);

public:

    /** Create a MeshEntity from an IFS or ply2 file.

        Example:
         <pre>
            MeshEntity::create("teapot.ifs", bsdfTable["Yellow"], 
                CoordinateFrame(Matrix3::fromAxisAngle(Vector3::unitY(), toRadians(30)),
                               Vector3(0,-1.2f,0)), 
                Vector3(1,1,1)*1.2f);
         </pre>
      */
    static MeshEntityRef create(
        const std::string&          filename, 
        const BSDF*                 bsdf, 
        const CoordinateFrame&      xform               = CoordinateFrame(), 
        const Vector3&              scale               = Vector3(1,1,1),
        bool                        smoothNormals       = true);

    /** Create a MeshEntity from a G3D::PosedModel. */
    static MeshEntityRef create(
        const PosedModelRef&        model,
        const BSDF*                 bsdf,
        bool                        smoothNormals = true);

    // Inherited from Entity
    void intersect(const Ray& worldRay, float& hitDistance, class Hit& hitResult);
    
    // Inherited from Entity
    virtual void getBounds(AABox& box) const;

    virtual int primitiveCount() const;
};

// Needed for AABSPTree<MeshEntity::Tri>
inline uint32 hashCode(const MeshEntity::Tri& tri) {
    return tri.hashCode();
}

// Needed for AABSPTree<MeshEntity::Tri>
inline void getBounds(const MeshEntity::Tri& tri, AABox& box) {
    tri.getBounds(box);
}

#endif
