/* -*-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_DRAGGERS_H
#define OSGEARTH_DRAGGERS_H

#include <osgEarth/Common>
#include <osgEarth/GeoData>
#include <osgEarth/TileKey>
#include <osgEarth/MapNodeObserver>
#include <osg/MatrixTransform>
#include <osg/ShapeDrawable>
#include <osgGA/GUIEventHandler>
#include <osgManipulator/Projector>
#include <osgEarth/Terrain>

namespace osgEarth
{
    class MapNode;
    class Terrain;

    class OSGEARTH_EXPORT Dragger : public osg::MatrixTransform, public MapNodeObserver
    {
    public:
        /**
        * Callback that is fired when the position changes
        */
        struct PositionChangedCallback : public osg::Referenced
        {
        public:
            virtual void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& position) {};
            virtual ~PositionChangedCallback() { }
        };

        typedef std::list< osg::ref_ptr<PositionChangedCallback> > PositionChangedCallbackList;

        enum DragMode
        {
          DRAGMODE_HORIZONTAL,
          DRAGMODE_VERTICAL
        };

        Dragger( MapNode* mapNode, int modKeyMask=0, const DragMode& defaultMode=DRAGMODE_HORIZONTAL );

        /** dtor */
        virtual ~Dragger();

        bool getDragging() const;

        bool getHovered() const;

        const osgEarth::GeoPoint& getPosition() const;

        void setPosition( const osgEarth::GeoPoint& position, bool fireEvents=true);

        void setModKeyMask(int mask) { _modKeyMask = mask; }

        int getModKeyMask() const { return _modKeyMask; }

        void setDefaultDragMode(const DragMode& mode) { _defaultMode = mode; }

        DragMode& getDefaultDragMode() { return _defaultMode; }

        void setVerticalMinimum(double min) { _verticalMinimum = min; }

        double getVerticalMinimim() const { return _verticalMinimum; }


        void updateTransform(osg::Node* patch = 0);

        virtual void enter();

        virtual void leave();

        virtual void setColor( const osg::Vec4f& color ) =0;

        virtual void setPickColor( const osg::Vec4f& color ) =0;

        void addPositionChangedCallback( PositionChangedCallback* callback );

        void removePositionChangedCallback( PositionChangedCallback* callback );

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

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


    public: // MapNodeObserver

        virtual void setMapNode( MapNode* mapNode );

        virtual MapNode* getMapNode() { return _mapNode.get(); }


    protected:
        virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
        void setHover( bool hovered);
        void firePositionChanged();

        osg::ref_ptr< TerrainCallback > _autoClampCallback;

        osg::observer_ptr< MapNode > _mapNode;
        osgEarth::GeoPoint _position;
        bool _dragging;
        bool _hovered;
        PositionChangedCallbackList _callbacks;

        osg::ref_ptr<  osgManipulator::LineProjector >  _projector;
        osgManipulator::PointerInfo  _pointer;
        osg::Vec3d _startProjectedPoint;
        bool _elevationDragging;
        int _modKeyMask;
        DragMode _defaultMode;
        double _verticalMinimum;
    };

    /**********************************************************/
    class OSGEARTH_EXPORT SphereDragger : public Dragger
    {
    public:
        SphereDragger(MapNode* mapNode);

        /** dtor */
        virtual ~SphereDragger() { }

        const osg::Vec4f& getColor() const;

        void setColor(const osg::Vec4f& color);

        const osg::Vec4f& getPickColor() const;

        void setPickColor(const osg::Vec4f& pickColor);

        float getSize() const;
        void setSize(float size);

        virtual void enter();

        virtual void leave();

    protected:

        void updateColor();

        osg::MatrixTransform* _scaler;
        osg::ShapeDrawable* _shapeDrawable;
        osg::Vec4f _pickColor;
        osg::Vec4f _color;
        float _size;
    };


} // namespace osgEarth

#endif // OSGEARTH_DRAGGERS_H
