/* -*-c++-*- */
/* osgEarth - Geospatial SDK 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/>
*/
#pragma once

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

#include <osgEarth/Ellipsoid>
#include <osg/MatrixTransform>
#include <osg/BoundingBox>
#include <osg/Drawable>
#include <osg/Geode>
#include <osgText/Text>
#include <osgUtil/CullVisitor>
#include <vector>

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(const osg::Image* raster, 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 getPixelSizeOnScreen(osg::CullStack* cull) const
        {
            // By using the width, the "apparent" pixel size will decrease as we near the poles.
            // TODO: Consider using the Height in ortho mode instead.
            bool ortho = ProjectionMatrix::isOrtho(*cull->getProjectionMatrix());
            double R = (ortho ? _drawable->_bboxHalfHeight : _drawable->_bboxHalfWidth);
            return cull->clampedPixelSize(getMatrix().getTrans(), R) / cull->getLODScale();
        }

    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
