#include "AbcWriteJob.h"
#include <Alembic/AbcCoreHDF5/All.h>
namespace
{
    void hasDuplicates(const util::ShapeSet & dagPath, unsigned int stripDepth)
    {
        std::map<std::string, MDagPath> roots;
        const util::ShapeSet::const_iterator end = dagPath.end();
        for (util::ShapeSet::const_iterator it = dagPath.begin();
            it != end; it++)
        {
            std::string fullName = it->fullPathName().asChar();
            if (!fullName.empty() && fullName[0] == '|')
            {
                fullName = fullName.substr(1);
            }
            if (stripDepth > 0)
            {
                fullName = util::stripNamespaces(name, stripDepth).
asChar();
            }
            std::map<std::string, MDagPath>::iterator strIt =
                roots.find(fullName);
            if (strIt != roots.end())
            {
                std::string theError = "Conflicting root node names specified: ";
                theError += it->fullPathName().asChar();
                theError += " ";
                theError += strIt->second.fullPathName().asChar();
                if (stripDepth > 0)
                {
                    theError += " with -stripNamespace specified.";
                }
                throw std::runtime_error(theError);
            }
            else
            {
                roots[fullName] = *it;
            }
        }
    }
    void addToString(std::string & str,
        const std::string & name, unsigned int value)
    {
        if (value > 0)
        {
            std::stringstream ss;
            ss << value;
            str += name + std::string(" ") + ss.str() + std::string(" ");
        }
    }
    void processCallback(std::string iCallback, bool isMelCallback,
    {
        if (iCallback.empty())
            return;
        size_t pos = iCallback.find("#FRAME#");
        if ( pos != std::string::npos )
        {
            std::stringstream sstrm;
            sstrm.precision(std::numeric_limits<double>::digits10);
            sstrm << iFrame;
            std::string str = sstrm.str();
            iCallback.replace(pos, 7, str);
        }
        pos = iCallback.find("#BOUNDS#");
        if ( pos != std::string::npos )
        {
            std::stringstream sstrm;
            sstrm.precision(std::numeric_limits<float>::digits10);
            sstrm << 
" " << iBbox.
min().
x << 
" " << iBbox.
min().
y << 
" " <<
                iBbox.
min().
z << 
" " << iBbox.
max().
x << 
" " <<
            std::string str = sstrm.str();
            iCallback.replace(pos, 8, str);
        }
        pos = iCallback.find("#BOUNDSARRAY#");
        if ( pos != std::string::npos )
        {
            std::stringstream sstrm;
            sstrm.precision(std::numeric_limits<float>::digits10);
            if (isMelCallback)
            {
                sstrm << " {";
            }
            else
            {
                sstrm << " [";
            }
            sstrm << iBbox.
min().
x << 
"," << iBbox.
min().
y << 
"," <<
                iBbox.
min().
z << 
"," << iBbox.
max().
x << 
"," <<
            if (isMelCallback)
            {
                sstrm << "} ";
            }
            else
            {
                sstrm << "] ";
            }
            std::string str = sstrm.str();
            iCallback.replace(pos, 13, str);
        }
        if (isMelCallback)
        else
    }
}
AbcWriteJob::AbcWriteJob(const char * iFileName,
    std::set<double> & iTransFrames,
    Alembic::AbcCoreAbstract::TimeSamplingPtr iTransTime,
    const JobArgs & iArgs)
{
    mFileName = iFileName;
    mBoxIndex = 0;
    mArgs = iArgs;
    mTransSamples = 1;
    if (mArgs.useSelectionList)
    {
        bool emptyDagPaths = mArgs.dagPaths.empty();
        
        mSList = activeList;
        unsigned int selectionSize = activeList.
length();
 
        for (unsigned int index = 0; index < selectionSize; index ++)
        {
            {
                unsigned int length = dagPath.
length();
 
                while (--length)
                {
                }
                if (emptyDagPaths)
                {
                    mArgs.dagPaths.insert(dagPath);
                }
            }
        }
    }
    mTransFrames = iTransFrames;
    
    mTransTime = iTransTime;
    mTransTimeIndex = 0;
    
    assert(!mTransFrames.empty());
    mFirstFrame = *(mTransFrames.begin());
    std::set<double>::iterator last = mTransFrames.end();
    last--;
    mLastFrame = *last;
}
void AbcWriteJob::setup(double iFrame, MayaTransformWriterPtr iParent, util::GetMembersMap& gmMap)
{
    
    
    if (mArgs.useSelectionList && !mSList.hasItem(mCurDag))
        return;
    bool bSolvedState = fnDepNode.typeName() == "bulletRigidCollection";
    
    if (util::isIntermediate(ob))
    {
        if (!bSolvedState)
        {
            return;
        }
    }
    
    if (mArgs.excludeInvisible && !util::isRenderable(ob))
    {
        return;
    }
    if ( bSolvedState )
    {
        
        {
            MString msg = 
"Initialize transform collection node ";
 
            msg += mCurDag.fullPathName();
            msg += " failed, skipping.";
            return;
        }
        MayaTransformCollectionWriterPtr transCol;
        
        Alembic::Abc::OObject obj = mRoot.getTop();
        transCol = MayaTransformCollectionWriterPtr(new MayaTransformCollectionWriter(
            obj, mCurDag, mTransTimeIndex, mArgs));
        mTransColList.push_back(transCol);
        mStats.mTransColNum++;
        AttributesWriterPtr attrs = transCol->getAttrs();
        if (attrs)
        {
            if (mTransTimeIndex != 0 && attrs->isAnimated())
                mTransColAttrList.push_back(attrs);
        }
    }
    {
        {
            MString msg = 
"Initialize transform node ";
 
            msg += mCurDag.fullPathName();
            msg += " failed, skipping.";
            return;
        }
        MayaTransformWriterPtr trans;
        
        if (iParent == NULL)
        {
            Alembic::Abc::OObject obj = mRoot.getTop();
            trans = MayaTransformWriterPtr(new MayaTransformWriter(
                obj, mCurDag, mTransTimeIndex, mArgs));
        }
        else
        {
            trans = MayaTransformWriterPtr(new MayaTransformWriter(
                *iParent, mCurDag, mTransTimeIndex, mArgs));
        }
        if (trans->isAnimated() && mTransTimeIndex != 0)
        {
            mTransList.push_back(trans);
            mStats.mTransAnimNum++;
        }
        else
            mStats.mTransStaticNum++;
        AttributesWriterPtr attrs = trans->getAttrs();
        if (mTransTimeIndex != 0 && attrs->isAnimated())
            mTransAttrList.push_back(attrs);
        
        
        unsigned int numChild = mCurDag.childCount();
        for (unsigned int i = 0; i < numChild; ++i)
        {
            mCurDag.push(mCurDag.child(i));
            setup(iFrame, trans, gmMap);
            mCurDag.pop();
        }
    }
    else
    {
        MString warn = mCurDag.fullPathName() + 
" is an unsupported type of ";
 
    }
}
AbcWriteJob::~AbcWriteJob()
{
}
bool AbcWriteJob::eval(double iFrame)
{
    if (iFrame == mFirstFrame)
    {
        
        
        hasDuplicates(mArgs.dagPaths, mArgs.stripNamespace);
        std::string appWriter = "Maya ";
        appWriter += " AbcBullet v";
        appWriter += ABCBULLET_VERSION;
        std::string userInfo = "Exported from: ";
        
        if (userInfo.find('=') != std::string::npos ||
            userInfo.find(';') != std::string::npos)
        {
            userInfo = "";
        }
        mRoot = CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
            mFileName, appWriter, userInfo,
            Alembic::Abc::ErrorHandler::kThrowPolicy);
        mTransTimeIndex = mRoot.addTimeSampling(*mTransTime);
        mBoxProp =  Alembic::AbcGeom::CreateOArchiveBounds(mRoot,
            mTransTimeIndex);
        if (!mRoot.valid())
        {
            std::string theError = "Unable to create abc file";
            throw std::runtime_error(theError);
        }
        util::ShapeSet::const_iterator end = mArgs.dagPaths.end();
        util::GetMembersMap gmMap;
        for (util::ShapeSet::const_iterator it = mArgs.dagPaths.begin();
            it != end; ++it)
        {
            mCurDag = *it;
            setup(iFrame * util::spf(), MayaTransformWriterPtr(), gmMap);
        }
        perFrameCallback(iFrame);
    }
    else
    {
        std::set<double>::iterator checkFrame = mTransFrames.find(iFrame);
        bool foundTransFrame = false;
        if (checkFrame != mTransFrames.end())
        {
            assert(mRoot.valid());
            foundTransFrame = true;
            mTransSamples ++;
            
            {
                std::vector< MayaTransformWriterPtr >::iterator tcur =
                    mTransList.begin();
                std::vector< MayaTransformWriterPtr >::iterator tend =
                    mTransList.end();
                for (; tcur != tend; tcur++)
                {
                    (*tcur)->write();
                }
                std::vector< AttributesWriterPtr >::iterator tattrCur =
                    mTransAttrList.begin();
                std::vector< AttributesWriterPtr >::iterator tattrEnd =
                    mTransAttrList.end();
                for(; tattrCur != tattrEnd; tattrCur++)
                {
                    (*tattrCur)->write();
                }
            }
            
            {
                std::vector< MayaTransformCollectionWriterPtr >::iterator tcur =
                    mTransColList.begin();
                std::vector< MayaTransformCollectionWriterPtr >::iterator tend =
                    mTransColList.end();
                for (; tcur != tend; tcur++)
                {
                    (*tcur)->write(iFrame);
                }
                std::vector< AttributesWriterPtr >::iterator tattrCur =
                    mTransColAttrList.begin();
                std::vector< AttributesWriterPtr >::iterator tattrEnd =
                    mTransColAttrList.end();
                for(; tattrCur != tattrEnd; tattrCur++)
                {
                    (*tattrCur)->write();
                }
            }
        }
        if (foundTransFrame)
            perFrameCallback(iFrame);
    }
    if (iFrame == mLastFrame)
    {
        postCallback(iFrame);
        return true;
    }
    return false;
}
void AbcWriteJob::perFrameCallback(double iFrame)
{
    processCallback(mArgs.melPerFrameCallback, true, iFrame, bbox);
    processCallback(mArgs.pythonPerFrameCallback, false, iFrame, bbox);
}
void AbcWriteJob::postCallback(double iFrame)
{
    std::string statsStr = "";
    addToString(statsStr, "TransStaticNum", mStats.mTransStaticNum);
    addToString(statsStr, "TransAnimNum", mStats.mTransAnimNum);
    addToString(statsStr, "TransColNum", mStats.mTransColNum);
    if (statsStr.length() > 0)
    {
        Alembic::Abc::OStringProperty stats(mRoot.getTop().getProperties(),
            "statistics");
        stats.set(statsStr);
    }
    if (mTransTimeIndex != 0)
    {
        propName += static_cast<int>(mTransTimeIndex);
        propName += ".samples";
        Alembic::Abc::OUInt32Property samp(mRoot.getTop().getProperties(),
    }
    processCallback(mArgs.melPostCallback, true, iFrame, bbox);
    processCallback(mArgs.pythonPostCallback, false, iFrame, bbox);
}