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

#include <osgEarthAnnotation/AnnotationNode>
#include <osgEarth/DrapeableNode>
#include <osgEarth/GeoData>
#include <osg/Transform>

namespace osgEarth { namespace Annotation
{	
    using namespace osgEarth;

    /**
     * Base class for node that is localized at a specific geographic point.
     */
    class OSGEARTHANNO_EXPORT LocalizedNode : public PositionedAnnotationNode
    {
    public:
        META_AnnotationNode(osgEarthAnnotation, LocalizedNode);

        /**
         * Constructs a new LocalizedNode.
         *
         * @param mapSRS  Spatial reference for position info
         * @param pos     Initial position of the node (map coords)
         * @param is2D    Whether to autotransform the node to face/scale to the screen
         */
        LocalizedNode( 
            MapNode*          mapNode,
            const GeoPoint&   pos      =GeoPoint::INVALID,
            bool              is2D     =false );

        LocalizedNode(
            MapNode*          mapNode,
            const Config&     conf );

        virtual ~LocalizedNode() { }


    public: // PositionedAnnotationNode

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

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

    public: // MapNodeObserver

        virtual void setMapNode( MapNode* mapNode );

    public: // osg::Node

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

    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;

        /**
         * Sets a local rotation - a rotation relative to the local tangent plane
         * of the geometry.
         */
        void setLocalRotation( const osg::Quat& rotation );

        /**
         * Gets the local tangent plane -space rotation.
         */
        const osg::Quat& getLocalRotation() const;

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

        void setScale( const osg::Vec3f& scale );

        const osg::Vec3f& getScale() const { return _scale; }

        virtual osg::Group* getRoot() { return _draper.get(); }

        virtual osg::Group* getChildAttachPoint() { return _xform.get(); }

        virtual osg::Transform* getTransform() { return _xform.get(); }

        virtual DrapeableNode* getDrapeable() { return _draper.get(); }

    protected:
        osg::ref_ptr<osg::Transform> _xform;
        osg::ref_ptr<DrapeableNode> _draper;
        bool _horizonCulling;
        bool _autoTransform;
        GeoPoint _mapPosition;
        osg::Vec3f _scale;
        osg::Vec3d _localOffset;
        osg::Quat  _localRotation;

        friend class Decoration;
        
        virtual void reclamp( const TileKey& key, osg::Node* tile, const Terrain* terrain );

            
        // copy constructor
        LocalizedNode() { }
        LocalizedNode(const LocalizedNode& rhs, const osg::CopyOp& op=osg::CopyOp::DEEP_COPY_ALL) { }

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

    private:

        void init( MapNode* mapNode, const GeoPoint& position );
        
    };

} } // namespace osgEarth::Annotation

#endif // OSGEARTH_ANNO_LOCALIZED_NODE_H
