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

/* Note, elements of GraphicsWindowX11 have used Prodcer/RenderSurface_X11.cpp as both
 * a guide to use of X11/GLX and copiying directly in the case of setBorder().
 * These elements are license under OSGPL as above, with Copyright (C) 2001-2004  Don Burns.
 */

#ifndef OSGVIEWER_GRAPHICSWINDOWX11
#define OSGVIEWER_GRAPHICSWINDOWX11 1

#include <osgViewer/GraphicsWindow>
#include <osgViewer/api/X11/GraphicsHandleX11>

#include <string.h>

namespace osgViewer
{

class OSGVIEWER_EXPORT GraphicsWindowX11 : public osgViewer::GraphicsWindow, public osgViewer::GraphicsHandleX11
{
    public:

        GraphicsWindowX11(osg::GraphicsContext::Traits* traits):
            _valid(false),
            _eventDisplay(0),
            _parent(0),
            _window(0),
            _visualInfo(0),
            #ifdef OSG_USE_EGL
            _eglDisplay(0),
            _eglSurface(0),
            #else
            _fbConfig(0),
            #endif
            _currentCursor(0),
            _initialized(false),
            _realized(false),
            _timeOfLastCheckEvents(-1.0),
            _lastEventType(0),
            _modifierState(0),
            _numLockMask(0)
        {
            _traits = traits;
            memset(_keyMap, 0, 32);

            init();

            if (valid())
            {
                setState( new osg::State );
                getState()->setGraphicsContext(this);

                if (_traits.valid() && _traits->sharedContext.valid())
                {
                    getState()->setContextID( _traits->sharedContext->getState()->getContextID() );
                    incrementContextIDUsageCount( getState()->getContextID() );   
                }
                else
                {
                    getState()->setContextID( osg::GraphicsContext::createNewContextID() );
                }

            }
        }

        virtual bool isSameKindAs(const Object* object) const { return dynamic_cast<const GraphicsWindowX11*>(object)!=0; }
        virtual const char* libraryName() const { return "osgViewer"; }
        virtual const char* className() const { return "GraphicsWindowX11"; }

        virtual bool valid() const { return _valid; }

        /** Realise the GraphicsContext.*/
        virtual bool realizeImplementation();

        /** Return true if the graphics context has been realised and is ready to use.*/
        virtual bool isRealizedImplementation() const { return _realized; }

        /** Close the graphics context.*/
        virtual void closeImplementation();

        /** Make this graphics context current.*/
        virtual bool makeCurrentImplementation();

        /** Release the graphics context.*/
        virtual bool releaseContextImplementation();

        /** Swap the front and back buffers.*/
        virtual void swapBuffersImplementation();
        
        /** Check to see if any events have been generated.*/
        virtual bool checkEvents();

        /** Set Window decoration.*/
        virtual bool setWindowDecorationImplementation(bool flag);

        /** Get focus.*/
        virtual void grabFocus();
        
        /** Get focus on if the pointer is in this window.*/
        virtual void grabFocusIfPointerInWindow();

        /** Raise specified window */
        virtual void raiseWindow();

        // Override from GUIActionAdapter
        virtual void requestWarpPointer(float x,float y);

        /** Set the window's position and size.*/
        virtual bool setWindowRectangleImplementation(int x, int y, int width, int height);

        /** Set the name of the window */
        virtual void setWindowName(const std::string& name);

        /** Set mouse cursor to a specific shape.*/
        virtual void setCursor(MouseCursor cursor);

        /** WindowData is used to pass in the X11 window handle attached the GraphicsContext::Traits structure. */
        struct WindowData : public osg::Referenced
        {
            WindowData(Window window):
                _window(window) {}

            Window          _window;
        };

    public:

        // X11 specific access functions

        Display* getEventDisplay() const { return _eventDisplay; }
        Display* getDisplayToUse() const ;


        Window& getParent() { return _parent; }
        Window& getWindow() { return _window; }

        Cursor getCurrentCursor() { return _currentCursor; }

    protected:

        ~GraphicsWindowX11();

        Cursor getOrCreateCursor(MouseCursor mouseShape);

        bool createVisualInfo();

        bool createWindow();

        bool setWindow(Window window);

        void init();

        bool checkAndSendEventFullScreenIfNeeded(Display* display, int x, int y, int width, int height, bool windowDecoration);


        void transformMouseXY(float& x, float& y);
        void adaptKey(XKeyEvent& keyevent, int& keySymbol, int& unmodifiedKeySymbol);
        void forceKey(int key, double time, bool state);
        void rescanModifierMapping();
        void getModifierMap(char* keymap) const;
        int getModifierMask() const;
        void syncLocks();
        void flushKeyEvents();

        bool            _valid;
        Display*        _eventDisplay;
        Window          _parent;
        Window          _window;
        XVisualInfo*    _visualInfo;

        #ifdef OSG_USE_EGL
        EGLDisplay      _eglDisplay;
        EGLSurface      _eglSurface;
        #else
        GLXFBConfig     _fbConfig;
        #endif

        Cursor          _currentCursor;

        Atom            _deleteWindow;

        bool            _initialized;
        bool            _realized;
        bool            _ownsWindow;

        double          _timeOfLastCheckEvents;
        int             _lastEventType;
        int             _modifierState;
        int             _numLockMask;

        char            _keyMap[32];
        std::map<MouseCursor,Cursor> _mouseCursorMap;
};

}

#endif
