/* -*-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 <osg/RefNodePath>

#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,
            TERRAIN_MANIPULATOR     = 8,
            STATE_MANIPULATOR       = 32,
            HEAD_LIGHT_SOURCE       = 64,
            SKY_LIGHT_SOURCE        = 128,
            STATS_MANIPULATOR       = 256,
            VIEWER_MANIPULATOR      = 512,
            ESCAPE_SETS_DONE        = 1024,
            STANDARD_SETTINGS = TRACKBALL_MANIPULATOR|
                                DRIVE_MANIPULATOR |
                                FLIGHT_MANIPULATOR |
                                TERRAIN_MANIPULATOR |
                                STATE_MANIPULATOR |
                                HEAD_LIGHT_SOURCE |
                                STATS_MANIPULATOR |
                                VIEWER_MANIPULATOR |
                                ESCAPE_SETS_DONE
        };
        
        void setUpViewer(unsigned int options=STANDARD_SETTINGS);
        

        /** Set the viewer so it sets done to true once the refrence time equals or exceeds specified elapsed time.
          * Automatically does a setDoneAtElapsedTimeEnabled(true). */
        void setDoneAtElapsedTime(double elapsedTime) { _setDoneAtElapsedTimeEnabled = true; _setDoneAtElapsedTime = elapsedTime; }

        /** Get the elapsed time that will cause done to be set to be true.*/
        double getDoneAtElapsedTime() const { return _setDoneAtElapsedTime; }

        /** Set whether to use a elapsed time to limit the run of the viewer.*/
        void setDoneAtElapsedTimeEnabled(bool enabled) { _setDoneAtElapsedTimeEnabled = enabled; }

        /** Get whether to use a elapsed time to limit the run of the viewer.*/
        bool getDoneAtElapsedTimeEnabled() const { return _setDoneAtElapsedTimeEnabled; }



        /** Set the viewer so it sets done to true once the frame number equals or exceeds specified frame number.
          * Automatically does a setDoneAtFrameNumberEnabled(true). */
        void setDoneAtFrameNumber(unsigned int frameNumber) { _setDoneAtFrameNumberEnabled = true; _setDoneAtFrameNumber = frameNumber; }

        /** Get the frame number that will cause done to be set to be true.*/
        unsigned int getDoneAtFrameNumber() const { return _setDoneAtFrameNumber; }

        /** Set whether to use a frame number to limit the run of the viewer.*/
        void setDoneAtFrameNumberEnabled(bool enabled) { _setDoneAtFrameNumberEnabled = enabled; }

        /** Get whether to use a frame number to limit the run of the viewer.*/
        bool getDoneAtFrameNumberEnabled() const { return _setDoneAtFrameNumberEnabled; }
        


        /** Set the done flag signalling that the viewer exit.*/
        void setDone(bool done) { _done = done; } 
        
        /** Get the done flag which signals that the viewer exit.*/
        bool getDone() const { return _done; } 

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


        /** Set the viewer to take an image snapshot on the last frame() when done is enabled.*/
        void setWriteImageWhenDone(bool enabled) { _writeImageWhenDone = enabled; }

        /** Set the viewer to take an image snapshot on the last frame() when done is enabled.*/
        bool getWriteImageWhenDone() const { return _writeImageWhenDone; }


        /** Set the filename to write to when the viewer takes an image snapshot on the last frame() when done is enabled.*/
        void setWriteImageFileName(const std::string& filename);

        /** Set the filename to write to when the viewer takes an image snapshot on the last frame() when done is enabled.*/
        const std::string& getWriteImageFileName() 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 setUpdateVisitor(osg::NodeVisitor* nv) { _updateVisitor = nv; }
        
        /** get the update visitor.*/
        osg::NodeVisitor* getUpdateVisitor() { return _updateVisitor.get(); }
        
        /** get the const update visitor.*/
        const osg::NodeVisitor* getUpdateVisitor() const { return _updateVisitor.get(); }
        

        void computeActiveCoordindateSystemNodePath();

        void setCoordindateSystemNodePath(const osg::RefNodePath& nodePath) { _coordinateSystemNodePath = nodePath; }
        
        void setCoordindateSystemNodePath(const osg::NodePath& nodePath);
        
        const osg::RefNodePath& getCoordindateSystemNodePath() const { return _coordinateSystemNodePath; }

        /** 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 all Cameras, intersections with the specified subgraph.*/
        bool computeIntersections(float x,float y,unsigned int cameraNum,osg::Node *node,osgUtil::IntersectVisitor::HitList& hits,osg::Node::NodeMask traversalMask = 0xffffffff);

        /** 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,osg::Node::NodeMask traversalMask = 0xffffffff);

        /** compute, from normalized mouse coords, for all Cameras, intersections with specified subgraph.*/
        bool computeIntersections(float x,float y,osg::Node *node,osgUtil::IntersectVisitor::HitList& hits,osg::Node::NodeMask traversalMask = 0xffffffff);

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

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


        const double* getPosition() const { return _position; }
        double getSpeed() const { return _speed; }
        osg::Quat getOrientation() const { return _orientation; }


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

        /** update internal structures w.r.t updated scene data.*/        
        virtual void updatedSceneData();

    protected :


        bool            _setDoneAtElapsedTimeEnabled;
        double          _setDoneAtElapsedTime;

        bool            _setDoneAtFrameNumberEnabled;
        unsigned int    _setDoneAtFrameNumber;

        bool            _done;

        bool            _writeImageWhenDone;
        std::string     _writeImageFileName;

        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;


        osg::RefNodePath                    _coordinateSystemNodePath;

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

        // record the current position and orientation of the view.
        double                              _position[3];
        osg::Quat                           _orientation;
        double                              _speed;
};

}

#endif
