/* -*-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 OSGVIEWER_GRAPHICWINDOW
#define OSGVIEWER_GRAPHICWINDOW 1

#include <osg/GraphicsContext>
#include <osg/Notify>

#include <osgGA/EventQueue>
#include <osgGA/GUIActionAdapter>

#include <osgViewer/Export>


extern "C"
{
    typedef void (* CGraphicsWindowFunction) (void);
}

namespace osgViewer {

class View;


/** Base class for providing Windowing API agnostic access to creating and managing graphics window and events.
  * Note, the GraphicsWindow is subclassed from osg::GraphicsContext, and to provide an implemention you'll need to implement its
  * range of pure virtual functions, you'll find these all have naming convention methodNameImplemention(..).
  * GraphicsWindow adds the event queue on top of the GraphicsContext, thereby adding a mechanism for adapting Windowing events
  * as well as basics graphics context work, you should wire up custom GraphicsWindowImplementation to push their events through
  * into the EventQueue. */
class OSGVIEWER_EXPORT GraphicsWindow : public osg::GraphicsContext, public osgGA::GUIActionAdapter
{
    public:

        GraphicsWindow() { _eventQueue = new osgGA::EventQueue; _eventQueue->setGraphicsContext(this); }

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

        void setEventQueue(osgGA::EventQueue* eventQueue) { _eventQueue = eventQueue; }
        osgGA::EventQueue* getEventQueue() { return _eventQueue.get(); }
        const osgGA::EventQueue* getEventQueue() const { return _eventQueue.get(); }

        /** Check events, return true if events have been received.*/
        virtual bool checkEvents() { return !(_eventQueue->empty()); }

        /** Set the window's position and size.*/
        void setWindowRectangle(int x, int y, int width, int height)
        {
            if (setWindowRectangleImplementation(x ,y ,width, height) && _traits.valid())
            {
                resized(x,y,width,height);
            }
        }

        /** implementation of setWindowRectangle, should be implemented by derived classes */
        virtual bool setWindowRectangleImplementation(int /*x*/, int /*y*/, int /*width*/, int /*height*/) { osg::notify(osg::NOTICE)<<"GraphicsWindow::setWindowRectangleImplementation(..) not implemented."<<std::endl; return false; }

        /** Get the window's position and size.*/
        virtual void getWindowRectangle(int& x, int& y, int& width, int& height) { if (_traits.valid()) { x = _traits->x; y = _traits->y; width = _traits->width; height = _traits->height; }  }

        /** Set Window decoration.*/
        void setWindowDecoration(bool flag)
        {
            if (setWindowDecorationImplementation(flag) && _traits.valid())
            {
                _traits->windowDecoration = flag;
            }
        }

        /** implementation of setWindowDecoration, should be implemented by derived classes */
        virtual bool setWindowDecorationImplementation(bool /*flag*/) {  osg::notify(osg::NOTICE)<<"GraphicsWindow::setWindowDecorationImplementation(..) not implemented."<<std::endl; return false; }


        /** Set Window decoration.*/
        virtual bool getWindowDecoration() const { return _traits.valid() ? _traits->windowDecoration : false; }

        /** Get focus.*/
        virtual void grabFocus() { osg::notify(osg::NOTICE)<<"GraphicsWindow::grabFocus(..) not implemented."<<std::endl; }

        /** Get focus on if the pointer is in this window.*/
        virtual void grabFocusIfPointerInWindow() { osg::notify(osg::NOTICE)<<"GraphicsWindow::grabFocusIfPointerInWindow(..) not implemented."<<std::endl; }

        /** Raise the window to the top.*/
        virtual void raiseWindow() { osg::notify(osg::NOTICE)<<"GraphicsWindow::raiseWindow(..) not implemented."<<std::endl; }

        /** Mouse cursor types, the same ones already present with ancient glut ... */
        enum MouseCursor {
            InheritCursor,
            NoCursor,
            RightArrowCursor,
            LeftArrowCursor,
            InfoCursor,
            DestroyCursor,
            HelpCursor,
            CycleCursor,
            SprayCursor,
            WaitCursor,
            TextCursor,
            CrosshairCursor,
            HandCursor,
            UpDownCursor,
            LeftRightCursor,
            TopSideCursor,
            BottomSideCursor,
            LeftSideCursor,
            RightSideCursor,
            TopLeftCorner,
            TopRightCorner,
            BottomRightCorner,
            BottomLeftCorner
        };

        /** Set the name of the window */
        virtual void setWindowName(const std::string& /*name*/) { osg::notify(osg::NOTICE)<<"GraphicsWindow::setWindowName(..) not implemented."<<std::endl; }

        /** Return the name of the window */
        virtual std::string getWindowName() { return _traits.valid() ? _traits->windowName : ""; }

        /** Switch on/off the cursor.*/
        virtual void useCursor(bool cursorOn) { setCursor(cursorOn ? InheritCursor : NoCursor); }

        /** Set mouse cursor to a specific shape.*/
        virtual void setCursor(MouseCursor /*mouseCursor*/) { osg::notify(osg::NOTICE)<<"GraphicsWindow::setCursor(..) not implemented."<<std::endl; }

        /** Create a new mouse cursor from the usual bitmap data.*/
        //virtual MouseCursor createCursor(const char *data, const char *mask, unsigned w, unsigned h, unsigned hotx, unsigned hoty) { osg::notify(osg::NOTICE)<<"GraphicsWindow::createCursor(..) not implemented."<<std::endl; }

        /** Set sync-to-vblank. */
        virtual void setSyncToVBlank(bool on)
        {
            osg::notify(osg::NOTICE) << "GraphicsWindow::setSyncToVBlank(" << on << ") not implemented." << std::endl;
        }

        bool getSyncToVBlank() const { return _traits.valid() ? _traits->vsync : true; }

        /** Set swap group. */
        virtual void setSwapGroup(bool on, GLuint group, GLuint barrier)
        {
            osg::notify(osg::NOTICE) << "GraphicsWindow::setSwapGroup(" << on << " " << group << " " << barrier << ") not implemented." << std::endl;
        }

        void getSwapGroup(bool& on, GLuint& group, GLuint& barrier) const { on = _traits->swapGroupEnabled; group = _traits->swapGroup; barrier = _traits->swapBarrier; }

    public:

        /** Return whether a valid and usable GraphicsContext has been created.*/
        virtual bool valid() const { osg::notify(osg::NOTICE)<<"GraphicsWindow::valid() not implemented."<<std::endl; return false; }

        /** Realize the GraphicsContext implementation,
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual bool realizeImplementation() { osg::notify(osg::NOTICE)<<"GraphicsWindow::realizeImplementation() not implemented."<<std::endl; return false; }

        /** Return true if the graphics context has been realized, and is ready to use, implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual bool isRealizedImplementation() const  { osg::notify(osg::NOTICE)<<"GraphicsWindow::isRealizedImplementation() not implemented."<<std::endl; return false; }

        /** Close the graphics context implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual void closeImplementation()  { osg::notify(osg::NOTICE)<<"GraphicsWindow::closeImplementation() not implemented."<<std::endl; }

        /** Make this graphics context current implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual bool makeCurrentImplementation()  { osg::notify(osg::NOTICE)<<"GraphicsWindow::makeCurrentImplementation() not implemented."<<std::endl; return false;}

        /** Make this graphics context current with specified read context implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual bool makeContextCurrentImplementation(GraphicsContext* /*readContext*/)  { osg::notify(osg::NOTICE)<<"GraphicsWindow::makeContextCurrentImplementation(..) not implemented."<<std::endl; return false;}

        /** Release the graphics context.*/
        virtual bool releaseContextImplementation() {  osg::notify(osg::NOTICE)<<"GraphicsWindow::releaseContextImplementation(..) not implemented."<<std::endl; return false; }

        /** Pure virtual, Bind the graphics context to associated texture implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual void bindPBufferToTextureImplementation(GLenum /*buffer*/)  { osg::notify(osg::NOTICE)<<"GraphicsWindow::bindPBufferToTextureImplementation(..) not implemented."<<std::endl; }

        /** Swap the front and back buffers implementation.
          * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */
        virtual void swapBuffersImplementation()  { osg::notify(osg::NOTICE)<<"GraphicsWindow:: swapBuffersImplementation() not implemented."<<std::endl; }

    public:

        typedef std::list<osgViewer::View*> Views;
        /** Returns the list of views (osgViewer::View) attached to this GraphicsWindow.
         *  Internally, the method walks through all the cameras and collects all the views attached to the cameras.*/
        void getViews(Views& views);

        // Override from GUIActionAdapter
        virtual void requestRedraw();

        // Override from GUIActionAdapter
        virtual void requestContinuousUpdate(bool /*needed*/=true) {}

        // Override from GUIActionAdapter
        virtual void requestWarpPointer(float /*x*/,float /*y*/) {}


    protected:

        osg::ref_ptr<osgGA::EventQueue> _eventQueue;

};


class GraphicsWindowEmbedded : public GraphicsWindow
{
    public:

        GraphicsWindowEmbedded(osg::GraphicsContext::Traits* traits=0)
        {
            _traits = traits;

            init();

        }

        GraphicsWindowEmbedded(int x, int y, int width, int height)
        {
            _traits = new GraphicsContext::Traits;
            _traits->x = x;
            _traits->y = y;
            _traits->width = width;
            _traits->height = height;

            init();
        }

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

        void 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() );
                }
            }
        }

        // dummy implementations, assume that graphics context is *always* current and valid.
        virtual bool valid() const { return true; }
        virtual bool realizeImplementation() { return true; }
        virtual bool isRealizedImplementation() const  { return true; }
        virtual void closeImplementation() {}
        virtual bool makeCurrentImplementation() { return true; }
        virtual bool releaseContextImplementation() { return true; }
        virtual void swapBuffersImplementation() {}
        virtual void grabFocus() {}
        virtual void grabFocusIfPointerInWindow() {}
        virtual void raiseWindow() {}
};

struct GraphicsWindowFunctionProxy
{
    GraphicsWindowFunctionProxy(CGraphicsWindowFunction function) { (function)(); }
};

#define USE_GRAPICSWINDOW_IMPLEMENTATION(ext) \
    extern "C" void graphicswindow_##ext(void); \
    static osgViewer::GraphicsWindowFunctionProxy graphicswindowproxy_##ext(graphicswindow_##ext);

#if defined(_WIN32)
    #define USE_GRAPHICSWINDOW()  USE_GRAPICSWINDOW_IMPLEMENTATION(Win32)
#elif defined(__APPLE__)
    #define USE_GRAPHICSWINDOW()  USE_GRAPICSWINDOW_IMPLEMENTATION(Carbon)
#else
    #define USE_GRAPHICSWINDOW()  USE_GRAPICSWINDOW_IMPLEMENTATION(X11)
#endif

}

#endif
