#ifndef _gpuCacheDrawTraversal_h_
#define _gpuCacheDrawTraversal_h_
#include "gpuCacheGeometry.h"
#include "gpuCacheFrustum.h"
#include "gpuCacheVBOProxy.h"
#include <boost/foreach.hpp>
namespace GPUCache {
    
class DrawTraversalState
{
public:
    enum TransparentPruneType {
        kPruneNone,
        kPruneOpaque,
        kPruneTransparent
    };
    DrawTraversalState(
        const Frustum&             frustrum,
        const double               seconds,
        const TransparentPruneType transparentPrune)
        : fFrustum(frustrum),
          fSeconds(seconds),
          fTransparentPrune(transparentPrune)
    {}
    virtual ~DrawTraversalState() {}
    const Frustum&  frustum() const { return fFrustum; }
    double          seconds() const { return fSeconds; }
    TransparentPruneType transparentPrune() const { return fTransparentPrune; }
    VBOProxy&       vboProxy()      { return fVBOProxy; }
    
private:
    DrawTraversalState(const DrawTraversalState&);
    const DrawTraversalState& operator=(const DrawTraversalState&);
            
private:
    const Frustum              fFrustum;
    const double               fSeconds;
    const TransparentPruneType fTransparentPrune;
    VBOProxy                   fVBOProxy;
};
template <class Derived, class State>
class DrawTraversal : public SubNodeVisitor
{
public:
    State& state() const            { return fState; }
    const MMatrix& xform()
 const    { 
return fXform; }
 
    
protected:
    
    DrawTraversal(
        State&                  state,
        bool                    isReflection,
        Frustum::ClippingResult parentClippingResult
    )
        : fState(state),
          fXform(xform),
          fIsReflection(isReflection),
          fParentClippingResult(parentClippingResult)
    {}
    bool isReflection() const { return fIsReflection; }
    const SubNode& subNode() const { return *fSubNode; }
private:
    
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        if (state().transparentPrune() == State::kPruneOpaque &&
                subNode.transparentType() == SubNode::kOpaque) {
            return;
        }
        else if (state().transparentPrune() == State::kPruneTransparent &&
                subNode.transparentType() == SubNode::kTransparent) {
            return;
        }
        const boost::shared_ptr<const XformSample>& sample =
            xform.getSample(fState.seconds());
        if (!sample) return;
        if (!sample->visibility()) return;
        
        
        Frustum::ClippingResult clippingResult = Frustum::kInside;
        if (fParentClippingResult != Frustum::kInside) {
            
            
            clippingResult = state().frustum().test(
                    sample->boundingBox(), fParentClippingResult);
            
            
            if (clippingResult == Frustum::kOutside) {
                return;
            }
        }
        
        
        Derived traversal(fState, sample->xform() * fXform,
                          bool(fIsReflection ^ sample->isReflection()),
                          clippingResult);
        
        BOOST_FOREACH(const SubNode::Ptr& child,
                      subNode.getChildren() ) {
            child->accept(traversal);
        }
    }
        
    virtual void visit(const ShapeData&   shape,
                       const SubNode&     subNode)
    {
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(fState.seconds());
        if (!sample) return;
        fSubNode = &subNode;
        static_cast<Derived*>(this)->draw(sample);
    }
private:
    DrawTraversal(const DrawTraversal&);
    const DrawTraversal& operator=(const DrawTraversal&);
        
private:
    
    State&                        fState;
    const SubNode*                fSubNode;
    const bool                    fIsReflection;
    const Frustum::ClippingResult fParentClippingResult;
};
} 
#endif