/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGGA_EVENT
#define OSGGA_EVENT 1 

#include <osg/Object>
#include <osg/Matrix>
#include <osg/GraphicsContext>
#include <osgGA/Export>

namespace osgGA{


/** Event class for storing Keyboard, mouse and window events.
*/
class OSGGA_EXPORT GUIEventAdapter : public osg::Object
{
public:

        enum MouseButtonMask { 
            LEFT_MOUSE_BUTTON    = 1<<0,
            MIDDLE_MOUSE_BUTTON  = 1<<1,
            RIGHT_MOUSE_BUTTON   = 1<<2
        };

        enum EventType {
            NONE                = 0,
            PUSH                = 1<<0, 
            RELEASE             = 1<<1,
            DOUBLECLICK         = 1<<2,
            DRAG                = 1<<3,
            MOVE                = 1<<4,
            KEYDOWN             = 1<<5,
            KEYUP               = 1<<6,
            FRAME               = 1<<7,
            RESIZE              = 1<<8,
            SCROLL              = 1<<9,
            PEN_PRESSURE        = 1<<10,
            PEN_ORIENTATION     = 1<<11,
            PEN_PROXIMITY_ENTER = 1<<12,
            PEN_PROXIMITY_LEAVE = 1<<13,
            CLOSE_WINDOW        = 1<<14,
            QUIT_APPLICATION    = 1<<15,
            USER                = 1<<16
        };

        enum KeySymbol
        {
            KEY_Space           = 0x20,
            
            KEY_BackSpace       = 0xFF08,        /* back space, back char */
            KEY_Tab             = 0xFF09,
            KEY_Linefeed        = 0xFF0A,        /* Linefeed, LF */
            KEY_Clear           = 0xFF0B,
            KEY_Return          = 0xFF0D,        /* Return, enter */
            KEY_Pause           = 0xFF13,        /* Pause, hold */
            KEY_Scroll_Lock     = 0xFF14,
            KEY_Sys_Req         = 0xFF15,
            KEY_Escape          = 0xFF1B,
            KEY_Delete          = 0xFFFF,        /* Delete, rubout */


            /* Cursor control & motion */

            KEY_Home            = 0xFF50,
            KEY_Left            = 0xFF51,        /* Move left, left arrow */
            KEY_Up              = 0xFF52,        /* Move up, up arrow */
            KEY_Right           = 0xFF53,        /* Move right, right arrow */
            KEY_Down            = 0xFF54,        /* Move down, down arrow */
            KEY_Prior           = 0xFF55,        /* Prior, previous */
            KEY_Page_Up         = 0xFF55,
            KEY_Next            = 0xFF56,        /* Next */
            KEY_Page_Down       = 0xFF56,
            KEY_End             = 0xFF57,        /* EOL */
            KEY_Begin           = 0xFF58,        /* BOL */


            /* Misc Functions */

            KEY_Select          = 0xFF60,        /* Select, mark */
            KEY_Print           = 0xFF61,
            KEY_Execute         = 0xFF62,        /* Execute, run, do */
            KEY_Insert          = 0xFF63,        /* Insert, insert here */
            KEY_Undo            = 0xFF65,        /* Undo, oops */
            KEY_Redo            = 0xFF66,        /* redo, again */
            KEY_Menu            = 0xFF67,        /* On Windows, this is VK_APPS, the context-menu key */
            KEY_Find            = 0xFF68,        /* Find, search */
            KEY_Cancel          = 0xFF69,        /* Cancel, stop, abort, exit */
            KEY_Help            = 0xFF6A,        /* Help */
            KEY_Break           = 0xFF6B,
            KEY_Mode_switch     = 0xFF7E,        /* Character set switch */
            KEY_Script_switch   = 0xFF7E,        /* Alias for mode_switch */
            KEY_Num_Lock        = 0xFF7F,

            /* Keypad Functions, keypad numbers cleverly chosen to map to ascii */

            KEY_KP_Space        = 0xFF80,        /* space */
            KEY_KP_Tab          = 0xFF89,
            KEY_KP_Enter        = 0xFF8D,        /* enter */
            KEY_KP_F1           = 0xFF91,        /* PF1, KP_A, ... */
            KEY_KP_F2           = 0xFF92,
            KEY_KP_F3           = 0xFF93,
            KEY_KP_F4           = 0xFF94,
            KEY_KP_Home         = 0xFF95,
            KEY_KP_Left         = 0xFF96,
            KEY_KP_Up           = 0xFF97,
            KEY_KP_Right        = 0xFF98,
            KEY_KP_Down         = 0xFF99,
            KEY_KP_Prior        = 0xFF9A,
            KEY_KP_Page_Up      = 0xFF9A,
            KEY_KP_Next         = 0xFF9B,
            KEY_KP_Page_Down    = 0xFF9B,
            KEY_KP_End          = 0xFF9C,
            KEY_KP_Begin        = 0xFF9D,
            KEY_KP_Insert       = 0xFF9E,
            KEY_KP_Delete       = 0xFF9F,
            KEY_KP_Equal        = 0xFFBD,        /* equals */
            KEY_KP_Multiply     = 0xFFAA,
            KEY_KP_Add          = 0xFFAB,
            KEY_KP_Separator    = 0xFFAC,       /* separator, often comma */
            KEY_KP_Subtract     = 0xFFAD,
            KEY_KP_Decimal      = 0xFFAE,
            KEY_KP_Divide       = 0xFFAF,

            KEY_KP_0            = 0xFFB0,
            KEY_KP_1            = 0xFFB1,
            KEY_KP_2            = 0xFFB2,
            KEY_KP_3            = 0xFFB3,
            KEY_KP_4            = 0xFFB4,
            KEY_KP_5            = 0xFFB5,
            KEY_KP_6            = 0xFFB6,
            KEY_KP_7            = 0xFFB7,
            KEY_KP_8            = 0xFFB8,
            KEY_KP_9            = 0xFFB9,
            
            /*
             * Auxiliary Functions; note the duplicate definitions for left and right
             * function keys;  Sun keyboards and a few other manufactures have such
             * function key groups on the left and/or right sides of the keyboard.
             * We've not found a keyboard with more than 35 function keys total.
             */

            KEY_F1              = 0xFFBE,
            KEY_F2              = 0xFFBF,
            KEY_F3              = 0xFFC0,
            KEY_F4              = 0xFFC1,
            KEY_F5              = 0xFFC2,
            KEY_F6              = 0xFFC3,
            KEY_F7              = 0xFFC4,
            KEY_F8              = 0xFFC5,
            KEY_F9              = 0xFFC6,
            KEY_F10             = 0xFFC7,
            KEY_F11             = 0xFFC8,
            KEY_F12             = 0xFFC9,
            KEY_F13             = 0xFFCA,
            KEY_F14             = 0xFFCB,
            KEY_F15             = 0xFFCC,
            KEY_F16             = 0xFFCD,
            KEY_F17             = 0xFFCE,
            KEY_F18             = 0xFFCF,
            KEY_F19             = 0xFFD0,
            KEY_F20             = 0xFFD1,
            KEY_F21             = 0xFFD2,
            KEY_F22             = 0xFFD3,
            KEY_F23             = 0xFFD4,
            KEY_F24             = 0xFFD5,
            KEY_F25             = 0xFFD6,
            KEY_F26             = 0xFFD7,
            KEY_F27             = 0xFFD8,
            KEY_F28             = 0xFFD9,
            KEY_F29             = 0xFFDA,
            KEY_F30             = 0xFFDB,
            KEY_F31             = 0xFFDC,
            KEY_F32             = 0xFFDD,
            KEY_F33             = 0xFFDE,
            KEY_F34             = 0xFFDF,
            KEY_F35             = 0xFFE0,

            /* Modifiers */

            KEY_Shift_L         = 0xFFE1,        /* Left shift */
            KEY_Shift_R         = 0xFFE2,        /* Right shift */
            KEY_Control_L       = 0xFFE3,        /* Left control */
            KEY_Control_R       = 0xFFE4,        /* Right control */
            KEY_Caps_Lock       = 0xFFE5,        /* Caps lock */
            KEY_Shift_Lock      = 0xFFE6,        /* Shift lock */

            KEY_Meta_L          = 0xFFE7,        /* Left meta */
            KEY_Meta_R          = 0xFFE8,        /* Right meta */
            KEY_Alt_L           = 0xFFE9,        /* Left alt */
            KEY_Alt_R           = 0xFFEA,        /* Right alt */
            KEY_Super_L         = 0xFFEB,        /* Left super */
            KEY_Super_R         = 0xFFEC,        /* Right super */
            KEY_Hyper_L         = 0xFFED,        /* Left hyper */
            KEY_Hyper_R         = 0xFFEE         /* Right hyper */
        };


        enum ModKeyMask
        {
            MODKEY_LEFT_SHIFT  = 0x0001,
            MODKEY_RIGHT_SHIFT = 0x0002,
            MODKEY_LEFT_CTRL   = 0x0004,
            MODKEY_RIGHT_CTRL  = 0x0008,
            MODKEY_LEFT_ALT    = 0x0010,
            MODKEY_RIGHT_ALT   = 0x0020,
            MODKEY_LEFT_META   = 0x0040,
            MODKEY_RIGHT_META  = 0x0080,
            MODKEY_LEFT_SUPER  = 0x0100,
            MODKEY_RIGHT_SUPER = 0x0200,
            MODKEY_LEFT_HYPER  = 0x0400,
            MODKEY_RIGHT_HYPER = 0x0800,
            MODKEY_NUM_LOCK    = 0x1000,
            MODKEY_CAPS_LOCK   = 0x2000,
            MODKEY_CTRL        = (MODKEY_LEFT_CTRL|MODKEY_RIGHT_CTRL),
            MODKEY_SHIFT       = (MODKEY_LEFT_SHIFT|MODKEY_RIGHT_SHIFT),
            MODKEY_ALT         = (MODKEY_LEFT_ALT|MODKEY_RIGHT_ALT),
            MODKEY_META        = (MODKEY_LEFT_META|MODKEY_RIGHT_META),
            MODKEY_SUPER       = (MODKEY_LEFT_SUPER|MODKEY_RIGHT_SUPER),
            MODKEY_HYPER       = (MODKEY_LEFT_HYPER|MODKEY_RIGHT_HYPER)
        };

        enum MouseYOrientation
        {
            Y_INCREASING_UPWARDS,
            Y_INCREASING_DOWNWARDS
        };

        enum ScrollingMotion 
        {
            SCROLL_NONE,
            SCROLL_LEFT,
            SCROLL_RIGHT,
            SCROLL_UP,
            SCROLL_DOWN,
            SCROLL_2D
        };

        enum TabletPointerType 
        {
            UNKNOWN = 0,
            PEN,
            PUCK,
            ERASER
        };
        
    public:

        GUIEventAdapter();

        GUIEventAdapter(const GUIEventAdapter& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

        META_Object(osgGA, GUIEventAdapter);
        

        /** Get the accumulated event state singleton.
          * Typically all EventQueue will share this single GUIEventAdapter object for tracking
          * the mouse position, keyboard and mouse masks. */
        static osg::ref_ptr<GUIEventAdapter>& getAccumulatedEventState();
        

        /** Set whether this event has been handled by an event handler or not.*/
        void setHandled(bool handled) const { _handled = handled; }

        /** Get whether this event has been handled by an event handler or not.*/
        bool getHandled() const { return _handled; }

        /** Get the Type of the GUI GUIEventAdapter.*/
        void setEventType(EventType Type) { _eventType = Type; }

        /** Get the Type of the GUI GUIEventAdapter.*/
        virtual EventType getEventType() const { return _eventType; }

        /** set time in seconds of event. */
        void setTime(double time) { _time = time; }

        /** get time in seconds of event. */
        double getTime() const { return _time; }

        /** deprecated function for getting time of event. */
        double time() const { return _time; }


        void setGraphicsContext(osg::GraphicsContext* context) { _context = context; }
        const osg::GraphicsContext* getGraphicsContext() const { return _context.get(); }
        

        /** set window rectangle. */
        void setWindowRectangle(int x, int y, int width, int height, bool updateMouseRange = true);

        /** get window x origin.*/
        int getWindowX() const { return _windowX; }

        /** get window y origin.*/
        int getWindowY() const { return _windowY; }

        /** get window width.*/
        int getWindowWidth() const { return _windowWidth; }

        /** get window height.*/
        int getWindowHeight() const { return _windowHeight; }


        /** set key pressed. */
        inline void setKey(int key) { _key = key; }

        /** get key pressed, return -1 if inappropriate for this GUIEventAdapter. */
        virtual int getKey() const { return _key; }

        /** set button pressed/released.*/
        void setButton(int button) { _button = button; }

        /** button pressed/released, return -1 if inappropriate for this GUIEventAdapter.*/
        int getButton() const { return _button; }


        /** set mouse input range. */
        void setInputRange(float Xmin, float Ymin, float Xmax, float Ymax);

        /** get mouse minimum x. */
        float getXmin() const { return _Xmin; }

        /** get mouse maximum x. */
        float getXmax() const { return _Xmax; }

        /** get mouse minimum y. */
        float getYmin() const { return _Ymin; }

        /** get mouse maYimum y. */
        float getYmax() const { return _Ymax; }

        /** set current mouse x position.*/
        void setX(float x) { _mx = x; }

        /** get current mouse x position.*/
        float getX() const { return _mx; }

        /** set current mouse y position.*/
        void setY(float y) { _my = y; }

        /** get current mouse y position.*/
        float getY() const { return _my; }

        /** set current mouse button state */
        void setButtonMask(unsigned int mask) { _buttonMask = mask; }

        /** get current mouse button state */
        unsigned int getButtonMask() const { return _buttonMask; }

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

        unsigned int getModKeyMask() const { return _modKeyMask; }

        /// get the pressure, from a tablet input device (range 0 - 1)
        float getPenPressure()  const { return _pressure; }
        /// sets the pressure from a tablet
        void setPenPressure(float pressure) { _pressure = pressure; }
        /// get the tiltX, from a tablet input device (range -1 - 1)
        float getPenTiltX()  const { return _tiltX; }
        /// sets the tiltX from a tablet
        void setPenTiltX(float tiltX) { _tiltX = tiltX; }
        /// get the tiltY, from a tablet input device (range -1 - 1)
        float getPenTiltY()  const { return _tiltY; }
        /// sets the tiltY from a tablet
        void setPenTiltY(float tiltY) { _tiltY = tiltY; }
        /// get the rotation, from a tablet input device (range 0 - 2PI)
        float getPenRotation()  const { return _rotation; }
        /// sets the rotation from a tablet
        void setPenRotation(float rotation) { _rotation = rotation; }
        /// get the orientation from a tablet input device as a matrix
        const osg::Matrix getPenOrientation() const;
        /// get the current used tablet pointer type
        TabletPointerType getTabletPointerType() const { return _tabletPointerType; }
        /// set the current used tablet pointer type
        void setTabletPointerType(TabletPointerType pt) { _tabletPointerType = pt; }
        
        void setScrollingMotion(ScrollingMotion motion) { _scrollingMotion = motion; }
        
        /** get the scrolling x-delta */
        float getScrollingDeltaX() const { return _scrollingDeltaX; }
        
        /** get the scrolling y-delta */
        float getScrollingDeltaY() const { return _scrollingDeltaY; }
        
        /** sets the scrolling delta to x,y and the type of scrolling to SCROLL_2D */
        void setScrollingMotionDelta(float x, float y) {
            _scrollingMotion = SCROLL_2D;
            _scrollingDeltaX = x;
            _scrollingDeltaY = y;
        }
        
        ScrollingMotion getScrollingMotion() const { return _scrollingMotion; }


        /** return the getX() value normalized to the range of -1 to 1.
          * -1 would be the left hand side of the window.
          * 0.0 would be the middle of the window. 
          * +1 would be the right hand side of the window.*/
        inline float getXnormalized() const { return 2.0f*(getX()-getXmin())/(getXmax()-getXmin())-1.0f; }

        /** return the getY() value normalized to the range of -1 to 1.
          * -1 would be the bottom of the window.
          * 0.0 would be the middle of the window. 
          * +1 would be the top of the window.*/
        inline float getYnormalized() const
        {
            if (_mouseYOrientation==Y_INCREASING_UPWARDS) return 2.0f*(getY()-getYmin())/(getYmax()-getYmin())-1.0f;
            else return -(2.0f*(getY()-getYmin())/(getYmax()-getYmin())-1.0f);
        }

        void setMouseYOrientation(MouseYOrientation myo) { _mouseYOrientation = myo; }
        MouseYOrientation getMouseYOrientation() const { return _mouseYOrientation; }

            

    protected:

        /** Force users to create on heap, so that multiple referencing is safe.*/
        virtual ~GUIEventAdapter();

        mutable bool _handled;        
        EventType _eventType;
        double _time;

        osg::observer_ptr<osg::GraphicsContext> _context;
        int _windowX;
        int _windowY;
        int _windowWidth;
        int _windowHeight;
        int _key;
        int _button;
        float _Xmin,_Xmax;
        float _Ymin,_Ymax;
        float _mx;
        float _my;
        float _pressure;
        float _tiltX;
        float _tiltY;
        float _rotation;
        unsigned int _buttonMask;
        unsigned int _modKeyMask;
        ScrollingMotion _scrollingMotion;
        float _scrollingDeltaX, _scrollingDeltaY;
        MouseYOrientation _mouseYOrientation;
        TabletPointerType _tabletPointerType;
};

}

#endif
