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

#include <osg/Image>
#include <osg/NodeVisitor>
#include <osg/observer_ptr>
#include <osg/OperationThread>
#include <osg/FrameStamp>

#include <OpenThreads/Mutex>

#include <osgDB/ReaderWriter>

namespace osgDB
{

class OSGDB_EXPORT ImagePager : public osg::NodeVisitor::ImageRequestHandler
{
    public:
    
        ImagePager();
          
        class OSGDB_EXPORT ImageThread : public osg::Referenced, public OpenThreads::Thread
        {
        public:
        
            enum Mode
            {
                HANDLE_ALL_REQUESTS,
                HANDLE_NON_HTTP,
                HANDLE_ONLY_HTTP
            };
        
            ImageThread(ImagePager* pager, Mode mode, const std::string& name);
            
            ImageThread(const ImageThread& dt, ImagePager* pager);
            
            void setDone(bool done) { _done = done; }
            bool getDone() const { return _done; }

            virtual int cancel();
            
            virtual void run();
            
        protected:

            virtual ~ImageThread();
        
            bool            _done;
            Mode            _mode;
            ImagePager*     _pager;
            std::string     _name;
        };


        ImageThread* getImageThread(unsigned int i) { return _imageThreads[i].get(); }
        
        const ImageThread* getImageThread(unsigned int i) const { return _imageThreads[i].get(); }

        unsigned int getNumImageThreads() const { return _imageThreads.size(); }
        

        void setPreLoadTime(double preLoadTime) { _preLoadTime=preLoadTime; }
        virtual double getPreLoadTime() const { return _preLoadTime; }

        virtual osg::Image* readImageFile(const std::string& fileName);

        virtual void requestImageFile(const std::string& fileName,osg::Object* attachmentPoint, int attachmentIndex, double timeToMergeBy, const osg::FrameStamp* framestamp);


        /** Return true if there are pending updates to the scene graph that require a call to updateSceneGraph(double). */
        virtual bool requiresUpdateSceneGraph() const;
        
        /** Merge the changes to the scene graph. */
        virtual void updateSceneGraph(const osg::FrameStamp &frameStamp);
        
        int cancel();

    protected:
    
        virtual ~ImagePager();
        // forward declare  
        struct RequestQueue;
        
        struct SortFileRequestFunctor;
        friend struct SortFileRequestFunctor;
              
        struct ImageRequest : public osg::Referenced
        {
            ImageRequest():
                osg::Referenced(true),
                _timeToMergeBy(0.0),
                _attachmentIndex(-1) {}
            
            double                              _timeToMergeBy;
            std::string                         _fileName;
            osg::ref_ptr<ReaderWriter::Options> _loadOptions;
            osg::observer_ptr<osg::Object>      _attachmentPoint;
            int                                 _attachmentIndex;
            osg::ref_ptr<osg::Image>            _loadedImage;
            RequestQueue*                       _requestQueue;
           
        };
        
        struct RequestQueue : public osg::Referenced
        {
            typedef std::vector< osg::ref_ptr<ImageRequest> > RequestList;

            void sort();
                                    
            RequestList         _requestList;
            OpenThreads::Mutex  _requestMutex;
        };

        
        struct ReadQueue : public RequestQueue
        {
            ReadQueue(ImagePager* pager, const std::string& name);
            
            void block() { _block->block(); }
            
            void release() { _block->release(); }
            
            void updateBlock()
            {
                _block->set((!_requestList.empty() || !_pager->_databasePagerThreadPaused));
            }
            
            void clear();
            
            void add(ImageRequest* imageRequest);
            
            void takeFirst(osg::ref_ptr<ImageRequest>& databaseRequest);
            
            osg::ref_ptr<osg::RefBlock> _block;
            
            ImagePager*                 _pager;
            std::string                 _name;
        };
        
        OpenThreads::Mutex          _run_mutex;
        bool                        _startThreadCalled;

        bool                        _done;
        bool                        _databasePagerThreadPaused;
        
        osg::ref_ptr<ReadQueue>     _readQueue;
        
        typedef std::vector< osg::ref_ptr<ImageThread> > ImageThreads;
        ImageThreads                _imageThreads;
        
        osg::ref_ptr<RequestQueue>  _completedQueue;
        
        double                      _preLoadTime;
};


}

#endif

