/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2015 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* 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_ANNO_ORTHO_NODE_H
#define OSGEARTH_ANNO_ORTHO_NODE_H 1

#include <osgEarthAnnotation/AnnotationNode>
#include <osgEarth/Decluttering>
#include <osgEarth/SpatialReference>
#include <osgEarth/Horizon>
#include <osgEarth/CullingUtils>
#include <osg/AutoTransform>
#include <osg/MatrixTransform>

namespace osgEarth { namespace Annotation
{	
    using namespace osgEarth;

    /**
     * Base class for an annotation node that it drawn in screen-space
     * (as an orthographic overlay)
     *
     * Don't use this class directly. Use one of its subclasses instead like
     * LabelNode or PlaceNode.
     */
    class OSGEARTHANNO_EXPORT OrthoNode : public PositionedAnnotationNode, 
                                          public SupportsDecluttering
    {
    public:
        META_AnnotationNode( osgEarthAnnotation, OrthoNode );

        /**
         * Constructs an relative-position ortho node
         */
        OrthoNode();

        /**
         * Constructs an ortho node that resides at an absolute position on the map
         * @param mapNode  MapNode used to referenced the ortho node
         * @param position Starting position
         */
        OrthoNode( 
            MapNode*        mapNode, 
            const GeoPoint& position );

        /**
         * Attaches a child node to the transforms. Use this instead of addChild.
         */
        virtual osg::Group* getAttachPoint() { return _attachPoint; }


    public: // PositionedAnnotationNode

        /** Sets the node position */
        virtual bool setPosition( const GeoPoint& pos );

        /** Gets the position (in map coordinates) of the node */
        virtual GeoPoint getPosition() const;

    public:
        /**
         * Sets a "local offset" position - for an ECEF map, this is an offset in 
         * the local tangent plane of the node that is applied to the geometry.
         */
        void setLocalOffset( const osg::Vec3d& offset );

        /**
         * Gets the local tangent plane -space offset.
         */
        const osg::Vec3d& getLocalOffset() const;

        /**
         * Enables or disable automatic horizon culling for geocentric maps
         */
        void setHorizonCulling(bool value);
        bool getHorizonCulling() const;

        /**
         * Enables or disables ray based occlusion culling
         */
        bool getOcclusionCulling() const;
        void setOcclusionCulling( bool value );

        /**
         * Gets or sets the maximum altitude that the occlusion culling takes place.
         */
        double getOcclusionCullingMaxAltitude() const;
        void setOcclusionCullingMaxAltitude( double occlusionCullingMaxAltitude );

    public: // AnnotationNode

        virtual void applyStyle(const Style& style);

    public: // MapNodeObserver

        virtual void setMapNode( MapNode* mapNode );

    public: // osg::Node

        virtual void traverse( osg::NodeVisitor& nv );

        virtual osg::BoundingSphere computeBound() const;

    protected:
        /** virtual dtor */
        virtual ~OrthoNode() { }

    private:
        osg::Switch*                   _switch;
        osg::Group*                    _oq;
        osg::AutoTransform*            _autoxform;
        osg::MatrixTransform*          _matxform;
        osg::Group*                    _attachPoint;
        bool                           _occlusionCulling;
        optional< double >             _occlusionCullingMaxAltitude;
        GeoPoint                       _mapPosition;
        osg::Vec3d                     _localOffset;
        bool                           _horizonCullingRequested;

        osg::ref_ptr< HorizonCullCallback >      _horizonCuller;
        osg::ref_ptr< OcclusionCullingCallback > _occlusionCuller;

        void init();

        osg::Vec3d adjustOcclusionCullingPoint( const osg::Vec3d& world );

    protected:
        // required by META_Node, but this object is not cloneable
        OrthoNode( const OrthoNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) : PositionedAnnotationNode(rhs, op) { }

    private:
        // autoclamping.
        virtual void reclamp( const TileKey& key, osg::Node* tile, const Terrain* );

        bool updateTransforms( const GeoPoint& mappos, osg::Node* patch =0L );
    };

} } // namespace osgEarth::Annotation

#endif // OSGEARTH_ANNO_LOCALIZED_NODE_H
