//
//   Copyright 2014 DreamWorks Animation LLC.
//
//   Licensed under the terms set forth in the LICENSE.txt file available at
//   https://opensubdiv.org/license.
//
#include "../far/topologyDescriptor.h"
#include "../far/topologyRefinerFactory.h"
#include "../far/topologyRefiner.h"

//  Unfortunately necessary for error codes that should be more accessible...
#include "../vtr/level.h"

#include <cstdio>
#ifdef _MSC_VER
    #define snprintf _snprintf
#endif


namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

namespace Far {

//
//  Definitions for TopologyDescriptor:
//
TopologyDescriptor::TopologyDescriptor() {
    memset(this, 0, sizeof(TopologyDescriptor));
}


//
//  Definitions/specializations for its RefinerFactory<TopologyDescriptor>:
//
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::resizeComponentTopology(
    TopologyRefiner & refiner, TopologyDescriptor const & desc) {

    setNumBaseVertices(refiner, desc.numVertices);
    setNumBaseFaces(refiner, desc.numFaces);

    for (int face=0; face<desc.numFaces; ++face) {

        setNumBaseFaceVertices(refiner, face, desc.numVertsPerFace[face]);
    }
    return true;
}

template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTopology(
    TopologyRefiner & refiner, TopologyDescriptor const & desc) {

    for (int face=0, idx=0; face<desc.numFaces; ++face) {

        IndexArray dstFaceVerts = getBaseFaceVertices(refiner, face);

        if (desc.isLeftHanded) {
            dstFaceVerts[0] = desc.vertIndicesPerFace[idx++];
            for (int vert=dstFaceVerts.size()-1; vert > 0; --vert) {

                dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
            }
        } else {
            for (int vert=0; vert<dstFaceVerts.size(); ++vert) {

                dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
            }
        }
    }
    return true;
}

template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTags(
    TopologyRefiner & refiner, TopologyDescriptor const & desc) {

    if ((desc.numCreases>0) && desc.creaseVertexIndexPairs && desc.creaseWeights) {

        int const * vertIndexPairs = desc.creaseVertexIndexPairs;
        for (int edge=0; edge<desc.numCreases; ++edge, vertIndexPairs+=2) {

            Index idx = findBaseEdge(refiner, vertIndexPairs[0], vertIndexPairs[1]);

            if (idx!=INDEX_INVALID) {
                setBaseEdgeSharpness(refiner, idx, desc.creaseWeights[edge]);
            } else {
                char msg[1024];
                snprintf(msg, 1024, "Edge %d specified to be sharp does not exist (%d, %d)",
                    edge, vertIndexPairs[0], vertIndexPairs[1]);
                reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_EDGE, msg, desc);
            }
        }
    }

    if ((desc.numCorners>0) && desc.cornerVertexIndices && desc.cornerWeights) {

        for (int vert=0; vert<desc.numCorners; ++vert) {

            int idx = desc.cornerVertexIndices[vert];

            if (idx >= 0 && idx < getNumBaseVertices(refiner)) {
                setBaseVertexSharpness(refiner, idx, desc.cornerWeights[vert]);
            } else {
                char msg[1024];
                snprintf(msg, 1024, "Vertex %d specified to be sharp does not exist", idx);
                reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_VERT, msg, desc);
            }
        }
    }
    if (desc.numHoles>0) {
        for (int i=0; i<desc.numHoles; ++i) {
            setBaseFaceHole(refiner, desc.holeIndices[i], true);
        }
    }
    return true;
}

template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignFaceVaryingTopology(
    TopologyRefiner & refiner, TopologyDescriptor const & desc) {

    if (desc.numFVarChannels>0) {

        for (int channel=0; channel<desc.numFVarChannels; ++channel) {

            int        numFVarValues = desc.fvarChannels[channel].numValues;
            int const* srcFVarValues = desc.fvarChannels[channel].valueIndices;

            createBaseFVarChannel(refiner, numFVarValues);

            for (int face = 0, srcNext = 0; face < desc.numFaces; ++face) {

                IndexArray dstFaceFVarValues = getBaseFaceFVarValues(refiner, face, channel);

                if (desc.isLeftHanded) {
                    dstFaceFVarValues[0] = srcFVarValues[srcNext++];
                    for (int vert = dstFaceFVarValues.size() - 1; vert > 0; --vert) {
                        
                        dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
                    }
                } else {
                    for (int vert = 0; vert < dstFaceFVarValues.size(); ++vert) {
                        
                        dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
                    }
                }
            }
        }
    }
    return true;
}

template <>
void
TopologyRefinerFactory<TopologyDescriptor>::reportInvalidTopology(
    TopologyError /* errCode */, char const * msg, TopologyDescriptor const& /* mesh */) {
    Warning(msg);
}

} // end namespace Far

} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv
