/* osgEarth
* Copyright 2025 Pelican Mapping
* MIT License
*/
#pragma once

#include <osgEarth/Common>
#include <osgEarth/GeoPositionNode>
#include <osgEarth/GeoData>
#include <osg/ShapeDrawable>
#include <osgManipulator/Projector>
#include <osgEarth/Callbacks>

namespace osgEarth
{
    class MapNode;
    class Terrain;

    /**
     * Dragger is a handle you can use to control things in the scene.
     * You drag it around with the mouse and it fires PositionChangedCallback's
     * that you can listen to to repond to.
     */
    class OSGEARTH_EXPORT Dragger : public GeoPositionNode
    {
    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();

        /** Sets the map position of the dragger, optionally firing a PositionChanged event. */
        void setPosition(const osgEarth::GeoPoint& position, bool fireEvents);

        /** Is the user currently dragging? */
        bool getDragging() const;

        /** Is the mouse hovering over this dragger? */
        bool getHovered() const;

        /** Mod key to require to activate the dragget */
        void setModKeyMask(int mask) { _modKeyMask = mask; }
        int getModKeyMask() const { return _modKeyMask; }

        /** Drag mode */
        void setDefaultDragMode(const DragMode& mode) { _defaultMode = mode; }
        DragMode& getDefaultDragMode() { return _defaultMode; }

        /** Minimum vertical distance for vertical dragging */
        void setVerticalMinimum(double min) { _verticalMinimum = min; }
        double getVerticalMinimim() const { return _verticalMinimum; }
        
        /** Runs when the mouse intersects/hovers over the dragger */
        virtual void enter();

        /** Runs when the mouse leaves the dragger */
        virtual void leave();

        /** Sets the main color of the dragger */
        virtual void setColor( const osg::Vec4f& color ) =0;

        /** Sets the color to use when the dragger is entered */
        virtual void setPickColor( const osg::Vec4f& color ) =0;

        /** Add a callback that runs whenever the user moves the dragger */
        OE_DEPRECATED("Use onPositionChanged() instead")
        void addPositionChangedCallback( PositionChangedCallback* callback );

        /** Remove a callback */
        OE_DEPRECATED("Use onPositionChanged() instead")
        void removePositionChangedCallback( PositionChangedCallback* callback );

        //! Notification of a position change
        Callback<void(const Dragger* sender, const osgEarth::GeoPoint& position)> onPositionChanged;


    public: // GeoPositionNode

        virtual void setPosition(const GeoPoint& point);

    public: // osg::Node

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

    protected:
        virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
        void setHover( bool hovered);
        void firePositionChanged();
        
        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::ShapeDrawable* _shapeDrawable;
        osg::Vec4f _pickColor;
        osg::Vec4f _color;
        float _size;
    };
}
