/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2013 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_ANNO_ORTHO_NODE_H
#define OSGEARTH_ANNO_ORTHO_NODE_H 1

#include <osgEarthAnnotation/AnnotationNode>
#include <osgEarthAnnotation/Decluttering>
#include <osgEarth/SpatialReference>
#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
         */
        void setHorizonCulling( bool value );
        bool getHorizonCulling() const;

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

    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                           _horizonCulling;
        bool                           _occlusionCulling;
        GeoPoint                       _mapPosition;
        osg::Vec3d                     _localOffset;

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

        void init();

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

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

        // 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
