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

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

#include <osgUtil/IntersectVisitor>

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

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

#include <list>

namespace osgProducer {

/** A Producer-based viewer. Just like OpenGL, the core of OSG is independent of
 *  windowing system. The integration between OSG and some windowing system is
 *  delegated to other, non-core parts of OSG (users are also allowed to
 *  integrate OSG with any exotic windowing system they happen to use).
 *  \c Viewer implements the integration between OSG and Producer, AKA Open
 *  Producer (http://www.andesengineering.com/Producer), thus offering an
 *  out-of-the-box, scalable and multi-platform abstraction of the windowing
 *  system.
 */
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();

       /** Specifies options to control some aspects of the
        *  \c Viewer behavior (using the \c setUpViewer() member
        *  function).
        */
        enum ViewerOptions
        {
            /** Do not activate any standard event handlers.  
              * If no event handlers are selected then the users would typically
              * register their own custom event handlers to add user interaction via
              * getEventHandlerList().push_back(myEventHandler) or addCameraManipulator(myCameraManipualtor).*/
            NO_EVENT_HANDLERS = 0,

            /** Add an \c osgGA::TrackballManipulator to manipulate the camera
             *  interactively.
             */
            TRACKBALL_MANIPULATOR = 1,

            /** Add an \c osgGA::DriveManipulator to manipulate the camera
             *  interactively.
             */
            DRIVE_MANIPULATOR = 2,

            /** Add an \c osgGA::FlightManipulator to manipulate the camera
             *  interactively.
             */
            FLIGHT_MANIPULATOR = 4,

            /** Add an \c osgGA::TerrainManipulator to manipulate the camera
             *  interactively.
             */
            TERRAIN_MANIPULATOR     = 8,

            /** Add an \c osgGA::UFOManipulator to manipulate the camera
             *  interactively.
             */
            UFO_MANIPULATOR         = 0x10,

            /** Add an \c osgGA::StateSetManipulator to interactively toggle
             *  some bits of the renderer state (texturing, lightning...)
             */
            STATE_MANIPULATOR       = 32,

            /** Add a light source some point near the camera. */
            HEAD_LIGHT_SOURCE       = 64,

            /** Add a light source above the scene; does nothing if
             *  \c HEAD_LIGHT_SOURCE is also used.
             */
            SKY_LIGHT_SOURCE        = 128,

            /** Add peformance statistics reporting, currently implemented via VIEWER_MANPULATOR. */
            STATS_MANIPULATOR       = 256,

            /** Add an \c osgProducer::ViewerEventHandler that enables lots of
             *  tricks like performance statistics and writing the scene to a
             *  file.
             */
            VIEWER_MANIPULATOR      = 512,

            /** Finish the viewer execution when the ESC key is pressed. */
            ESCAPE_SETS_DONE        = 1024,

            /** Enable a set of standard settings (\c TRACKBALL_MANIPULATOR,
             *  \c DRIVE_MANIPULATOR, \c FLIGHT_MANIPULATOR,
             *  \c TERRAIN_MANIPULATOR, \c STATE_MANIPULATOR,
             *  \c HEAD_LIGHT_SOURCE, \c STATS_MANIPULATOR,
             *  \c VIEWER_MANIPULATOR, \c ESCAPE_SETS_DONE).
             */
            STANDARD_SETTINGS = TRACKBALL_MANIPULATOR|
                                DRIVE_MANIPULATOR |
                                FLIGHT_MANIPULATOR |
                                TERRAIN_MANIPULATOR |
                                UFO_MANIPULATOR |
                                STATE_MANIPULATOR |
                                HEAD_LIGHT_SOURCE |
                                STATS_MANIPULATOR |
                                VIEWER_MANIPULATOR |
                                ESCAPE_SETS_DONE
        };

        /** Set up the viewer, allowing to control some aspects of its behavior.
         *  @param options One or more of the options defined by the
         *         \c ViewerOptions enumeration, combined using the bitwise OR
         *         operator (``<tt>|</tt>'').
         */
        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);

        /** Get 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;

        /** Get the default filename for writing an image snapshot */
        static const char* getDefaultImageFileName();


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


        /** Set the update visitor which does the event traversal of the scene graph. Automatically called by the update() method.*/
        void setEventVisitor(osgGA::EventVisitor* nv) { _eventVisitor = nv; }

        /** Get the update visitor.*/
        osgGA::EventVisitor* getEventVisitor() { return _eventVisitor.get(); }

        /** Get the const update visitor.*/
        const osgGA::EventVisitor* getEventVisitor() const { return _eventVisitor.get(); }


        void computeActiveCoordinateSystemNodePath();

        void setCoordinateSystemNodePath(const osg::NodePath& nodePath);

        osg::NodePath getCoordinateSystemNodePath() const;

        /** 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);

        /** Set the EventQueue - a thread safe queue for registering events.*/
        void setEventQueue(osgGA::EventQueue* eventQueue) { _eventQueue = eventQueue; }

        /** Get the EventQueue.*/
        osgGA::EventQueue* getEventQueue() { return _eventQueue.get(); }

        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 getCameraManipulatorNameList( std::list<std::string> &nameList );
        bool selectCameraManipulatorByName( const std::string &name );
        osgGA::MatrixManipulator *getCameraManipulatorByName( const std::string &name );


        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<osgGA::EventQueue> _eventQueue;

        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::ref_ptr<osgGA::EventVisitor>   _eventVisitor;


        typedef std::vector< osg::observer_ptr<osg::Node> >   ObserveredNodePath;
        ObserveredNodePath                  _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
