#ifndef _CacheReaderAlembic_h_
#define _CacheReaderAlembic_h_
#include <Alembic/AbcCoreAbstract/TimeSampling.h>
#include <Alembic/Abc/IArchive.h>
#include <Alembic/AbcGeom/INuPatch.h>
#include <Alembic/AbcGeom/IPolyMesh.h>
#include <Alembic/AbcGeom/ISubD.h>
#include <Alembic/AbcGeom/IXform.h>
#include <Alembic/AbcMaterial/IMaterial.h>
#include <Alembic/AbcMaterial/MaterialAssignment.h>
#include <Alembic/AbcCoreHDF5/ReadWrite.h>
#include "CacheReader.h"
#include <maya/MString.h>
#include <maya/MTime.h>
#include <maya/MFileObject.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MFnNurbsSurface.h>
#include <maya/MFnNurbsSurfaceData.h>
#include <boost/enable_shared_from_this.hpp>
#include <boost/unordered_map.hpp>
#include <boost/scoped_ptr.hpp>
#include <tbb/recursive_mutex.h>
#include <map>
namespace GPUCache {
class AlembicCacheReader;
namespace CacheReaderAlembicPrivate {
class AlembicCacheMeshReader;
typedef Alembic::Abc::index_t           index_t; 
typedef Alembic::Abc::chrono_t          chrono_t;
typedef Alembic::AbcGeom::IXformSchema  IXformSchema;
typedef Alembic::AbcGeom::XformSample   XformSample;
typedef Alembic::AbcGeom::XformOp       XformOp;
typedef Alembic::AbcCoreAbstract::TimeSamplingPtr TimeSamplingPtr;
template <class ElemType>
struct BaseTypeOfElem
{
    
    typedef ElemType value_type;
    static const size_t kDimensions = 1;
};
template <>
struct BaseTypeOfElem<int32_t>
{
    
    typedef uint32_t value_type;
    static const size_t kDimensions = 1;
};
    
template <class T>
struct BaseTypeOfElem<Imath::Vec2<T> > 
{
    typedef T value_type;
    static const size_t kDimensions = 2;
};
    
template <class T>
struct BaseTypeOfElem<Imath::Vec3<T> > 
{
    typedef T value_type;
    static const size_t kDimensions = 3;
};
    
template <class ArrayProperty>
class AlembicArray :
    public ReadableArray<
    typename BaseTypeOfElem<
        typename ArrayProperty::traits_type::value_type
        >::value_type
    >
{
public:
    
    typedef typename ArrayProperty::sample_ptr_type ArraySamplePtr;
    typedef typename ArrayProperty::traits_type traits_type;
    typedef typename BaseTypeOfElem<typename traits_type::value_type>::value_type T;
    typedef typename Array<T>::Digest Digest;
        
    static const size_t kDimensions =
        BaseTypeOfElem<typename traits_type::value_type>::kDimensions;
        
    
    
    static boost::shared_ptr<ReadableArray<T> > create(
        const ArraySamplePtr& arraySamplePtr, const Digest& digest );
    virtual ~AlembicArray();
    virtual const T* get() const;
private:
    
    
    
    GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_2;
 
    AlembicArray(
        const ArraySamplePtr& arraySamplePtr, const Digest& digest
    )
        : ReadableArray<T>(
            arraySamplePtr->size() * kDimensions, digest),
          fArraySamplePtr(arraySamplePtr)
    {
        assert(traits_type::dataType().getExtent() == kDimensions);
    }
 
    
    const ArraySamplePtr    fArraySamplePtr;
};
template <typename PROPERTY, typename KEY, typename VALUE, typename DERIVED>
class PropertyCache 
{
public:
    typedef PROPERTY    Property;
    typedef KEY         Key;
    typedef VALUE       Value;
    typedef DERIVED     Derived;
    PropertyCache()
        : fValidityInterval(TimeInterval::kInvalid)
    {}
    void reset()
    {
        fProperty = Property();
        fValidityInterval = TimeInterval(TimeInterval::kInvalid);
        fValue = Value();
    }
        
    bool valid() const
    {
        return fProperty.valid();
    }
        
    void init(const Property& property)
    {
        this->fProperty = property;
            
        const index_t numSamples       = this->fProperty.getNumSamples();
        const TimeSamplingPtr sampling = this->fProperty.getTimeSampling();
        if (this->fProperty.isConstant()) {
            
            fValidityInterval = TimeInterval(TimeInterval::kInvalid);
        }
        else {
            
            
            
            
            
            
            
            
            this->fUniqueSampleIndexes.push_back(0);
            this->fTimeBoundaries.push_back(-std::numeric_limits<chrono_t>::infinity());
            Key prevKey;
            static_cast<Derived*>(this)->getKey(prevKey, 0);
            for (index_t i=1; i<numSamples; ++i) {
                Key key;
                static_cast<Derived*>(this)->getKey(key, i);
                if (key != prevKey) {
                    this->fUniqueSampleIndexes.push_back(i);
                    
                    
                    
                    this->fTimeBoundaries.push_back(
                        0.5 * (sampling->getSampleTime(i-1) + sampling->getSampleTime(i))
                    );
                    prevKey = key;
                }
            }
            this->fTimeBoundaries.push_back(+std::numeric_limits<chrono_t>::infinity());
        }
    }
    bool setTime(chrono_t time)
    {
        if (fProperty.isConstant())
        {
            
            if (!fValidityInterval.valid()) {
                static_cast<Derived*>(this)->readValue(0);
                
                fValidityInterval = TimeInterval(TimeInterval::kInfinite);
            }
            return false;
        }
        if (fValidityInterval.contains(time)) {
            return false;
        }
        std::vector<chrono_t>::const_iterator bgn = fTimeBoundaries.begin();
        std::vector<chrono_t>::const_iterator end = fTimeBoundaries.end();
        std::vector<chrono_t>::const_iterator it = std::upper_bound(
            bgn, end, time);
        assert(it != bgn);
        
        index_t idx = fUniqueSampleIndexes[std::distance(bgn, it) - 1];
        
        static_cast<Derived*>(this)->readValue(idx);
            
        
        
        fValidityInterval = TimeInterval(*(it-1), *it);
        return true;
    }
    const Value& getValue() const
    {
        return fValue;
    }
        
    TimeInterval getValidityInterval() const
    {
        return fValidityInterval;
    }
        
protected:
    Property              fProperty;
    std::vector<index_t>  fUniqueSampleIndexes;
    std::vector<chrono_t> fTimeBoundaries;
        
    TimeInterval          fValidityInterval;
    Value                 fValue;
};
template <typename PROPERTY>
class ScalarPropertyCache 
    : public PropertyCache<
        PROPERTY,
        typename PROPERTY::value_type,
        typename PROPERTY::value_type,
        ScalarPropertyCache<PROPERTY>
    >
{
public:
    typedef PropertyCache<
        PROPERTY,
        typename PROPERTY::value_type,
        typename PROPERTY::value_type,
        ScalarPropertyCache<PROPERTY>
    > BaseClass;
    typedef typename BaseClass::Key Key;
        
    void readValue(index_t idx)
    {
        
        getKey(this->fValue, idx);
    }
        
    void getKey(Key& key, index_t idx)
    {
        this->fProperty.get(key, idx);
    }
        
};
class XformPropertyCache 
    : public PropertyCache<
        IXformSchema,
        MMatrix,
        MMatrix,
        XformPropertyCache
    >
{
public:
    void readValue(index_t idx)
    {
        
        getKey(fValue, idx);
    }
        
    void getKey(Key& key, index_t idx)
    {
        XformSample sample;
        fProperty.get(sample, idx);
        key = toMatrix(sample);
    }
private:
    
    
    static MMatrix toMatrix(
const XformSample& sample)
 
    {
        Alembic::Abc::M44d matrix = sample.getMatrix();
    }
        
};
    
template <typename PROPERTY>
class ArrayPropertyCache 
    : public PropertyCache<
        PROPERTY,
        Alembic::AbcCoreAbstract::ArraySampleKey,
        boost::shared_ptr<
            ReadableArray<
                typename BaseTypeOfElem<
                    typename PROPERTY::traits_type::value_type
                    >::value_type> >,
        ArrayPropertyCache<PROPERTY>
    >
{   
public:
    typedef PropertyCache<
        PROPERTY,
        Alembic::AbcCoreAbstract::ArraySampleKey,
        boost::shared_ptr<
            ReadableArray<
                typename BaseTypeOfElem<
                    typename PROPERTY::traits_type::value_type
                    >::value_type> >,
        ArrayPropertyCache<PROPERTY>
    > BaseClass;
    typedef typename BaseClass::Property Property;
    typedef typename BaseClass::Key Key;
    typedef typename BaseClass::Value Value;
    typedef typename Property::sample_ptr_type ArraySamplePtr;
    typedef typename Property::traits_type traits_type;
    typedef typename BaseTypeOfElem<typename traits_type::value_type>::value_type BaseType;
    static const size_t kDimensions =
        BaseTypeOfElem<typename traits_type::value_type>::kDimensions;
    void readValue(index_t idx)
    {
        Key key;
        getKey(key, idx);
        
        
        
        
        assert(key.origPOD == key.readPOD);
        assert(key.origPOD == traits_type::dataType().getPod());
        assert(sizeof(BaseType) == PODNumBytes(traits_type::dataType().getPod()));
        assert(kDimensions == traits_type::dataType().getExtent());
        const size_t size = size_t(key.numBytes / sizeof(BaseType));
            
        
        
        
        
        
        
        
        this->fValue = Value();
        {
            tbb::mutex::scoped_lock lock(ArrayRegistry<BaseType>::mutex());
            this->fValue = ArrayRegistry<BaseType>::lookupReadable(key.digest, size);
        
            if (this->fValue) return;
        }            
        
        ArraySamplePtr sample;
        this->fProperty.get(sample, idx);
#ifndef NDEBUG
        Key key2 = sample->getKey();
        const size_t size2 = (sample->size() *
                             Property::traits_type::dataType().getExtent());
        assert(key == key2);
        assert(size == size2);
#endif
        
        
        this->fValue = AlembicArray<Property>::create(sample, key.digest);
    }
        
    void getKey(Key& key, index_t idx)
    {
#ifndef NDEBUG
        bool result =
#endif                
            this->fProperty.getKey(key, idx);
        
        assert(result);
    }
};
template <typename PROPERTY>
class ArrayPropertyCacheWithConverter
    : public PropertyCache<
        PROPERTY,
        Alembic::AbcCoreAbstract::ArraySampleKey,
        boost::shared_ptr<
            ReadableArray<
                typename BaseTypeOfElem<
                    typename PROPERTY::traits_type::value_type
                    >::value_type> >,
        ArrayPropertyCacheWithConverter<PROPERTY>
    >
{
public:
    typedef PropertyCache<
        PROPERTY,
        Alembic::AbcCoreAbstract::ArraySampleKey,
        boost::shared_ptr<
            ReadableArray<
                typename BaseTypeOfElem<
                    typename PROPERTY::traits_type::value_type
                    >::value_type> >,
        ArrayPropertyCacheWithConverter<PROPERTY>
    > BaseClass;
    typedef typename BaseClass::Property Property;
    typedef typename BaseClass::Key Key;
    typedef typename BaseClass::Value Value;
    typedef typename Property::sample_ptr_type ArraySamplePtr;
    typedef typename Property::traits_type traits_type;
    typedef typename BaseTypeOfElem<typename traits_type::value_type>::value_type BaseType;
    typedef Alembic::Util::Digest Digest;
    typedef boost::shared_ptr<ReadableArray<BaseType> >
    (*Converter)(const typename PROPERTY::sample_ptr_type& sample);
    static const size_t kDimensions =
        BaseTypeOfElem<typename traits_type::value_type>::kDimensions;
    ArrayPropertyCacheWithConverter(Converter converter) 
        : fConverter(converter)
    {}
        
    void readValue(index_t idx)
    {
        Key key;
        getKey(key, idx);
        
        
        
        
        assert(key.origPOD == key.readPOD);
        assert(key.origPOD == traits_type::dataType().getPod());
        assert(sizeof(BaseType) == PODNumBytes(traits_type::dataType().getPod()));
        assert(kDimensions == traits_type::dataType().getExtent());
        const size_t size = size_t(key.numBytes / sizeof(BaseType));
            
        
        
        
        
        
        
        
        this->fValue = Value();
        typename ConvertionMap::const_iterator it = fsConvertionMap.find(key.digest);
        if (it != fsConvertionMap.end()) {
            tbb::mutex::scoped_lock lock(ArrayRegistry<BaseType>::mutex());
            this->fValue = ArrayRegistry<BaseType>::lookupReadable(it->second, size);
        
            if (this->fValue) return;
        }            
        
        ArraySamplePtr sample;
        this->fProperty.get(sample, idx);
#ifndef NDEBUG        
        Key key2 = sample->getKey();
        const size_t size2 = (sample->size() *
                             Property::traits_type::dataType().getExtent());
        assert(key == key2);
        assert(size == size2);
#endif
        
        
        this->fValue = fConverter(sample);
        fsConvertionMap[key.digest] = this->fValue->digest();
    }
        
    void getKey(Key& key, index_t idx)
    {
#ifndef NDEBUG
        bool result =
#endif                
            this->fProperty.getKey(key, idx);
        
        assert(result);
    }
private:
    struct DigestHash : std::unary_function<Digest, std::size_t>
    {
        std::size_t operator()(Digest const& v) const
        {
            std::size_t seed = 0;
            boost::hash_combine(seed, v.words[0]);
            boost::hash_combine(seed, v.words[1]);
            return seed;
        }
    };
    typedef boost::unordered_map<Digest, Digest, DigestHash> ConvertionMap;
    static ConvertionMap fsConvertionMap;
    const Converter fConverter;
};
class DataProvider
{
public:
    virtual ~DataProvider();
    
    virtual bool valid() const;
    
    
    
    void fillBBoxAndVisSample(chrono_t time);
    
    TimeInterval getBBoxAndVisValidityInterval() const { return fBBoxAndVisValidityInterval; }
    
    
    
    
    void fillTopoAndAttrSample(chrono_t time);
    
    
    TimeInterval getValidityInterval() const { return fValidityInterval; }
    
    bool isVisible() const;
    
    boost::shared_ptr<const ShapeSample>
    getBBoxPlaceHolderSample(double seconds);
    
    virtual boost::shared_ptr<const ShapeSample>
    getSample(double seconds) = 0;
    
    
    {
        const Imath::Box<Imath::V3d> boundingBox =
            fBoundingBoxCache.getValue();
        if (boundingBox.isEmpty()) {
        }
            MPoint(boundingBox.min.x, boundingBox.min.y, boundingBox.min.z),
 
            MPoint(boundingBox.max.x, boundingBox.max.y, boundingBox.max.z));
 
    }
    TimeInterval getBoundingBoxValidityInterval() const
    { return fBoundingBoxCache.getValidityInterval(); }
    TimeInterval getAnimTimeRange() const
    { return fAnimTimeRange; }
    
protected:
    
    DataProvider(const DataProvider&);
    const DataProvider& operator= (const DataProvider&);
    template<class INFO>
    DataProvider(Alembic::AbcGeom::IGeomBaseSchema<INFO>& abcGeom,
                 Alembic::Abc::TimeSamplingPtr            timeSampling,
                 size_t numSamples,
                 bool   needUVs);
    
    TimeInterval updateBBoxAndVisCache(chrono_t time);
    
    virtual TimeInterval updateCache(chrono_t time);
    
    const bool fNeedUVs;
    
    TimeInterval fAnimTimeRange;
    
    TimeInterval fBBoxAndVisValidityInterval;
    
    TimeInterval fValidityInterval;
    
    ScalarPropertyCache<Alembic::Abc::ICharProperty>  fVisibilityCache;
    
    ScalarPropertyCache<Alembic::Abc::IBox3dProperty> fBoundingBoxCache;
    
    std::vector<ScalarPropertyCache<Alembic::Abc::ICharProperty> > fParentVisibilityCache;
};
class PolyDataProvider : public DataProvider
{
public:
    virtual ~PolyDataProvider();
    
    virtual bool valid() const;
protected:
    
    PolyDataProvider(const PolyDataProvider&);
    const PolyDataProvider& operator= (const PolyDataProvider&);
    template<class SCHEMA>
    PolyDataProvider(SCHEMA&                         abcMesh,
                     bool                            needUVs);
    
    virtual TimeInterval updateCache(chrono_t time);
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fFaceCountsCache;
    ArrayPropertyCache<Alembic::Abc::IP3fArrayProperty>   fPositionsCache;
};
class RawDataProvider : public PolyDataProvider
{
public:
    RawDataProvider(Alembic::AbcGeom::IPolyMeshSchema& abcMesh,
                    bool needUVs);
    virtual ~RawDataProvider();
    
    virtual bool valid() const;
    
    virtual boost::shared_ptr<const ShapeSample>
    getSample(double seconds);
protected:
    
    RawDataProvider(const RawDataProvider&);
    const RawDataProvider& operator= (const RawDataProvider&);
    
    static boost::shared_ptr<ReadableArray<IndexBuffer::index_t> >
    correctPolygonWinding(const Alembic::Abc::Int32ArraySamplePtr& indices);
    
    virtual TimeInterval updateCache(chrono_t time);
    ArrayPropertyCacheWithConverter<Alembic::Abc::IInt32ArrayProperty> fFaceIndicesCache;
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fWireIndicesCache;  
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fGroupSizesCache;   
    ScalarPropertyCache<Alembic::Abc::IC4fProperty>       fDiffuseColorCache; 
    ArrayPropertyCache<Alembic::Abc::IN3fArrayProperty>   fNormalsCache;      
    ArrayPropertyCache<Alembic::Abc::IV2fArrayProperty>   fUVsCache;          
};
class Triangulator : public PolyDataProvider
{
public:
    Triangulator(Alembic::AbcGeom::IPolyMeshSchema& abcMesh,
                 bool needUVs);
    virtual ~Triangulator();
    
    virtual bool valid() const;
    
    virtual boost::shared_ptr<const ShapeSample>
    getSample(double seconds);
protected:
    
    Triangulator(const Triangulator&);
    const Triangulator& operator= (const Triangulator&);
    
    virtual TimeInterval updateCache(chrono_t time);
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fFaceIndicesCache;
    
    Alembic::AbcGeom::GeometryScope                        fNormalsScope;
    ArrayPropertyCache<Alembic::Abc::IN3fArrayProperty>    fNormalsCache;
    ArrayPropertyCache<Alembic::Abc::IUInt32ArrayProperty> fNormalIndicesCache;
    
    Alembic::AbcGeom::GeometryScope                        fUVsScope;
    ArrayPropertyCache<Alembic::Abc::IV2fArrayProperty>    fUVsCache;
    ArrayPropertyCache<Alembic::Abc::IUInt32ArrayProperty> fUVIndicesCache;
        
private:
    template<size_t SIZE>
    boost::shared_ptr<ReadableArray<float> > convertMultiIndexedStream(
        boost::shared_ptr<ReadableArray<float> > attribArray,
        boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > indexArray);
    void check();
    void computeNormals();
    void convertMultiIndexedStreams();
    void remapVertAttribs();
    void computeWireIndices();
    void triangulate();
    
    Alembic::AbcGeom::GeometryScope                 fCheckedNormalsScope;
    boost::shared_ptr<ReadableArray<float> >                fCheckedNormals;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fCheckedNormalIndices;
    Alembic::AbcGeom::GeometryScope                 fCheckedUVsScope;
    boost::shared_ptr<ReadableArray<float> >                fCheckedUVs;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fCheckedUVIndices;
    
    Alembic::AbcGeom::GeometryScope                 fComputedNormalsScope;
    boost::shared_ptr<ReadableArray<float> >                fComputedNormals;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fComputedNormalIndices;
    
    size_t                                          fNumVertices;
    boost::shared_array<unsigned int>               fVertAttribsIndices;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fMappedFaceIndices;
    
    boost::shared_ptr<ReadableArray<float> >                fMappedPositions;
    boost::shared_ptr<ReadableArray<float> >                fMappedNormals;
    boost::shared_ptr<ReadableArray<float> >                fMappedUVs;
        
    
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fWireIndices;
    
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fTriangleIndices;
};
class NurbsTessellator : public DataProvider
{
public:
    NurbsTessellator(Alembic::AbcGeom::INuPatchSchema& abcNurbs,
                     bool needUVs);
        
    virtual ~NurbsTessellator();
    
    virtual bool valid() const;
    
    virtual boost::shared_ptr<const ShapeSample>
    getSample(double seconds);
protected:
    
    NurbsTessellator(const NurbsTessellator&);
    const NurbsTessellator& operator= (const NurbsTessellator&);
    
    virtual TimeInterval updateCache(chrono_t time);
    
    ArrayPropertyCache<Alembic::Abc::IP3fArrayProperty>       fPositionsCache;
    ScalarPropertyCache<Alembic::Abc::IInt32Property>         fNumUCache;
    ScalarPropertyCache<Alembic::Abc::IInt32Property>         fNumVCache;
    ScalarPropertyCache<Alembic::Abc::IInt32Property>         fUOrderCache;
    ScalarPropertyCache<Alembic::Abc::IInt32Property>         fVOrderCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fUKnotCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fVKnotCache;
    
    
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fPositionWeightsCache;
    
    ScalarPropertyCache<Alembic::Abc::IInt32Property>         fTrimNumLoopsCache;
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty>     fTrimNumCurvesCache;
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty>     fTrimNumVerticesCache;
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty>     fTrimOrderCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fTrimKnotCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fTrimUCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fTrimVCache;
    ArrayPropertyCache<Alembic::AbcGeom::IFloatArrayProperty> fTrimWCache;
private:
    void check();
    void setNurbs(bool rebuild, bool positionsChanged);
    void tessellate();
    void convertToPoly();
    
    bool fSurfaceValid;
    
    
    
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fTriangleIndices;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fWireIndices;
    boost::shared_ptr<ReadableArray<float> >                fPositions;
    boost::shared_ptr<ReadableArray<float> >                fNormals;
    boost::shared_ptr<ReadableArray<float> >                fUVs;
};
class SubDSmoother : public PolyDataProvider
{
public:
    SubDSmoother(Alembic::AbcGeom::ISubDSchema&     abcSubd,
                 const bool                         needUVs);
    virtual ~SubDSmoother();
    
    virtual bool valid() const;
    
    virtual boost::shared_ptr<const ShapeSample>
    getSample(double seconds);
protected:
    
    SubDSmoother(const SubDSmoother&);
    const SubDSmoother& operator= (const SubDSmoother&);
    
    virtual TimeInterval updateCache(chrono_t time);
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fFaceIndicesCache;
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fCreaseIndicesCache;
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fCreaseLengthsCache;
    ArrayPropertyCache<Alembic::Abc::IFloatArrayProperty> fCreaseSharpnessesCache;
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fCornerIndicesCache;
    ArrayPropertyCache<Alembic::Abc::IFloatArrayProperty> fCornerSharpnessesCache;
    
    ArrayPropertyCache<Alembic::Abc::IInt32ArrayProperty> fHolesCache;
    
    Alembic::AbcGeom::GeometryScope                        fUVsScope;
    ArrayPropertyCache<Alembic::Abc::IV2fArrayProperty>    fUVsCache;
    ArrayPropertyCache<Alembic::Abc::IUInt32ArrayProperty> fUVIndicesCache;
private:
    void check();
    void rebuildSubD();
    void setPositions();
    void setCreaseEdges();
    void setCreaseVertices();
    void setInvisibleFaces();
    void setUVs();
    void convertToPoly();
    
    Alembic::AbcGeom::GeometryScope                 fCheckedUVsScope;
    boost::shared_ptr<ReadableArray<float> >                fCheckedUVs;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fCheckedUVIndices;
    
    
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fTriangleIndices;
    boost::shared_ptr<ReadableArray<IndexBuffer::index_t> > fWireIndices;
    boost::shared_ptr<ReadableArray<float> >                fPositions;
    boost::shared_ptr<ReadableArray<float> >                fNormals;
    boost::shared_ptr<ReadableArray<float> >                fUVs;
};
    
class AlembicCacheObjectReader
    : boost::noncopyable, 
      public boost::enable_shared_from_this<AlembicCacheObjectReader>
{
public:
    typedef  boost::shared_ptr<AlembicCacheObjectReader> Ptr;
    
    
    static Ptr create(Alembic::Abc::IObject& abcObj, bool needUVs);
    
    virtual ~AlembicCacheObjectReader() = 0;
    virtual bool valid() const = 0;
    
    
    
    
    virtual TimeInterval sampleHierarchy(double seconds,
        const MMatrix& rootMatrix, TimeInterval rootMatrixInterval) = 0;
 
    
    
    
    virtual TimeInterval sampleShape(double seconds) = 0;
    
    virtual SubNode::MPtr get() const = 0;
    
    
    
    virtual TimeInterval getBoundingBoxValidityInterval() const = 0;  
    
    virtual TimeInterval getAnimTimeRange() const = 0;
    
    virtual void saveAndReset(AlembicCacheReader& cacheReader) = 0;
};
class AlembicCacheTopReader : public AlembicCacheObjectReader
{
public:
    AlembicCacheTopReader(Alembic::Abc::IObject object, bool needUVs);
    ~AlembicCacheTopReader();
    virtual bool valid() const;
    virtual TimeInterval sampleHierarchy(double seconds, 
        const MMatrix& rootMatrix, TimeInterval rootMatrixInterval);
 
    virtual TimeInterval sampleShape(double seconds);
    virtual SubNode::MPtr get() const;
    virtual TimeInterval getBoundingBoxValidityInterval() const;
    virtual TimeInterval getAnimTimeRange() const;
    virtual void saveAndReset(AlembicCacheReader& cacheReader);
private:
    
    TimeInterval fBoundingBoxValidityInterval;
    
    XformData::MPtr fXformData;
    
    std::vector<AlembicCacheObjectReader::Ptr> fChildren;
};
class AlembicCacheXformReader : public AlembicCacheObjectReader
{
public:
    AlembicCacheXformReader(Alembic::Abc::IObject object, bool needUVs);
    ~AlembicCacheXformReader();
    virtual bool valid() const;
    virtual TimeInterval sampleHierarchy(double seconds, 
        const MMatrix& rootMatrix, TimeInterval rootMatrixInterval);
 
    virtual TimeInterval sampleShape(double seconds);
    virtual SubNode::MPtr get() const;
    virtual TimeInterval getBoundingBoxValidityInterval() const;
    virtual TimeInterval getAnimTimeRange() const;
    virtual void saveAndReset(AlembicCacheReader& cacheReader);
private:
    void fillTopoAndAttrSample(chrono_t time);
    bool isVisible() const;
    
    
    const std::string                 fName;
    
    TimeInterval fValidityInterval;
    
    XformPropertyCache fXformCache;
    
    ScalarPropertyCache<Alembic::Abc::ICharProperty>  fVisibilityCache;
    
    TimeInterval fBoundingBoxValidityInterval;
    
    XformData::MPtr fXformData;
    
    
    std::vector<AlembicCacheObjectReader::Ptr> fChildren;
};
class AlembicCacheMeshReader : public AlembicCacheObjectReader
{
public:
    AlembicCacheMeshReader(Alembic::Abc::IObject object, bool needUVs);
    ~AlembicCacheMeshReader();
    virtual bool valid() const;
    virtual TimeInterval sampleHierarchy(double seconds, 
        const MMatrix& rootMatrix, TimeInterval rootMatrixInterval);
 
    virtual TimeInterval sampleShape(double seconds);
    virtual SubNode::MPtr get() const;
protected:
    
    virtual TimeInterval getBoundingBoxValidityInterval() const;
    virtual TimeInterval getAnimTimeRange() const;
    virtual void saveAndReset(AlembicCacheReader& cacheReader);
private:
    
    const std::string                 fName;
    const std::string                 fFullName;
    boost::scoped_ptr<DataProvider>   fDataProvider;
    
    TimeInterval fBoundingBoxValidityInterval;
    
    ShapeData::MPtr fShapeData;
    size_t          fNumTransparentSample;
};
class AlembicCacheMaterialReader : boost::noncopyable
{
public:
    AlembicCacheMaterialReader(Alembic::Abc::IObject abcObj);
    ~AlembicCacheMaterialReader();
    TimeInterval sampleMaterial(double seconds);
    MaterialGraph::MPtr get() const;
private:
    
    const std::string fName;
    
    template<typename ABC_PROP> 
    class ScalarMaterialProp
    {
    public:
        ScalarMaterialProp(Alembic::Abc::ICompoundProperty& parent,
                           const std::string& name,
                           MaterialNode::MPtr& node)
            : fName(name)
        {
            
            ABC_PROP abcProp(parent, name);
            assert(abcProp.valid());
            
            fCache.reset(new ScalarPropertyCache<ABC_PROP>());
            fCache->init(abcProp);
            
            fProp = node->findProperty(name.c_str());
            assert(!fProp || fProp->type() == propertyType<ABC_PROP>());
            if (fProp && fProp->type() != propertyType<ABC_PROP>()) {
                
                fCache.reset();
                fProp.reset();
                return;
            }
            
            if (!fProp) {
                fProp = node->createProperty(name.c_str(), propertyType<ABC_PROP>());
                assert(fProp->type() == propertyType<ABC_PROP>());
            }
        }
        TimeInterval sample(double seconds)
        {
            TimeInterval validityInterval(TimeInterval::kInfinite);
            if (fCache && fCache->valid()) {
                fCache->setTime(seconds);
                validityInterval &= fCache->getValidityInterval();
                if (seconds == validityInterval.startTime()) {
                    setMaterialProperty<ABC_PROP>(fCache, fProp, seconds);
                }
            }
            return validityInterval;
        }
    private:
        std::string                                       fName;
        boost::shared_ptr<ScalarPropertyCache<ABC_PROP> > fCache;
        MaterialProperty::MPtr                            fProp;
    };
    template<typename T> static MaterialProperty::Type propertyType()
    { assert(0); return MaterialProperty::kFloat; }
    template<typename T> static void setMaterialProperty(
        boost::shared_ptr<ScalarPropertyCache<T> >& cache,
        MaterialProperty::MPtr prop,
        double seconds)
    {}
    
    std::vector<ScalarMaterialProp<Alembic::Abc::IBoolProperty> >    fBoolCaches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IInt32Property> >   fInt32Caches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IFloatProperty> >   fFloatCaches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IV2fProperty> >     fFloat2Caches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IV3fProperty> >     fFloat3Caches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IC3fProperty> >     fRGBCaches;
    std::vector<ScalarMaterialProp<Alembic::Abc::IWstringProperty> > fStringCaches;
    
    TimeInterval fValidityInterval;
    
    MaterialGraph::MPtr fMaterialGraph;
};
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IBoolProperty>()
{ return MaterialProperty::kBool; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IInt32Property>()
{ return MaterialProperty::kInt32; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IFloatProperty>()
{ return MaterialProperty::kFloat; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IV2fProperty>()
{ return MaterialProperty::kFloat2; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IV3fProperty>()
{ return MaterialProperty::kFloat3; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IC3fProperty>()
{ return MaterialProperty::kRGB; }
template<>
inline MaterialProperty::Type AlembicCacheMaterialReader::propertyType<Alembic::Abc::IWstringProperty>()
{ return MaterialProperty::kString; }
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IBoolProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IBoolProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    prop->setBool(seconds, cache->getValue());
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IInt32Property>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IInt32Property> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    prop->setInt32(seconds, cache->getValue());
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IFloatProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IFloatProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    prop->setFloat(seconds, cache->getValue());
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IV2fProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IV2fProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    Alembic::Abc::V2f value = cache->getValue();
    prop->setFloat2(seconds, value.x, value.y);
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IV3fProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IV3fProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    Alembic::Abc::V3f value = cache->getValue();
    prop->setFloat3(seconds, value.x, value.y, value.z);
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IC3fProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IC3fProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    Alembic::Abc::C3f value = cache->getValue();
    prop->setColor(seconds, 
MColor(value.x, value.y, value.z));
}
template<>
inline void AlembicCacheMaterialReader::setMaterialProperty<Alembic::Abc::IWstringProperty>(
    boost::shared_ptr<ScalarPropertyCache<Alembic::Abc::IWstringProperty> >& cache,
    MaterialProperty::MPtr prop,
    double seconds)
{
    std::wstring value = cache->getValue();
    prop->setString(seconds, value.c_str());
}
} 
class AlembicCacheReader : public CacheReader
{
public:
    static boost::shared_ptr<CacheReader> create(
const MFileObject& file);
 
    virtual ~AlembicCacheReader();
    virtual bool valid() const;
    virtual bool validateGeomPath(
    virtual SubNode::Ptr readScene(
        const MString& geomPath, 
bool needUVs);
 
    virtual SubNode::Ptr readHierarchy(
        const MString& geomPath, 
bool needUVs);
 
    virtual SubNode::Ptr readShape(
        const MString& geomPath, 
bool needUVs);
 
    virtual MaterialGraphMap::Ptr readMaterials();
    virtual bool readAnimTimeRange(TimeInterval& range);
    void saveReader(const std::string& fullName, 
                    CacheReaderAlembicPrivate::AlembicCacheObjectReader::Ptr& reader);
private:
    friend class AlembicCacheMeshReader;
    GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_1;
    mutable Alembic::Abc::IArchive fAbcArchive;
    typedef boost::unordered_map<std::string,CacheReaderAlembicPrivate::AlembicCacheObjectReader::Ptr> ObjectReaderMap;
    ObjectReaderMap fSavedReaders;
};
} 
#endif