/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGUTIL_INTERSECTVISITOR
#define OSGUTIL_INTERSECTVISITOR 1

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

#include <osgUtil/Export>

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

namespace osgUtil {


class OSGUTIL_EXPORT Hit
{ 
    /** Describes a point in space produced by an intersection of a line with a scene.
      * A Hit is always on a surface as rendered by the Open Scene Graph scene (usually
      * a triangle or other primitive, but a special hit handler could return a
      * different value perhaps: a sphere shape might return a Hit on the true sphere
      * rather than the approximate tesselated sphere rendered.
      */
    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::Drawable>     _drawable;
        osg::ref_ptr<osg::RefMatrix>    _matrix;
        osg::ref_ptr<osg::RefMatrix>    _inverse;
        
        VecIndexList                    _vecIndexList;
        int                             _primitiveIndex;
        osg::Vec3                       _intersectPoint;
        osg::Vec3                       _intersectNormal;


};


/** Basic visitor for ray based collisions of a scene.*/
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);

        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::RefMatrix> _matrix;
                osg::ref_ptr<osg::RefMatrix> _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 LineSegmentMask;
                typedef std::vector<LineSegmentMask> LineSegmentMaskStack;
                LineSegmentMaskStack _segmentMaskStack;

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

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

            protected:

                ~IntersectState();

        };

        bool intersect(osg::Drawable& 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;
        LineSegmentHitListMap       _segHitList;
};

}

#endif

