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


#include <Producer/CameraConfig>
#include <Producer/CameraGroup>

#include <osg/Timer>
#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>
#include <osg/Group>
#include <osg/StateSet>
#include <osg/FrameStamp>
#include <osg/DisplaySettings>
#include <osg/CullSettings>
#include <osg/Matrixd>

#include <osgProducer/OsgSceneHandler>
#include <osgProducer/GraphicsContextImplementation>

namespace osgProducer {

class OSGPRODUCER_EXPORT OsgCameraGroup : public Producer::CameraGroup
{
    public :
    
        typedef Producer::CameraGroup::ThreadModel ThreadingModel;

        OsgCameraGroup();

        OsgCameraGroup(Producer::CameraConfig *cfg);

        OsgCameraGroup(const std::string& configFile);

        OsgCameraGroup(osg::ArgumentParser& arguments);

        virtual ~OsgCameraGroup();


        void setApplicationUsage(osg::ApplicationUsage* au) { _applicationUsage = au; }

        osg::ApplicationUsage* getApplicationUsage() { return _applicationUsage; }

        const osg::ApplicationUsage* getApplicationUsage() const { return _applicationUsage; }


        typedef std::vector< osg::ref_ptr<GraphicsContextImplementation> > GraphicsContextList;

        void setGraphicsContextList(GraphicsContextList& gcList) { _gcList = gcList; }

        GraphicsContextList& getGraphicsContextList() { return _gcList;}

        const GraphicsContextList& getGraphicsContextList() const { return _gcList;}


        typedef std::vector < Producer::ref_ptr<osgProducer::OsgSceneHandler> > SceneHandlerList;

        SceneHandlerList& getSceneHandlerList() { return _shvec;}

        const SceneHandlerList& getSceneHandlerList() const { return _shvec;}

        /** Set the scene data to be rendered. The scene graph traversal during
         *  rendering will start at the node passed as parameter.
         *  @param scene The node to be used as the starting point during the
         *         rendering traversal of the scene graph.
         */
        void setSceneData( osg::Node *scene );

        /** Get the scene data being used for rendering.
         *  @return The node being used as the starting point during the
         *          rendering traversal of the scene graph.
         */
        osg::Node *getSceneData() { return _scene_data.get(); }

        const osg::Node *getSceneData() const { return _scene_data.get(); }



        void setSceneDecorator( osg::Group* decorator);

        osg::Group* getSceneDecorator() { return _scene_decorator.get(); }

        const osg::Group* getSceneDecorator() const { return _scene_decorator.get(); }


        osg::Node* getTopMostSceneData();

        const osg::Node* getTopMostSceneData() const;


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


        void setDisplaySettings( osg::DisplaySettings *ds ) { _ds = ds; }

        osg::DisplaySettings *getDisplaySettings() { return _ds.get(); }

        const osg::DisplaySettings *getDisplaySettings() const { return _ds.get(); }

        void setCullSettings( const osg::CullSettings& cs) { _cullSettings = cs; }

        osg::CullSettings& getCullSettings() { return _cullSettings; }

        const osg::CullSettings& getCullSettings() const { return _cullSettings; }



        void setFrameStamp( osg::FrameStamp* fs );

        osg::FrameStamp *getFrameStamp() { return _frameStamp.get(); }

        const osg::FrameStamp *getFrameStamp() const { return _frameStamp.get(); }


        void setGlobalStateSet( osg::StateSet *sset );

        osg::StateSet *getGlobalStateSet() { return _global_stateset.get(); }

        const osg::StateSet *getGlobalStateSet() const { return _global_stateset.get(); }


        void setClearColor( const osg::Vec4& clearColor );

        const osg::Vec4& getClearColor() const;


        void setLODScale( float scale );

        float getLODScale() const;

        void setFusionDistance( osgUtil::SceneView::FusionDistanceMode mode,float value=1.0f);


        /** Set the options to set up SceneView with, see osgUtil::SceneView::Options for available options.*/
        void setRealizeSceneViewOptions(unsigned int options) { _realizeSceneViewOptions = options; }

        unsigned int getRealizeSceneViewOptions() { return _realizeSceneViewOptions; }


        /** RealizeCallback class one should override to provide an the implemention of realize callbacks.
          * Note, this callback overrides the normal call to OsgSceneHandler::init() so it become the your
          * responisibility to call this within your callback if required, it is a safe assumption to
          * always call OsgSceneHandler::init() within your callback..*/
        class OSGPRODUCER_EXPORT RealizeCallback : public osg::Referenced
        {
        public:
            virtual void operator()( OsgCameraGroup& cg, OsgSceneHandler& sh, const Producer::RenderSurface & rs) = 0;

        protected:
            virtual ~RealizeCallback(); // {}
        };

        /** Set the realize callback to use when once the render surfaces are realized.*/
        void setRealizeCallback( RealizeCallback* cb) { _realizeCallback = cb; }

        /** Get the realize callback.*/
        RealizeCallback* getRealizeCallback() { return _realizeCallback.get(); }

        /** Get the const realize callback.*/
        const RealizeCallback* getRealizeCallback() const { return _realizeCallback.get(); }


        void advance();

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

        /** Realize the render surfaces (OpenGL graphics) and various threads, and call any realize callbacks.*/
        virtual bool realize();

        /** Set the model view matrix of the camera group,
          * by individually set all the camera groups's camera.*/
        virtual void setView(const osg::Matrixd& matrix);

        /** Get the model view martrix of the camera group,
          * taking its value for camera 0.*/
        osg::Matrixd getViewMatrix() const;


        virtual void sync();

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

        /** Dispatch a clean up frame that should be called before closing a OsgCameraGroup, i.e. on exit from an app.
          * The clean up frame first release all GL objects associated with all the graphics context associated with
          * the camera group, then runs a special frame that does the actual OpenGL deletion of GL objects for each
          * graphics context. */
        virtual void cleanup_frame();


   protected :


        virtual void setUpSceneViewsWithData();


        osg::ApplicationUsage*                  _applicationUsage;

        osg::ref_ptr<osg::Node>                 _scene_data;
        osg::ref_ptr<osg::Group>                _scene_decorator;

        osg::ref_ptr<osg::StateSet>             _global_stateset;
        osg::Vec4                               _clear_color;

        osgUtil::SceneView::FusionDistanceMode  _fusionDistanceMode;
        float                                   _fusionDistanceValue;

        unsigned int                            _realizeSceneViewOptions;

        GraphicsContextList                     _gcList;
        SceneHandlerList                        _shvec;

        osg::ref_ptr<RealizeCallback>           _realizeCallback;

        osg::ref_ptr<osg::DisplaySettings>      _ds;
        bool                                    _initialized;

        osg::CullSettings                       _cullSettings;

        unsigned int    _frameNumber;
        osg::Timer      _timer;
        osg::Timer_t    _start_tick;
        osg::ref_ptr<osg::FrameStamp>           _frameStamp;

        void _init();
};

}

#endif
