//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield
//Distributed under the terms of the GNU Library General Public License (LGPL)
//as published by the Free Software Foundation.

#ifndef OSGUTIL_INTERSECTVISITOR
#define OSGUTIL_INTERSECTVISITOR 1

#include <osg/NodeVisitor>
#include <osg/LineSegment>
#include <osg/Geode>
#include <osg/GeoSet>
#include <osg/Matrix>

#include <osgUtil/Export>

#include <map>
#include <set>
#include <vector>

namespace osgUtil {


class OSGUTIL_EXPORT Hit
{
    public:

        Hit();
        Hit(const Hit& hit);
        ~Hit();
        
        Hit& operator = (const Hit& hit);
        
        typedef std::vector<int> VecIndexList;

        bool operator < (const Hit& hit) const
        {
            if (_originalLineSegment<hit._originalLineSegment) return true;
            if (_originalLineSegment>hit._originalLineSegment) return false;
            return _ratio<hit._ratio;
        }
        
        
        const osg::Vec3& getLocalIntersectPoint() const { return _intersectPoint; }
        const osg::Vec3& getLocalIntersectNormal() const { return _intersectNormal; }
        
        const osg::Vec3 getWorldIntersectPoint() const { if (_matrix.valid()) return _intersectPoint*(*_matrix); else return _intersectPoint; }
        const osg::Vec3 getWorldIntersectNormal() const ;

        float                           _ratio;
        osg::ref_ptr<osg::LineSegment>  _originalLineSegment;
        osg::ref_ptr<osg::LineSegment>  _localLineSegment;
        osg::NodePath                   _nodePath;
        osg::ref_ptr<osg::Geode>        _geode;
        osg::ref_ptr<osg::GeoSet>       _geoset;
        osg::ref_ptr<osg::Matrix>       _matrix;
        osg::ref_ptr<osg::Matrix>       _inverse;
        
        VecIndexList                    _vecIndexList;
        int                             _primitiveIndex;
        osg::Vec3                       _intersectPoint;
        osg::Vec3                       _intersectNormal;


};


/** Basic visitor for ray based collisions of a scene.
    Note, still in development, current version has not
    practical functionality!*/
class OSGUTIL_EXPORT IntersectVisitor : public osg::NodeVisitor
{
    public:

        IntersectVisitor();
        virtual ~IntersectVisitor();

        void reset();
        
        /** Add a line segment to use for intersection testing during scene traversal.*/
        void addLineSegment(osg::LineSegment* seg);

        /** Modes to control how IntersectVisitor reports hits. */
        enum HitReportingMode {
            ONLY_NEAREST_HIT,
            ALL_HITS
        };

        /** Set the mode of how hits should reported back from a traversal.*/
        void setHitReportingMode(HitReportingMode hrm) { _hitReportingMode = hrm; }
        /** Get the mode of how hits should reported back from a traversal.*/
        HitReportingMode getHitReportingMode() { return _hitReportingMode; }

        //typedef std::multiset<Hit> HitList;
        typedef std::vector<Hit> HitList;
        typedef std::map<osg::LineSegment*,HitList > LineSegmentHitListMap;
        HitList& getHitList(osg::LineSegment* seg) { return _segHitList[seg]; }
        int getNumHits(osg::LineSegment* seg) { return _segHitList[seg].size(); }

        bool hits();

        virtual void apply(osg::Node&);
        virtual void apply(osg::Geode& node);
        virtual void apply(osg::Billboard& node);

        virtual void apply(osg::Group& node);
        virtual void apply(osg::Transform& node);
        virtual void apply(osg::Switch& node);
        virtual void apply(osg::LOD& node);

    protected:


        class IntersectState : public osg::Referenced
        {
            public:

                IntersectState();

                osg::ref_ptr<osg::Matrix> _matrix;
                osg::ref_ptr<osg::Matrix> _inverse;

                typedef std::pair<osg::ref_ptr<osg::LineSegment>,osg::ref_ptr<osg::LineSegment> >   LineSegmentPair;
                typedef std::vector< LineSegmentPair >                                              LineSegmentList;
                LineSegmentList _segList;

                typedef unsigned int LineSegmentmentMask;
                typedef std::vector<LineSegmentmentMask> LineSegmentmentMaskStack;
                LineSegmentmentMaskStack _segmentMaskStack;

                bool isCulled(const osg::BoundingSphere& bs,LineSegmentmentMask& segMaskOut);
                bool isCulled(const osg::BoundingBox& bb,LineSegmentmentMask& segMaskOut);

                void addLineSegmentPair(osg::LineSegment* first,osg::LineSegment* second)
                {
                    _segList.push_back(LineSegmentPair(first,second));
                }

            protected:

                ~IntersectState();

        };

        bool intersect(osg::GeoSet& gset);

        void pushMatrix(const osg::Matrix& matrix);
        void popMatrix();

        bool enterNode(osg::Node& node);
        void leaveNode();

        typedef std::vector<osg::ref_ptr<IntersectState> > IntersectStateStack;
        
        IntersectStateStack         _intersectStateStack;

        osg::NodePath               _nodePath;

        HitReportingMode            _hitReportingMode;
        LineSegmentHitListMap       _segHitList;
};

}

#endif

