/* osgEarth
* Copyright 2008-2014 Pelican Mapping
* MIT License
*/
#pragma once

#include "Common"
#include "GeometryPool"
#include "RenderBindings"
#include "TileDrawable"
#include "TileRenderModel"

#include <osgEarth/Ellipsoid>
#include <osgEarth/Horizon>

#include <osg/MatrixTransform>
#include <osg/BoundingBox>
#include <osg/Drawable>
#include <osgUtil/CullVisitor>

namespace osgEarth { namespace REX
{
    using namespace osgEarth;

    /**
     * SurfaceNode holds the geometry and transform information
     * for one terrain tile surface.
     */
    class SurfaceNode : public osg::MatrixTransform
    {
    public:
        osg::ref_ptr<TileDrawable> _drawable;
        osg::ref_ptr<osg::Node> _debugNode;
        int _lastFramePassedCull = 0;

    public:
        SurfaceNode(const TileKey& tilekey, TileDrawable* drawable);

        //! Update the elevation raster associated with this tile
        //! and update the culling information based on the new data.
        void setElevationRaster(Texture::Ptr image, const osg::Matrixf& scaleBias);

        //! Whether this tile is visible given the provided horizon.
        inline bool isVisible(Horizon* horizon) const {
            return _horizonCullingPointSet ? horizon->isVisible(_horizonCullingPoint, 0.0) : true;
        }

        //! Tests whether any "child box" is in visible range. This object holds information
        //! about the predicted bounding boxes of each its four potential children. This
        //! method tests whether any of those boxes is within the specified range so that
        //! the owning TileNode can decide when to subdivide.
        inline bool anyChildBoxWithinRange(float range, osg::NodeVisitor& nv) const
        {
            for(int c=0; c<4; ++c) {
                for(int j=0; j<8; ++j) {
                    if (nv.getDistanceToViewPoint(_childrenCorners[c][j], true) < range)
                        return true;
                }
            }
            return false;
        }

        //! Size of this node on the screen in pixels; used for culling decisions
        //! when using in SCREEN_SPACE lod method.
        inline float getPixelSize(osg::CullStack* cull) const
        {
            return Util::getPixelSize(
                cull, getMatrix().getTrans(),
                0.5 * (_drawable->_bboxHalfWidth + _drawable->_bboxHalfHeight));
        }

    protected:

        osg::BoundingSphere computeBound() const override;

    private:

        const Ellipsoid& _ellipsoid;
        osg::Vec3d _horizonCullingPoint;
        bool _horizonCullingPointSet = false;
        osg::Vec3 _childrenCorners[4][8];
        static const bool _enableDebugNodes;

        void addDebugNode(const osg::BoundingBox& box);
        void removeDebugNode(void);
    };

} } // namespace osgEarth::REX
