/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2014 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/
#ifndef OSGEARTH_QUAD_TREE
#define OSGEARTH_QUAD_TREE 1

#include <osgEarth/Common>

#include <osg/Shape>
#include <osg/Geometry>

#include <map>

namespace osgEarth
{
    class OSGEARTH_EXPORT QuadTree : public osg::Shape
    {
        public:

            QuadTree();

            QuadTree(const QuadTree& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

            META_Shape(osg, QuadTree)

            struct OSGEARTH_EXPORT BuildOptions
            {
                BuildOptions();

                unsigned int _numVerticesProcessed;
                unsigned int _targetNumTrianglesPerLeaf;
                unsigned int _maxNumLevels;
                unsigned int _numTriangles;
            };


            /** Build the quadtree from the specified source geometry object.
              * retun true on success. */
            virtual bool build(BuildOptions& buildOptions, osg::Geometry* geometry);

            struct LineSegmentIntersection
            {
                LineSegmentIntersection():
                    ratio(-1.0),
                    p0(0),
                    p1(0),
                    p2(0),
                    r0(0.0f),
                    r1(0.0f),
                    r2(0.0f),
                    primitiveIndex(0) {}

                bool operator < (const LineSegmentIntersection& rhs) const { return ratio < rhs.ratio; }

                typedef std::vector<unsigned int>   IndexList;
                typedef std::vector<double>         RatioList;

                double                          ratio;
                osg::Vec3d                      intersectionPoint;
                osg::Vec3                       intersectionNormal;

                unsigned int                    p0;
                unsigned int                    p1;
                unsigned int                    p2;
                float                           r0;
                float                           r1;
                float                           r2;

                unsigned int                    primitiveIndex;
            };


            typedef std::vector<LineSegmentIntersection> LineSegmentIntersections;

            /** compute the intersection of a line segment and the quadtree, return true if an intersection has been found.*/
            virtual bool intersect(const osg::Vec3d& start, const osg::Vec3d& end, LineSegmentIntersections& intersections) const;


            typedef int value_type;

            struct QuadNode
            {
                QuadNode():
                    first(0),
                    second(0) {}

                QuadNode(value_type f, value_type s):
                    first(f),
                    second(s) {}

                osg::BoundingBox bb;

                value_type first;
                value_type second;
            };

            struct Triangle
            {
                Triangle():
                    p0(0),p1(0),p2(0) {}

                Triangle(unsigned int ip0, unsigned int ip1, unsigned int ip2):
                    p0(ip0), p1(ip1), p2(ip2) {}

                bool operator < (const Triangle& rhs) const
                {
                    if (p0<rhs.p0) return true;
                    if (p0>rhs.p0) return false;
                    if (p1<rhs.p1) return true;
                    if (p1>rhs.p1) return false;
                    return p2<rhs.p2;
                }

                unsigned int p0;
                unsigned int p1;
                unsigned int p2;
            };

            typedef std::vector< QuadNode >     QuadNodeList;
            typedef std::vector< Triangle >     TriangleList;

            int addNode(const QuadNode& node)
            {
                int num = static_cast<int>(_quadNodes.size());
                _quadNodes.push_back(node);
                return num;
            }

            QuadNode& getNode(int nodeNum) { return _quadNodes[nodeNum]; }
            const QuadNode& getNode(int nodeNum) const { return _quadNodes[nodeNum]; }

            QuadNodeList& getNodes() { return _quadNodes; }
            const QuadNodeList& getNodes() const { return _quadNodes; }

            void setVertices(osg::Vec3Array* vertices) { _vertices = vertices; }
            const osg::Vec3Array* getVertices() const { return _vertices.get(); }

            unsigned int addTriangle(const Triangle& tri)
            {
                unsigned int num = static_cast<unsigned int>(_triangles.size());
                _triangles.push_back(tri);
                return num;
            }

            Triangle& getTriangle(unsigned int i) { return _triangles[i]; }
            const Triangle& getTriangle(unsigned int i) const { return _triangles[i]; }

            TriangleList& getTriangles() { return _triangles; }
            const TriangleList& getTriangles() const { return _triangles; }


        protected:

            osg::ref_ptr<osg::Vec3Array>        _vertices;
            QuadNodeList                        _quadNodes;
            TriangleList                        _triangles;

    };

    class QuadTreeBuilder : public osg::NodeVisitor
    {
        public:

            QuadTreeBuilder();

            QuadTreeBuilder(const QuadTreeBuilder& rhs);

            META_NodeVisitor(osg, QuadTreeBuilder)

            virtual QuadTreeBuilder* clone() { return new QuadTreeBuilder(*this); }

            void apply(osg::Geode& geode);

            QuadTree::BuildOptions _buildOptions;

            osg::ref_ptr<QuadTree> _quadTreePrototype;
        protected:

            virtual ~QuadTreeBuilder() {}
    };

} // namespace osgEarth

#endif // OSGEARTH_QUAD_TREE
