#include "CacheWriter.h"
#include "gpuCacheUtil.h"
#include "gpuCacheStrings.h"
#include <maya/MFnDagNode.h>
#include <maya/MAnimControl.h>
#include <maya/MFnMeshData.h>
#include <maya/MIntArray.h>
#include <maya/MFloatArray.h>
#include <maya/MFloatPointArray.h>
#include <maya/MFloatVectorArray.h>
#include <maya/MFnMesh.h>
#include <maya/MUintArray.h>
#include <maya/MHWGeometry.h>
#include <maya/MGeometryExtractor.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MDagPathArray.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <maya/MFnTransform.h>
#include <string.h>         
#include <string>
#include <cassert>
using namespace GPUCache;
std::map<std::string,void*> CacheWriter::fsRegistry;
boost::shared_ptr<CacheWriter> CacheWriter::create(
const MString& impl,
{
    std::string key = impl.
asChar();
    std::map<std::string,void*>::iterator iter = fsRegistry.find(key);
    if (iter != fsRegistry.end()) {
        return ((CreateFunction*)(*iter).second)(file, compressLevel, dataFormat);
    }
    assert("not implemented");
    return boost::shared_ptr<CacheWriter>();
}
void CacheWriter::registerWriter(
const MString& impl, CreateFunction func)
 
{
    std::string key = impl.
asChar();
    fsRegistry[key] = (void*)func;
}
boost::shared_ptr<CacheXformSampler>
CacheXformSampler::create(
const MObject& xformObject)
{
    return boost::make_shared<CacheXformSampler>(xformObject);
}
CacheXformSampler::CacheXformSampler(
const MObject& xformObject)
    : fXform(xformObject),
      fIsFirstSample(true),
      
      
      fXformAnimated(true),
      fVisibilitySample(false),
      fVisibilityAnimated(true)
{}
CacheXformSampler::~CacheXformSampler()
{}
void CacheXformSampler::addSample()
{
    MMatrix      prevXformSample      = fXformSample;
 
    bool         prevVisibilitySample = fVisibilitySample;
    fXformSample       = fXform.transformationMatrix();
    fVisibilitySample  = ShapeVisibilityChecker(fXform.object()).isVisible();
    if (fIsFirstSample) {
        
        
        fIsFirstSample = false;
    }
    else {
        fXformAnimated = (prevXformSample != fXformSample);
        fVisibilityAnimated = (prevVisibilitySample != fVisibilitySample);
    }
}
boost::shared_ptr<const XformSample>
CacheXformSampler::getSample(double timeInSeconds)
{
    boost::shared_ptr<XformSample> sample =
        XformSample::create(
            timeInSeconds,fXformSample, 
MBoundingBox(), fVisibilitySample);
    return sample;
}
CacheMeshSampler::AttributeSet::AttributeSet(
    const bool visibility,
    const bool needUVs
)
{
    typedef IndexBuffer::index_t index_t;
    MayaMeshExtractor<index_t> extractor(meshObject);
    extractor.setWantUVs(needUVs);
    extractor.compute();
    boost::shared_ptr<Array<index_t> > wireIndices     = extractor.wireIndices();
    boost::shared_ptr<Array<index_t> > triangleIndices = extractor.triangleIndices();
    boost::shared_ptr<Array<float> > positions = extractor.positions();
    boost::shared_ptr<Array<float> > normals   = extractor.normals();
    boost::shared_ptr<Array<float> > uvs;
    if (needUVs) {
        uvs = extractor.uvs();
    }
    fNumWires     = wireIndices->size() / 2;
    fNumTriangles = triangleIndices->size() / 3;
    fNumVerts     = positions->size() / 3;
    fWireVertIndices = IndexBuffer::create(wireIndices);
    fTriangleVertIndices.push_back(IndexBuffer::create(triangleIndices));
    fPositions = VertexBuffer::createPositions(positions);
    fNormals   = VertexBuffer::createNormals(normals);
    if (needUVs) {
        fUVs       = VertexBuffer::createUVs(uvs);
    }
    {
        float minX = +std::numeric_limits<float>::max();
        float minY = +std::numeric_limits<float>::max();
        float minZ = +std::numeric_limits<float>::max();
        float maxX = -std::numeric_limits<float>::max();
        float maxY = -std::numeric_limits<float>::max();
        float maxZ = -std::numeric_limits<float>::max();
        VertexBuffer::ReadInterfacePtr readable = fPositions->readableInterface();
        const float* srcPositions = readable->get();
        for (size_t i=0; i<fNumVerts; ++i) {
            const float x = srcPositions[3*i + 0];
            const float y = srcPositions[3*i + 1];
            const float z = srcPositions[3*i + 2];
            minX = std::min(x, minX);
            minY = std::min(y, minY);
            minZ = std::min(z, minZ);
            maxX = std::max(x, maxX);
            maxY = std::max(y, maxY);
            maxZ = std::max(z, maxZ);
        }
    }
    
    fVisibility  = visibility;
}
CacheMeshSampler::AttributeSet::AttributeSet(
    const bool needUVs,
    const bool useBaseTessellation
)
    :fNumWires(0),
    fNumTriangles(0),
    fNumVerts(0),
    fVisibility(false)
{
    
    
    
    
    
    if (needUVs)
    
    
    
    MIndexBufferDescriptor edgeDesc(MIndexBufferDescriptor::kEdgeLine, noName, MGeometry::kLines, 2, edgeCompObj);
 
    
    MIndexBufferDescriptor triangleDesc(MIndexBufferDescriptor::kTriangle, noName, MGeometry::kTriangles, 3, compObj);
 
    typedef IndexBuffer::index_t index_t;
    
    
    if (useBaseTessellation) {
    }
    
        return;
    
    unsigned int numVertices   = extractor.vertexCount();
    
    unsigned int numWires = extractor.primitiveCount(edgeDesc);
    
    boost::shared_array<float> vertices(new float[numVertices*posDesc.stride()]);
    boost::shared_array<float> normals(new float[numVertices*normalDesc.stride()]);
    boost::shared_array<float> uvs(new float[numVertices*uvDesc.stride()]);
    unsigned int minBufferSize = extractor.minimumBufferSize(numWires, edgeDesc.primitive());
    boost::shared_array<index_t> wireframeIdx(new index_t[minBufferSize]);
    
    if (
MS::kFailure==extractor.populateIndexBuffer(wireframeIdx.get(), numWires, edgeDesc))
 
        return;
    
    if (
MS::kFailure==extractor.populateVertexBuffer(vertices.get(), numVertices, posDesc))
 
        return;
    if (
MS::kFailure==extractor.populateVertexBuffer(normals.get(), numVertices, normalDesc))
 
        return; 
    if (needUVs && 
MS::kFailure==extractor.populateVertexBuffer(uvs.get(), numVertices, uvDesc))
 
        return; 
    
    {
        std::vector<boost::shared_ptr<Array<index_t> > > trgIdxGrps;
        
        unsigned int numTriangles = extractor.primitiveCount(triangleDesc);
        minBufferSize = extractor.minimumBufferSize(numTriangles, triangleDesc.primitive());
        boost::shared_array<index_t> triangleIdx(new index_t[minBufferSize]);
        if (numTriangles != 0) 
        {
            if (
MS::kFailure==extractor.populateIndexBuffer(triangleIdx.get(), numTriangles, triangleDesc))
 
                return;
            fNumTriangles += (size_t)numTriangles;
        }
        trgIdxGrps.push_back(SharedArray<index_t>::create(
            triangleIdx, 3 * numTriangles));
        for(size_t i=0, offset=0; i<trgIdxGrps.size(); ++i) {
            fTriangleVertIndices.push_back(IndexBuffer::create(
                trgIdxGrps[0], offset, offset + trgIdxGrps[i]->size()));
            offset += trgIdxGrps[i]->size();
        }
    }
    fNumWires     = (size_t)numWires;
    fNumVerts     = (size_t)numVertices;
    fWireVertIndices = IndexBuffer::create(
        SharedArray<index_t>::create( wireframeIdx, 2 * fNumWires));   
    fPositions = VertexBuffer::createPositions(
        SharedArray<float>::create( vertices, 3 * fNumVerts));
    fNormals = VertexBuffer::createNormals(
        SharedArray<float>::create( normals, 3 * fNumVerts));
    if (needUVs) {
        fUVs = VertexBuffer::createUVs(
            SharedArray<float>::create(uvs, 2 * fNumVerts));
    }
    
    fVisibility = ShapeVisibilityChecker(mesh.
object()).isVisible();
}
bool CacheMeshSampler::AttributeSet::updateAnimatedChannels(
    bool& animated, const AttributeSet& newer
)
{
    const bool numWiresAnimated     = fNumWires     != newer.fNumWires;
    const bool numTrianglesAnimated = fNumTriangles != newer.fNumTriangles;
    const bool numVertsAnimated     = fNumVerts     != newer.fNumVerts;
    const bool wiresAnimated = fWireVertIndices != newer.fWireVertIndices;
    
    
    
    
    const bool trianglesAnimated =
        (!numWiresAnimated && !numTrianglesAnimated && !numVertsAnimated &&
         !wiresAnimated) ?
        false :
        (fTriangleVertIndices != newer.fTriangleVertIndices);
    
    const bool positionsAnimated = fPositions != newer.fPositions;
    const bool normalsAnimated   = fNormals   != newer.fNormals;
    const bool uvsAnimated       = fUVs       != newer.fUVs;
    const bool boundingBoxAnimated =
        (!fBoundingBox.min().isEquivalent(newer.fBoundingBox.min()) ||
         !fBoundingBox.max().isEquivalent(newer.fBoundingBox.max()));
    const bool visibilityAnimated = fVisibility != newer.fVisibility;
    fNumWires     = newer.fNumWires;
    fNumTriangles = newer.fNumTriangles;
    fNumVerts     = newer.fNumVerts;
    
    fWireVertIndices = wiresAnimated ? newer.fWireVertIndices : fWireVertIndices;
    fTriangleVertIndices = trianglesAnimated ? newer.fTriangleVertIndices : fTriangleVertIndices;
    fPositions = positionsAnimated ? newer.fPositions : fPositions;
    fNormals   = normalsAnimated   ? newer.fNormals   : fNormals;
    fUVs       = uvsAnimated       ? newer.fUVs       : fUVs;
    fBoundingBox = newer.fBoundingBox;
    fVisibility  = newer.fVisibility;
    
    animated =
        numWiresAnimated || numTrianglesAnimated || numVertsAnimated ||
        wiresAnimated || trianglesAnimated ||
        positionsAnimated || normalsAnimated || uvsAnimated ||
        boundingBoxAnimated || visibilityAnimated;
    return true;
}
boost::shared_ptr<CacheMeshSampler> CacheMeshSampler::create(
    const bool needUVs)
{
    return boost::make_shared<CacheMeshSampler>(needUVs);
}
CacheMeshSampler::CacheMeshSampler(const bool needUVs)
    : fNeedUVs(needUVs), fUseBaseTessellation(false), fIsAnimated(true)
{}
CacheMeshSampler::~CacheMeshSampler()
{}
bool CacheMeshSampler::addSample(
MObject meshObject, 
bool visibility)
 
{
    return fAttributeSet.updateAnimatedChannels(
        fIsAnimated, AttributeSet(meshObject, visibility, fNeedUVs));
}
bool CacheMeshSampler::addSampleFromMesh(
MFnMesh& mesh)
 
{
    return fAttributeSet.updateAnimatedChannels(
        fIsAnimated, AttributeSet(mesh, fNeedUVs, fUseBaseTessellation));
}
boost::shared_ptr<const ShapeSample>
CacheMeshSampler::getSample(
double timeInSeconds, 
const MColor& diffuseColor)
{
    if (!fAttributeSet.fVisibility) {
        
        boost::shared_ptr<ShapeSample> sample = 
        ShapeSample::createEmptySample(timeInSeconds);
        return sample;
    }
    boost::shared_ptr<ShapeSample> sample =
        ShapeSample::create(
            timeInSeconds,
            fAttributeSet.fNumWires,
            fAttributeSet.fNumVerts,
            fAttributeSet.fWireVertIndices,
            fAttributeSet.fTriangleVertIndices,
            fAttributeSet.fPositions,
            fAttributeSet.fBoundingBox,
            diffuseColor,
            fAttributeSet.fVisibility
        );
    sample->setNormals(fAttributeSet.fNormals);
    sample->setUVs(fAttributeSet.fUVs);
    return sample;
}