#include "CacheReader.h"
#include "gpuCacheShapeNode.h"
#include "gpuCacheUtil.h"
#include <list>
#include <vector>
#include <boost/bimap.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/unordered_set_of.hpp>
#include <boost/foreach.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/hashed_index.hpp>
#define BOOST_DATE_TIME_NO_LIB
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
#include <tbb/compat/condition_variable>
#include <tbb/mutex.h>
#include <tbb/task.h>
#define DURATION_SECONDS(x) (tbb::tick_count::interval_t(double(x)))
namespace tbb {
    using tbb::interface5::unique_lock;
    using tbb::interface5::condition_variable;
};
#include <iostream>
#ifdef _WIN32
    #include <stdio.h>
#else
    #include <sys/resource.h>
    #include <limits.h>
#endif
#include <maya/MFnDagNode.h>
#include <maya/MGlobal.h>
#include <maya/MViewport2Renderer.h>
using namespace GPUCache;
class GlobalReaderCache::Impl {
public:
    Impl(int initNumFileHandles)
        :  fMaxNumFileHandles(initNumFileHandles), fHitCount(0), fGetCount(0)
    {
        assert(fMaxNumFileHandles > 10);
    }
    ~Impl()
    {
    }
    boost::shared_ptr<CacheReader> getCacheReader(
const MFileObject& file)
    {
        
        std::string key = resolvedFullName.
asChar();
        boost::shared_ptr<CacheReader> value;
        tbb::unique_lock<tbb::mutex> lock(fMutex);
        while (!value)
        {
            
            const LeftIterator iter = fData.left.find(key);
            if (iter == fData.left.end()) {
                
                
                
                
                if (fData.size() == (size_t)fMaxNumFileHandles) {
                    
                    RightIterator leastUsed;
                    for (leastUsed = fData.right.begin();
                            leastUsed != fData.right.end(); ++leastUsed) {
                        if ((*leastUsed).get_right() == 0) {
                            
                            break;
                        }
                    }
                    if (leastUsed != fData.right.end()) {
                        
                        fData.right.erase(leastUsed);
                    }
                }
                if (fData.size() < (size_t)fMaxNumFileHandles) {
                    
                    value = createReader(file);
                    fData.left[key] = 1;
                    fData.right.back().info = value;
                }
            }
            else {
                
                
                fData.right.relocate(fData.right.end(),
                    fData.project_right(iter));
                value = (*iter).info;
                ++(*iter).get_right();
                ++fHitCount;
            }
            ++fGetCount;
            
            
            
            if (!value) {
                fCond.wait(lock);
            }
        }
        
        return value;
    }
    {
        std::string key = resolvedFullName.
asChar();
        tbb::unique_lock<tbb::mutex> lock(fMutex);
        
        FileRefCountIterator fileRefCountIter = fFileRefCount.find(key);
        if (fileRefCountIter != fFileRefCount.end()) {
            
            ++(*fileRefCountIter).second;
        }
        else {
            
            fFileRefCount[key] = 1;
        }
    }
    {
        std::string key = resolvedFullName.
asChar();
        tbb::unique_lock<tbb::mutex> lock(fMutex);
        
        FileRefCountIterator fileRefCountIter = fFileRefCount.find(key);
        if (fileRefCountIter != fFileRefCount.end()) {
            
            if (--(*fileRefCountIter).second == 0) {
                
                
                
                
                fFileRefCount.erase(fileRefCountIter);
                LeftIterator iter = fData.left.find(key);
                if (iter != fData.left.end()) {
                    fData.left.erase(iter);
                }
            }
        }
    }
    boost::shared_ptr<CacheReader> acquireOwnership(
const MFileObject& file)
    {
        
        return getCacheReader(file);
    }
    {
        std::string key = resolvedFullName.
asChar();
        tbb::unique_lock<tbb::mutex> lock(fMutex);
        
        const LeftIterator iter = fData.left.find(key);
        if (iter != fData.left.end()) {
            
            if (--(*iter).get_right() == 0) {
                
                fCond.notify_one();
            }
        }
        else {
            
            assert(iter != fData.left.end());
        }
    }
    void print()
    {
        tbb::unique_lock<tbb::mutex> lock(fMutex);
        
        std::cout << "File Reader Cache" << std::endl
            << "    Get Count: " << fGetCount << std::endl
            << "    Hit Count: " << fHitCount << std::endl
            << "    Hit Ratio: " << (1.0f * fHitCount / fGetCount) << std::endl;
        std::cout << "LRU list: " << fData.size() << std::endl;
        for (RightIterator iter = fData.right.begin(); iter != fData.right.end(); iter++) {
            std::cout << "    " << (*iter).get_left() << std::endl;
        }
        std::cout << std::endl;
    }
private:
    
    Impl(const Impl&);
    const Impl& operator= (const Impl&);
    boost::shared_ptr<CacheReader> createReader(
const MFileObject& file)
    {
        return CacheReader::create("Alembic", file);
    }
    typedef boost::bimaps::unordered_set_of<std::string>   LeftViewType;
    typedef boost::bimaps::list_of<int>                    RightViewType;
    typedef boost::bimaps::with_info<boost::shared_ptr<CacheReader> > InfoViewType;
    typedef boost::bimap<LeftViewType, RightViewType, InfoViewType>   BimapType;
    typedef BimapType::value_type      BimapValueType;
    typedef BimapType::left_iterator   LeftIterator;
    typedef BimapType::right_iterator  RightIterator;
    typedef std::map<std::string,int>  FileRefCountType;
    typedef FileRefCountType::iterator FileRefCountIterator;
    int              fMaxNumFileHandles;
    BimapType        fData;
    FileRefCountType fFileRefCount;
    tbb::mutex                  fMutex;
    tbb::condition_variable     fCond;
    int        fHitCount;
    int        fGetCount;
};
#if 0
#define DEBUG_SCHEDULER 1
#endif
class GlobalReaderCache::Scheduler
{
public:
    
    class BGReadHierarchyTask : public tbb::task
    {
    public:
        BGReadHierarchyTask(Scheduler*             scheduler,
                            const CacheFileEntry*  entry,
                            CacheReaderProxy::Ptr& proxy,
            : fScheduler(scheduler),
              fCacheFileEntry(entry),
              fProxy(proxy), 
              fGeometryPath(geometryPath)
        {}
        virtual ~BGReadHierarchyTask()
        {}
        virtual task* execute()
        {
            
            SubNode::Ptr          geometry;
            MString               validatedGeometryPath = fGeometryPath;
 
            MaterialGraphMap::Ptr materials;
            try {
                CacheReaderHolder holder(fProxy);
                const boost::shared_ptr<CacheReader> cacheReader = holder.getCacheReader();
                if (cacheReader && cacheReader->valid()) {
                    
                    cacheReader->validateGeomPath(fGeometryPath, validatedGeometryPath);
                    
                    geometry = cacheReader->readHierarchy(
                        validatedGeometryPath, !Config::isIgnoringUVs());
                    
                    materials = cacheReader->readMaterials();
                }
            }
            catch (CacheReaderInterruptException&) {
#ifdef DEBUG_SCHEDULER
                std::cout << "[gpuCache] Background reading is interrupted" << std::endl;
#endif
            }
            catch (std::exception&) {
#ifdef DEBUG_SCHEDULER
                std::cout << "[gpuCache] Background reading is interrupted for unknown reason" << std::endl;
#endif
            }
            
            fScheduler->hierarchyTaskFinished(fCacheFileEntry, 
                geometry, validatedGeometryPath, materials, fProxy);
            fProxy.reset();
            return 0;
        }
    private:
        Scheduler*            fScheduler;
        const CacheFileEntry* fCacheFileEntry;
        CacheReaderProxy::Ptr fProxy;
    };
    
    class BGReadShapeTask : public tbb::task
    {
    public:
        BGReadShapeTask(Scheduler*             scheduler,
                        const CacheFileEntry*  entry,
                        CacheReaderProxy::Ptr& proxy,
            : fScheduler(scheduler),
              fCacheFileEntry(entry),
              fProxy(proxy), 
              fPrefix(prefix),
              fGeometryPath(geometryPath)
        {}
        virtual ~BGReadShapeTask()
        {}
        virtual task* execute()
        {
            
            SubNode::Ptr geometry;
            try {
                CacheReaderHolder holder(fProxy);
                const boost::shared_ptr<CacheReader> cacheReader = holder.getCacheReader();
                if (cacheReader && cacheReader->valid()) {
                    
                    geometry = cacheReader->readShape(fPrefix + fGeometryPath, !Config::isIgnoringUVs());
                }
            }
            catch (CacheReaderInterruptException&) {
#ifdef DEBUG_SCHEDULER
                std::cout << "[gpuCache] Background reading is interrupted" << std::endl;
#endif
            }
            catch (std::exception&) {
#ifdef DEBUG_SCHEDULER
                std::cout << "[gpuCache] Background reading is interrupted for unknown reason" << std::endl;
#endif
            }
            
            fProxy.reset();
            
            fScheduler->shapeTaskFinished(fCacheFileEntry, geometry, fGeometryPath);
            return 0;
        }
    private:
        Scheduler*            fScheduler;
        const CacheFileEntry* fCacheFileEntry;
        CacheReaderProxy::Ptr fProxy;
    };
    class WorkItem
    {
    public:
        typedef boost::shared_ptr<WorkItem> Ptr;
        enum WorkItemType {
            kHierarchyWorkItem,
            kShapeWorkItem,
        };
        WorkItem(Scheduler*             scheduler,
                 const CacheFileEntry*  entry, 
                 CacheReaderProxy::Ptr& proxy)
            : fCacheFileEntry(entry), 
              fSubNode(NULL),
              fValidatedGeometryPath(geometryPath), 
              fCancelled(false),
              fType(kHierarchyWorkItem)
        {
            
            fTask = new (tbb::task::allocate_root())
                BGReadHierarchyTask(scheduler, entry, proxy, geometryPath);
        }
        WorkItem(Scheduler*             scheduler,
                 const CacheFileEntry*  entry, 
                 const SubNode*         subNode,
                 CacheReaderProxy::Ptr& proxy)
            : fCacheFileEntry(entry), 
              fSubNode(subNode),
              fValidatedGeometryPath(geometryPath), 
              fCancelled(false),
              fType(kShapeWorkItem)
        {
            
            fTask = new (tbb::task::allocate_root())
                BGReadShapeTask(scheduler, entry, proxy, prefix, geometryPath);
        }
        ~WorkItem()
        { 
            
            if (fTask) {
                assert(fTask->state() != tbb::task::executing);
                tbb::task::destroy(*fTask);
            }
        }
        void startTask()
        {
            assert(fTask);
            if (fTask) {
                tbb::task::enqueue(*fTask);
            }
        }
        void cancelTask()
        {
            assert(fTask);
            fCancelled = true;
        }
        void finishTask(SubNode::Ptr& geometry, 
                        const MString& validatedGeometryPath, 
 
                        MaterialGraphMap::Ptr& materials)
        {
            
            fTask = NULL;
            fGeometry = geometry;
            fMaterials = materials;
            fValidatedGeometryPath = validatedGeometryPath;
        }
        const CacheFileEntry*       cacheFileEntry() const  { return fCacheFileEntry; }
        const SubNode*              subNode()   const  { return fSubNode;  }
        SubNode::Ptr&               geometry()         { return fGeometry; }
        const MString&              validatedGeometryPath()
 const { 
return fValidatedGeometryPath; }
 
        MaterialGraphMap::Ptr&      materials()     { return fMaterials; }
        bool          isCancelled() const { return fCancelled; }
        WorkItemType  type() const        { return fType; }
    private:
        const CacheFileEntry* fCacheFileEntry;
        const SubNode*        fSubNode;
        tbb::task*            fTask;
        SubNode::Ptr          fGeometry;
        MaterialGraphMap::Ptr fMaterials;
        bool                  fCancelled;
        WorkItemType          fType;
    };
    Scheduler()
    {
        fInterrupted = false;
        fPaused      = false;
        fRefreshTime = boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time();
    }
    ~Scheduler() {}
    
    bool scheduleRead(const CacheFileEntry*  entry, 
                      CacheReaderProxy::Ptr& proxy
    )
    {
        
        assert(entry && proxy);
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        if (!entry) return false;
        
        WorkItem::Ptr item(new WorkItem(this, entry, geometryPath, proxy));
#ifdef DEBUG_SCHEDULER
        MString fileName = entry->fCacheFileName;
 
        std::cout << 
"[gpuCache] Schedule background reading of " << fileName.
asChar() << std::endl;
#endif
        if (fTaskRunning) {
            
            fHierarchyTaskQueue.push_back(item);
        }
        else {
            
            fTaskRunning = item;
            fTaskRunning->startTask();
        }
        return true;
    }
    
    bool pullHierarchy(const CacheFileEntry*  entry, 
                       SubNode::Ptr&          geometry,
                       MaterialGraphMap::Ptr& materials
    )
    {
        
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        if (!entry) return false;
        
        WorkItem::Ptr resultItem;
        HierarchyItemPtrListHashIterator iter = fHierarchyTaskDone.get<1>().find(entry);
        if (iter != fHierarchyTaskDone.get<1>().end()) {
            resultItem = *iter;
            fHierarchyTaskDone.get<1>().erase(iter);
        }
        
        if (resultItem) {
            assert(resultItem->type() == WorkItem::kHierarchyWorkItem);
#ifdef DEBUG_SCHEDULER
            MString fileName = entry->fCacheFileName;
 
            std::cout << 
"[gpuCache] Background reading (hierarchy) of " << name.
asChar() << 
" finished." << std::endl;
#endif
            
            geometry              = resultItem->geometry();
            validatedGeometryPath = resultItem->validatedGeometryPath();
            materials             = resultItem->materials();
            return true;
        }
#ifdef DEBUG
        
        bool inProgress = false;
        if (fTaskRunning && fTaskRunning->cacheFileEntry() == entry) {
            inProgress = true;
        }
        if (fHierarchyTaskQueue.get<1>().find(entry) != fHierarchyTaskQueue.get<1>().end()) {
            inProgress = true;
        }
        assert(inProgress);
#endif
        
        return false;
    }
    
    bool pullShape(const CacheFileEntry*   entry, 
                   GPUCache::SubNode::Ptr& geometry
    )
    {
        
        assert(geometry);
        if (!geometry) return false;
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        if (!entry) return false;
        
        std::vector<WorkItem::Ptr> resultItems;
        
        std::pair<ShapeItemPtrListHashIterator,ShapeItemPtrListHashIterator> range =
                fShapeTaskDone.get<1>().equal_range(entry);
        for (ShapeItemPtrListHashIterator iter = range.first; iter != range.second; iter++) {
            resultItems.push_back(*iter);
        }
        fShapeTaskDone.get<1>().erase(range.first, range.second);
        
        BOOST_FOREACH (const WorkItem::Ptr& item, resultItems) {
            assert(item->type() == WorkItem::kShapeWorkItem);
#ifdef DEBUG_SCHEDULER
            MString fileName = entry->fCacheFileName;
 
            std::cout << 
"[gpuCache] Background reading (shape) of " << fileName.
asChar() << 
" finished." << std::endl;
#endif
            
            SubNode::Ptr shape = item->geometry();
            MString      path  = item->validatedGeometryPath();
 
            if (shape && path.
length() > 0) {
 
                ReplaceSubNodeData(geometry, shape, path);
            }
        }
        
        if (!resultItems.empty()) {
            SubNodeTransparentTypeVisitor transparentTypeVisitor;
            geometry->accept(transparentTypeVisitor);
        }
        
        bool inProgress = false;
        if (fTaskRunning && fTaskRunning->cacheFileEntry() == entry) {
            inProgress = true;
        }
        if (fShapeTaskQueue.get<1>().find(entry) != fShapeTaskQueue.get<1>().end()) {
            inProgress = true;
        }
        
        return !inProgress;
    }
    void hintShapeReadOrder(const SubNode& subNode)
    {
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        
        SubNodePtrList::nth_index<1>::type::iterator iter = 
            fShapeTaskOrder.get<1>().find(&subNode);
        if (iter == fShapeTaskOrder.get<1>().end()) {
            
            
            fShapeTaskOrder.push_front(&subNode);
        }
        else {
            
            fShapeTaskOrder.get<1>().erase(iter);
            fShapeTaskOrder.push_front(&subNode);
        }
    }
    
    void cancelRead(const CacheFileEntry* entry)
    {
        
        assert(entry);
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        if (!entry) return;
#ifdef DEBUG_SCHEDULER
        MString fileName = entry->fCacheFileName;
 
        std::cout << 
"[gpuCache] Background reading of " << fileName.
asChar() << 
" canceled" << std::endl;
#endif
        
        fHierarchyTaskQueue.get<1>().erase(entry);
        
        fHierarchyTaskDone.get<1>().erase(entry);
        
        fShapeTaskQueue.get<1>().erase(entry);
        
        fShapeTaskDone.get<1>().erase(entry);
        
        if (fTaskRunning && fTaskRunning->cacheFileEntry() == entry) {
            fTaskRunning->cancelTask();
            fInterrupted = true;
        }
        
        fCondition.notify_all();
    }
    void waitForRead(const CacheFileEntry* entry)
    {
        
        assert(entry);
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        if (!entry) return;
#ifdef DEBUG_SCHEDULER
        MString fileName = entry->fCacheFileName;
 
        std::cout << 
"[gpuCache] Waiting for background reading of " << fileName.
asChar() << std::endl;
#endif
        while (true) {
            
            bool inProgress = false;
            if (fTaskRunning && fTaskRunning->cacheFileEntry() == entry) {
                inProgress = true;
            }
            if (fHierarchyTaskQueue.get<1>().find(entry) != fHierarchyTaskQueue.get<1>().end()) {
                inProgress = true;
            }
            if (fShapeTaskQueue.get<1>().find(entry) != fShapeTaskQueue.get<1>().end()) {
                inProgress = true;
            }
            
            if (!inProgress) break;
            
            fCondition.wait_for(lock, DURATION_SECONDS(3));
        }
    }
    bool isInterrupted()
    {
        return fInterrupted;
    }
    void pauseRead()
    {
        tbb::unique_lock<tbb::mutex> lock(fPauseMutex);
        
        
        
        assert(!fPaused);
        fPaused = true;
    }
    void resumeRead()
    {
        tbb::unique_lock<tbb::mutex> lock(fPauseMutex);
        
        assert(fPaused);
        fPaused = false;
        
        fPauseCond.notify_all();
    }
    bool isPaused()
    {
        return fPaused;
    }
    void pauseUntilNotified()
    {
        tbb::unique_lock<tbb::mutex> lock(fPauseMutex);
        
        if (fPaused) {
            
            fPauseCond.wait(lock);
        }
    }
private:
    friend class WorkItem;
    void hierarchyTaskFinished(const CacheFileEntry*  entry, 
                               SubNode::Ptr&          geometry, 
                               const MString&         validatedGeometryPath,
 
                               MaterialGraphMap::Ptr& materials,
                               CacheReaderProxy::Ptr& proxy)
    {
        
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        
        assert(fTaskRunning);
        assert(fTaskRunning->cacheFileEntry() == entry);
        assert(fTaskRunning->type() == WorkItem::kHierarchyWorkItem);
        
        fTaskRunning->finishTask(geometry, validatedGeometryPath, materials);
        
        bool isCancelled = fTaskRunning->isCancelled();
        if (!isCancelled) {
            fHierarchyTaskDone.push_back(fTaskRunning);
            
            ShapePathVisitor::ShapePathAndSubNodeList shapeGeomPaths;
            if (geometry) {
                ShapePathVisitor shapePathVisitor(shapeGeomPaths);
                geometry->accept(shapePathVisitor);
            }
            
            int lastStep = validatedGeometryPath.
rindexW(
'|');
 
            if (lastStep > 0) {
                prefix = validatedGeometryPath.
substringW(0, lastStep - 1);
            }
            
            BOOST_FOREACH (const ShapePathVisitor::ShapePathAndSubNode& pair, shapeGeomPaths) {
                WorkItem::Ptr item(new WorkItem(
                    this, 
                    entry, 
                    pair.second,   
                    prefix,        
                    pair.first,    
                    proxy
                ));
                fShapeTaskQueue.push_back(item);
            }
        }
        fTaskRunning.reset();
        fInterrupted = false;
        
        startNextTask();
        
        ShapeNode::dirtyVP2Geometry( entry->fResolvedCacheFileName );
        
        fCondition.notify_all();
        
        postRefresh();
    }
    void shapeTaskFinished(const CacheFileEntry* entry, 
                           SubNode::Ptr&         geometry, 
    {
        
        
        tbb::unique_lock<tbb::mutex> lock(fBigMutex);
        
        assert(fTaskRunning);
        assert(fTaskRunning->cacheFileEntry() == entry);
        assert(fTaskRunning->type() == WorkItem::kShapeWorkItem);
        
        MaterialGraphMap::Ptr noMaterials;
        fTaskRunning->finishTask(geometry, geometryPath, noMaterials);
        
        bool isCancelled = fTaskRunning->isCancelled();
        if (!isCancelled) {
            fShapeTaskDone.push_back(fTaskRunning);
        }
        fTaskRunning.reset();
        fInterrupted = false;
        
        startNextTask();
        
        fCondition.notify_all();
        
        postRefresh();
    }
    void startNextTask()
    {
        
        if (!fHierarchyTaskQueue.empty()) {
            fTaskRunning = fHierarchyTaskQueue.front();
            fTaskRunning->startTask();
            fHierarchyTaskQueue.pop_front();
            return;
        }
        
        while (!fShapeTaskOrder.empty()) {
            const SubNode* subNode = fShapeTaskOrder.front();
            assert(subNode);
            fShapeTaskOrder.pop_front();
            
            ShapeItemPtrListSubNodeHashIterator iter = fShapeTaskQueue.get<2>().find(subNode);
            if (iter != fShapeTaskQueue.get<2>().end()) {
                fTaskRunning = *iter;
                fTaskRunning->startTask();
                fShapeTaskQueue.get<2>().erase(iter);
                return;
            }
        }
        
        if (!fShapeTaskQueue.empty()) {
            fTaskRunning = fShapeTaskQueue.front();
            fTaskRunning->startTask();
            fShapeTaskQueue.pop_front();
            return;
        }
    }
    void postRefresh()
    {
        
        boost::posix_time::ptime currentTime = 
            boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time();
        
        if (!fTaskRunning) {
            fRefreshTime = currentTime;
        }
        
        boost::posix_time::time_duration interval = currentTime - fRefreshTime;
        if (interval.total_milliseconds() >= (int)Config::backgroundReadingRefresh()) {
            fRefreshTime = currentTime;
        }
    }
private:
    tbb::mutex                   fBigMutex;
    tbb::condition_variable      fCondition;
    
    typedef boost::multi_index_container<
        WorkItem::Ptr,
        boost::multi_index::indexed_by<
            boost::multi_index::sequenced<>,
            boost::multi_index::hashed_unique<BOOST_MULTI_INDEX_CONST_MEM_FUN(WorkItem,const CacheFileEntry*,cacheFileEntry)>
        >
    > HierarchyItemPtrList;
    typedef HierarchyItemPtrList::nth_index<1>::type::iterator HierarchyItemPtrListHashIterator;
    
    typedef boost::multi_index_container<
        WorkItem::Ptr,
        boost::multi_index::indexed_by<
            boost::multi_index::sequenced<>,
            boost::multi_index::hashed_non_unique<BOOST_MULTI_INDEX_CONST_MEM_FUN(WorkItem,const CacheFileEntry*,cacheFileEntry)>,
            boost::multi_index::hashed_unique<BOOST_MULTI_INDEX_CONST_MEM_FUN(WorkItem,const SubNode*,subNode)>
        >
    > ShapeItemPtrList;
    typedef ShapeItemPtrList::nth_index<1>::type::iterator ShapeItemPtrListHashIterator;
    typedef ShapeItemPtrList::nth_index<2>::type::iterator ShapeItemPtrListSubNodeHashIterator;
    WorkItem::Ptr        fTaskRunning;
    HierarchyItemPtrList fHierarchyTaskQueue;
    HierarchyItemPtrList fHierarchyTaskDone;
    ShapeItemPtrList     fShapeTaskQueue;
    ShapeItemPtrList     fShapeTaskDone;
    typedef boost::multi_index_container<
        const SubNode*,
        boost::multi_index::indexed_by<
            boost::multi_index::sequenced<>,
            boost::multi_index::hashed_unique<boost::multi_index::identity<const SubNode*> >
        >
    > SubNodePtrList;
    SubNodePtrList fShapeTaskOrder;
    tbb::atomic<bool>        fInterrupted;
    boost::posix_time::ptime fRefreshTime;
    
    
    tbb::atomic<bool>           fPaused;
    tbb::mutex                  fPauseMutex;
    tbb::condition_variable     fPauseCond;
};
CacheFileEntry::CacheFileEntry()
:   fCachedGeometry()
,   fCachedMaterial()
,   fCacheReaderProxy()
,   fReadState(CacheFileEntry::kReadingDone)
{}
CacheFileEntry::CacheFileEntry( const CacheFileEntry& rhs )
{
    (*this) = rhs;
}
CacheFileEntry::CacheFileEntry( 
const MString& fileName )
:   fCacheFileName(fileName)
,   fCachedGeometry()
,   fCachedMaterial()
,   fCacheReaderProxy()
,   fReadState(CacheFileEntry::kReadingDone)
{
    
    {
        fCacheReaderProxy = GlobalReaderCache::theCache().getCacheReaderProxy(cacheFile);
    }
}
CacheFileEntry::~CacheFileEntry()
{}
CacheFileEntry& CacheFileEntry::operator=( const CacheFileEntry& rhs )
{
    fCacheFileName = rhs.fCacheFileName;
    fCachedGeometry = rhs.fCachedGeometry;
    fCachedMaterial = rhs.fCachedMaterial;
    fCacheReaderProxy = rhs.fCacheReaderProxy;
    fReadState = rhs.fReadState;
    return (*this);
}
CacheFileRegistry CacheFileRegistry::fsSingleton;
CacheFileRegistry::Map CacheFileRegistry::fMap;
CacheFileRegistry::CacheFileRegistry()
{}
CacheFileRegistry::~CacheFileRegistry()
{}
CacheFileRegistry& CacheFileRegistry::theCache()
{
    return fsSingleton;
}
void CacheFileRegistry::getAll( std::vector<CacheFileEntry::MPtr>& entries ) const
{
    Map::iterator it = fMap.begin();
    for( ; it != fMap.end(); it++ )
    {
        entries.push_back(it->second);
    }
}
CacheFileEntry::MPtr CacheFileRegistry::find( 
const MString& key )
{
    Map::iterator it = fMap.find(key);
    if( it != fMap.end() )
    {
        return it->second;
    }
    return CacheFileEntry::MPtr();
}
bool CacheFileRegistry::insert( 
const MString& key, 
const CacheFileEntry::MPtr& entry )
 
{
    
    Map::iterator it = fMap.find(key);
    if( it != fMap.end() )
    {
        
        it->second = entry;
        return true;
    }
    
    Map::value_type item( key, entry );
    return fMap.insert( item ).second;
}
bool CacheFileRegistry::remove( 
const MString& key )
 
{
    Map::iterator it = fMap.find(key);
    if( it != fMap.end() )
    {
        fMap.erase(it);
        return true;
    }
    return false;
}
size_t CacheFileRegistry::size() const
{
    return fMap.size();
}
bool CacheFileRegistry::cleanUp( 
const MString& key )
 
{
    Map::iterator it = fMap.find(key);
    if( it != fMap.end() )
    {
        CacheFileEntry::MPtr& entry = it->second;
        if( entry.use_count() == 1 )
        {
            
            
            if( entry->fReadState != CacheFileEntry::kReadingDone )
            {
                GlobalReaderCache::theCache().cancelRead(entry.get());
                entry->fReadState = CacheFileEntry::kReadingDone;
            }
            fMap.erase(it);
            return true;
        }
    }
    return false;
}
void CacheFileRegistry::clear()
{
    fMap.clear();
}
GlobalReaderCache& GlobalReaderCache::theCache()
{
    static GlobalReaderCache gsReaderCache;
    return gsReaderCache;
}
int GlobalReaderCache::maxNumOpenFiles()
{
    
    const int mayaOpenFiles = 100;
    
    int softLimit;
#ifdef _WIN32
    
    _setmaxstdio(2048);
    softLimit = _getmaxstdio();
#else
    
    rlimit rlp;
    getrlimit(RLIMIT_NOFILE, &rlp);
    
    rlp.rlim_cur = rlp.rlim_max;
    setrlimit(RLIMIT_NOFILE, &rlp);
    getrlimit(RLIMIT_NOFILE, &rlp);
    if (rlp.rlim_cur < rlp.rlim_max) {
        
        rlp.rlim_cur = (rlp.rlim_max > 0 && rlp.rlim_max <= 8000) ? rlp.rlim_max : 8000;
        setrlimit(RLIMIT_NOFILE, &rlp);
    }
    
    getrlimit(RLIMIT_NOFILE, &rlp);
    softLimit = (rlp.rlim_cur > INT_MAX) ? INT_MAX : (int)rlp.rlim_cur;
#endif
    softLimit -= (mayaOpenFiles + 3);
    return softLimit;
}
GlobalReaderCache::CacheReaderProxy::CacheReaderProxy(
const MFileObject& file)
    : fFile(file)
{
    GlobalReaderCache::theCache().increaseFileRef(fFile);
}
GlobalReaderCache::CacheReaderProxy::~CacheReaderProxy()
{
    GlobalReaderCache::theCache().decreaseFileRef(fFile);
}
GlobalReaderCache::CacheReaderHolder::CacheReaderHolder(boost::shared_ptr<CacheReaderProxy> proxy)
    : fProxy(proxy)
{
    fReader = GlobalReaderCache::theCache().acquireOwnership(fProxy->file());
}
GlobalReaderCache::CacheReaderHolder::~CacheReaderHolder()
{
    GlobalReaderCache::theCache().releaseOwnership(fProxy->file());
}
boost::shared_ptr<CacheReader> GlobalReaderCache::CacheReaderHolder::getCacheReader()
{
    return fReader;
}
GlobalReaderCache::GlobalReaderCache()
    : fImpl(new Impl(maxNumOpenFiles())), fScheduler(new Scheduler())
{}
GlobalReaderCache::~GlobalReaderCache()
{}
boost::shared_ptr<GlobalReaderCache::CacheReaderProxy>
    GlobalReaderCache::getCacheReaderProxy(
const MFileObject& file)
{
    return boost::make_shared<CacheReaderProxy>(file);
}
bool GlobalReaderCache::scheduleRead(
    const CacheFileEntry*  entry, 
    CacheReaderProxy::Ptr& proxy
)
{
    return fScheduler->scheduleRead(entry, geometryPath, proxy);
}
bool GlobalReaderCache::pullHierarchy(
    const CacheFileEntry*  entry,
    SubNode::Ptr&          geometry,
    MaterialGraphMap::Ptr& materials
)
{
    return fScheduler->pullHierarchy(entry, geometry, validatedGeometryPath, materials);
}
bool GlobalReaderCache::pullShape(
    const CacheFileEntry*   entry,
    GPUCache::SubNode::Ptr& geometry
)
{
    return fScheduler->pullShape(entry, geometry);
}
void GlobalReaderCache::hintShapeReadOrder(const GPUCache::SubNode& subNode)
{
    return fScheduler->hintShapeReadOrder(subNode);
}
void GlobalReaderCache::cancelRead(const CacheFileEntry* entry)
{
    fScheduler->cancelRead(entry);
}
void GlobalReaderCache::waitForRead(const CacheFileEntry* entry)
{
    fScheduler->waitForRead(entry);
}
bool GlobalReaderCache::isInterrupted()
{
    return fScheduler->isInterrupted();
}
void GlobalReaderCache::pauseRead()
{
    fScheduler->pauseRead();
}
void GlobalReaderCache::resumeRead()
{
    fScheduler->resumeRead();
}
bool GlobalReaderCache::isPaused()
{
    return fScheduler->isPaused();
}
void GlobalReaderCache::pauseUntilNotified()
{
    fScheduler->pauseUntilNotified();
}
void GlobalReaderCache::increaseFileRef(
const MFileObject& file)
 
{
    fImpl->increaseFileRef(file);
}
void GlobalReaderCache::decreaseFileRef(
const MFileObject& file)
 
{
    fImpl->decreaseFileRef(file);
}
boost::shared_ptr<CacheReader> GlobalReaderCache::acquireOwnership(
const MFileObject& file)
{
    return fImpl->acquireOwnership(file);
}
void GlobalReaderCache::releaseOwnership(
const MFileObject& file)
 
{
    fImpl->releaseOwnership(file);
}
std::map<std::string,CacheReader::CreateFunction*> CacheReader::fsRegistry;
boost::shared_ptr<CacheReader> CacheReader::create(
const MString& impl,
{
    std::string key = impl.
asChar();
    std::map<std::string,CreateFunction*>::iterator iter = fsRegistry.find(key);
    if (iter != fsRegistry.end()) {
        return ((*iter).second)(file);
    }
    assert("not implemented");
    return boost::shared_ptr<CacheReader>();
}
void CacheReader::registerReader(
const MString& impl, CreateFunction* func)
 
{
    std::string key = impl.
asChar();
    fsRegistry[key] = func;
}