#ifndef _CacheReader_h_
#define _CacheReader_h_
#include "gpuCacheGeometry.h"
#include "gpuCacheMaterial.h"
#include <maya/MFileObject.h>
#include <maya/MString.h>
#include <map>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/unordered_map.hpp>
class CacheReader;
class CacheFileEntry;
class GlobalReaderCache
{
public:
    static GlobalReaderCache& theCache();
    static int maxNumOpenFiles();
    
    class CacheReaderProxy
    {
    public:
        typedef boost::shared_ptr<CacheReaderProxy> Ptr;
        ~CacheReaderProxy();
    private:
        GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_1;
    };
    
    
    
    class CacheReaderHolder
    {
    public:
        CacheReaderHolder(boost::shared_ptr<CacheReaderProxy> proxy);
        ~CacheReaderHolder();
        boost::shared_ptr<CacheReader> getCacheReader();
    private:
        boost::shared_ptr<CacheReaderProxy> fProxy;
        boost::shared_ptr<CacheReader>      fReader;
    };
    boost::shared_ptr<CacheReaderProxy> getCacheReaderProxy(
const MFileObject& file);
    
    
    
    
    bool scheduleRead(const CacheFileEntry*  entry, 
                      CacheReaderProxy::Ptr& proxy);
    
    bool pullHierarchy(const CacheFileEntry*            entry, 
                       GPUCache::SubNode::Ptr&          geometry,
                       GPUCache::MaterialGraphMap::Ptr& materials);
    
    bool pullShape(const CacheFileEntry*   entry, 
                   GPUCache::SubNode::Ptr& geometry);
    
    void hintShapeReadOrder(const GPUCache::SubNode& subNode);
    
    void cancelRead(const CacheFileEntry* entry);
    
    void waitForRead(const CacheFileEntry* entry);
    
    bool isInterrupted();
    
    
    
    
    void pauseRead();
    
    void resumeRead();
    
    bool isPaused();
    
    void pauseUntilNotified();
private:
    friend class CacheReader;
    friend class CacheReaderProxy;
    class Impl;
    class Scheduler;
    
    
    GlobalReaderCache(const GlobalReaderCache&);
    const GlobalReaderCache& operator= (const GlobalReaderCache&);
    
    
    
    
    boost::shared_ptr<CacheReader> acquireOwnership(
const MFileObject& file);
    GlobalReaderCache();
    ~GlobalReaderCache();
    boost::shared_ptr<Impl>      fImpl;
    boost::shared_ptr<Scheduler> fScheduler;
};
class CacheReader
{
public:
    typedef boost::shared_ptr<CacheReader> CreateFunction(
const MFileObject& file);
 
    static void registerReader(
const MString& impl, CreateFunction* func);
 
    
    virtual bool valid() const = 0;
    
    
    
    
    
    
    virtual bool validateGeomPath(
    
    
    virtual GPUCache::SubNode::Ptr readScene(
        const MString& geomPath, 
bool needUVs) = 0;
 
    
    
    
    virtual GPUCache::SubNode::Ptr readHierarchy(
        const MString& geomPath, 
bool needUVs) = 0;
 
    
    virtual GPUCache::SubNode::Ptr readShape(
        const MString& geomPath, 
bool needUVs) = 0;
 
    
    virtual GPUCache::MaterialGraphMap::Ptr readMaterials() = 0;
    
    
    virtual bool readAnimTimeRange(GPUCache::TimeInterval& range) = 0;
    
protected:
    
    CacheReader() {}
    virtual ~CacheReader() {}
    
private:
    friend class GlobalReaderCache::Impl;
    static boost::shared_ptr<CacheReader> create(
const MString& impl,
 
    
    CacheReader(const CacheReader&);
    const CacheReader& operator=(const CacheReader&);
    static std::map<std::string,CreateFunction*> fsRegistry;
};
class CacheReaderInterruptException : public std::exception
{
public:
    CacheReaderInterruptException(const std::string& str) throw()
        : fWhat(str)
    {}
    virtual ~CacheReaderInterruptException() throw()
    {}
    virtual const char* what() const throw()
    { return fWhat.c_str(); }
private:
    std::string fWhat;
};
class CacheFileEntry
{
public:
    
    typedef boost::shared_ptr<CacheFileEntry>       MPtr;
    enum BackgroundReadingState {
        kReadingHierarchyInProgress,
        kReadingShapesInProgress,
        kReadingDone
    };
    ~CacheFileEntry();
    static MPtr create( 
const MString& fileName )
 
    {
        return boost::make_shared<CacheFileEntry>(fileName);
    }
    CacheFileEntry& operator=( const CacheFileEntry& rhs );
    GPUCache::SubNode::Ptr                   fCachedGeometry;
    GPUCache::MaterialGraphMap::Ptr          fCachedMaterial;
    GlobalReaderCache::CacheReaderProxy::Ptr fCacheReaderProxy;
    BackgroundReadingState                   fReadState;
private:
    template<class T> friend void boost::checked_delete(T * x);
    GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_1;
    
    CacheFileEntry();
    CacheFileEntry( const CacheFileEntry& rhs );
    CacheFileEntry( 
const MString& fileName );
};
class CacheFileRegistry
{
public:
    typedef boost::unordered_map<MString, CacheFileEntry::MPtr, GPUCache::MStringHash> Map;
    ~CacheFileRegistry();
    static CacheFileRegistry& theCache();
    void                    getAll(std::vector<CacheFileEntry::MPtr>& entries) const;
    CacheFileEntry::MPtr    find(
const MString& key);
    bool                    insert(
const MString& key, 
const CacheFileEntry::MPtr& file);
 
    size_t                  size() const;
    void                    clear();
private:
    CacheFileRegistry();
    static CacheFileRegistry fsSingleton;
    static Map fMap;
};
#endif