/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 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 OSGPRODUCER_VIEWER
#define OSGPRODUCER_VIEWER 1

#include <osg/NodeVisitor>
#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>
#include <osg/AnimationPath>

#include <osgUtil/IntersectVisitor>

#include <osgGA/GUIActionAdapter>
#include <osgGA/GUIEventHandler>
#include <osgGA/KeySwitchMatrixManipulator>

#include <osgProducer/OsgCameraGroup>
#include <osgProducer/KeyboardMouseCallback>

#include <list>

namespace osgProducer {

class OSGPRODUCER_EXPORT Viewer : public OsgCameraGroup, public osgGA::GUIActionAdapter
{
    public :

        Viewer();

        Viewer(Producer::CameraConfig *cfg);

        Viewer(const std::string& configFile);

        Viewer(osg::ArgumentParser& arguments);

        virtual ~Viewer();


        enum ViewerOptions
        {
            NO_EVENT_HANDLERS       = 0,
            TRACKBALL_MANIPULATOR   = 1,
            DRIVE_MANIPULATOR       = 2,
            FLIGHT_MANIPULATOR      = 4,
            STATE_MANIPULATOR       = 8,
            HEAD_LIGHT_SOURCE       = 16,
            SKY_LIGHT_SOURCE        = 32,
            STATS_MANIPULATOR       = 64,
            VIEWER_MANIPULATOR      = 128,
            ESCAPE_SETS_DONE        = 256,
            STANDARD_SETTINGS = TRACKBALL_MANIPULATOR|
                                DRIVE_MANIPULATOR |
                                FLIGHT_MANIPULATOR |
                                STATE_MANIPULATOR |
                                HEAD_LIGHT_SOURCE |
                                STATS_MANIPULATOR |
                                VIEWER_MANIPULATOR |
                                ESCAPE_SETS_DONE
        };
        
        void setUpViewer(unsigned int options=STANDARD_SETTINGS);
        

        void setDone(bool done) { _done = done; } 
        
        bool getDone() const { return _done; } 

        /** return true if the application is done and should exit.*/
        virtual bool done() const;

        /** Override the Producer::CameraGroup::setViewByMatrix to catch all changes to view.*/
        virtual void setViewByMatrix( const Producer::Matrix & pm);

        /** Set the threading model and then call realize().*/
        virtual bool realize(ThreadingModel thread_model);

        virtual bool realize();

        /** Updated the scene.  Handle any queued up events, do an update traversal and set the CameraGroup's setViewByMatrix if any camera manipulators are active.*/
        virtual void update();
        
        /** set the update visitor which does the update traversal of the scene graph. Automatically called by the update() method.*/
        void setUpdateVistor(osg::NodeVisitor* nv) { _updateVisitor = nv; }
        
        /** get the update visitor.*/
        osg::NodeVisitor* getUpdateVistor() { return _updateVisitor.get(); }
        
        /** get the const update visitor.*/
        const osg::NodeVisitor* getUpdateVistor() const { return _updateVisitor.get(); }
        

        /** Dispatch the cull and draw for each of the Camera's for this frame.*/
        virtual void frame();

        virtual void requestRedraw() {}
        virtual void requestContinuousUpdate(bool) {}
        virtual void requestWarpPointer(float x,float y);


        /** compute, from normalized mouse coords, for sepecified Camera, the pixel coords relative to that Camera's RenderSurface.*/
        bool computePixelCoords(float x,float y,unsigned int cameraNum,float& pixel_x,float& pixel_y);

        /** compute, from normalized mouse coords, for sepecified Camera, the near and far points in worlds coords.*/
        bool computeNearFarPoints(float x,float y,unsigned int cameraNum,osg::Vec3& near, osg::Vec3& far);
        
        /** compute, from normalized mouse coords, for sepecified Camera, intersections with the scene.*/
        bool computeIntersections(float x,float y,unsigned int cameraNum,osgUtil::IntersectVisitor::HitList& hits);

        /** compute, from normalized mouse coords, for all Cameras, intersections with the scene.*/
        bool computeIntersections(float x,float y,osgUtil::IntersectVisitor::HitList& hits);


        void setKeyboardMouse(Producer::KeyboardMouse* kbm);
        Producer::KeyboardMouse* getKeyboardMouse() { return _kbm.get(); }
        const Producer::KeyboardMouse* getKeyboardMouse() const { return _kbm.get(); }

        void setKeyboardMouseCallback(osgProducer::KeyboardMouseCallback* kbmcb);
        osgProducer::KeyboardMouseCallback* getKeyboardMouseCallback() { return _kbmcb.get(); }
        const osgProducer::KeyboardMouseCallback* getKeyboardMouseCallback() const { return _kbmcb.get(); }

        

        typedef std::list< osg::ref_ptr<osgGA::GUIEventHandler> > EventHandlerList;
        EventHandlerList& getEventHandlerList() { return _eventHandlerList; }
        const EventHandlerList& getEventHandlerList() const { return _eventHandlerList; }
        
        osgGA::KeySwitchMatrixManipulator* getKeySwitchMatrixManipulator() { return _keyswitchManipulator.get(); }
        const osgGA::KeySwitchMatrixManipulator* getKeySwitchMatrixManipulator() const { return _keyswitchManipulator.get(); }

        unsigned int addCameraManipulator(osgGA::MatrixManipulator* cm);
        void selectCameraManipulator(unsigned int no);


        void setRecordingAnimationPath(bool on) { _recordingAnimationPath = on; }
        bool getRecordingAnimationPath() const { return _recordingAnimationPath; }

        void setAnimationPath(osg::AnimationPath* path) { _animationPath = path; }
        osg::AnimationPath* getAnimationPath() { return _animationPath.get(); }
        const osg::AnimationPath* getAnimationPath() const { return _animationPath.get(); }


        /** Get the keyboard and mouse usage of this viewer.*/
        virtual void getUsage(osg::ApplicationUsage& usage) const;

    protected :


        bool            _done;


        osg::ref_ptr<Producer::KeyboardMouse> _kbm;

        osg::ref_ptr<osgProducer::KeyboardMouseCallback> _kbmcb;

        EventHandlerList _eventHandlerList;
        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> _keyswitchManipulator;

        osg::ref_ptr<osg::NodeVisitor> _updateVisitor;

        bool                                _recordingAnimationPath;
        osg::ref_ptr<osg::AnimationPath>    _animationPath;

};

}

#endif
