//-*****************************************************************************
//
// Copyright (c) 2009-2012,
//  Sony Pictures Imageworks, Inc. and
//  Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// *       Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// *       Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// *       Neither the name of Sony Pictures Imageworks, nor
// Industrial Light & Magic nor the names of their contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//-*****************************************************************************

#ifndef Alembic_AbcGeom_ONuPatch_h
#define Alembic_AbcGeom_ONuPatch_h

#include <Alembic/Util/Export.h>
#include <Alembic/AbcGeom/Foundation.h>
#include <Alembic/AbcGeom/Basis.h>
#include <Alembic/AbcGeom/SchemaInfoDeclarations.h>
#include <Alembic/AbcGeom/OGeomParam.h>
#include <Alembic/AbcGeom/OGeomBase.h>

namespace Alembic {
namespace AbcGeom {
namespace ALEMBIC_VERSION_NS {

//-*****************************************************************************
// for default "null" values for the int scalar properties (INT_MIN/4)
static ALEMBIC_EXPORT_CONST
int32_t ABC_GEOM_NUPATCH_NULL_INT_VALUE( -536870912 );

//-*****************************************************************************
class ALEMBIC_EXPORT ONuPatchSchema : public OGeomBaseSchema<NuPatchSchemaInfo>
{
public:
    //-*************************************************************************
    // NuPatch SCHEMA SAMPLE TYPE
    //-*************************************************************************
    class Sample
    {
    public:
        //! Creates a default sample with no data in it.
        //! ...
        Sample() { reset(); }

        Sample(
                const Abc::P3fArraySample &iPos,
                const int32_t &iNumU,
                const int32_t &iNumV,
                const int32_t &iUOrder,
                const int32_t &iVOrder,
                const Abc::FloatArraySample &iUKnot,
                const Abc::FloatArraySample &iVKnot,
                const ON3fGeomParam::Sample &iNormals = ON3fGeomParam::Sample(),
                const OV2fGeomParam::Sample &iUVs = OV2fGeomParam::Sample(),
                const Abc::FloatArraySample & iPosWeight = Abc::FloatArraySample()
              ): m_positions( iPos )
               , m_numU( iNumU )
               , m_numV( iNumV )
               , m_uOrder( iUOrder )
               , m_vOrder( iVOrder )
               , m_uKnot( iUKnot )
               , m_vKnot( iVKnot )
               , m_positionWeights( iPosWeight )
               , m_normals( iNormals )
               , m_uvs( iUVs )
               , m_trimNumLoops( ABC_GEOM_NUPATCH_NULL_INT_VALUE )
               , m_trimNumVertices( Abc::Int32ArraySample() )
               , m_trimOrder( Abc::Int32ArraySample() )
               , m_trimKnot( Abc::FloatArraySample() )
               , m_trimMin( Abc::FloatArraySample() )
               , m_trimMax( Abc::FloatArraySample() )
               , m_trimU( Abc::FloatArraySample() )
               , m_trimV( Abc::FloatArraySample() )
               , m_trimW( Abc::FloatArraySample() )
               , m_hasTrimCurve( false )
            {}

        // positions
        const Abc::P3fArraySample &getPositions() const { return m_positions; }
        void setPositions( const Abc::P3fArraySample &iSmp )
        { m_positions = iSmp; }

        // position weights, if it isn't set, it's 1 for every point
        const Abc::FloatArraySample &getPositionWeights() const
        { return m_positionWeights; }
        void setPositionWeights( const Abc::FloatArraySample &iSmp )
        { m_positionWeights = iSmp; }

        // nu
        int32_t getNu() const { return m_numU; }
        void setNu( const int32_t iNu )
        { m_numU = iNu; }

        // nv
        int32_t getNv() const { return m_numV; }
        void setNv( const int32_t iNv )
        { m_numV = iNv; }

        // uOrder
        int32_t getUOrder() const { return m_uOrder; }
        void setUOrder( const int32_t iUOrder )
        { m_uOrder = iUOrder; }

        // vOrder
        int32_t getVOrder() const { return m_vOrder; }
        void setVOrder( const int32_t iVOrder )
        { m_vOrder = iVOrder; }

        // uKnot
        const Abc::FloatArraySample &getUKnot() const { return m_uKnot; }
        void setUKnot( const Abc::FloatArraySample &iUKnot )
        { m_uKnot = iUKnot; }

        // vKnot
        const Abc::FloatArraySample &getVKnot() const { return m_vKnot; }
        void setVKnot( const Abc::FloatArraySample &iVKnot )
        { m_vKnot = iVKnot; }

        // uvs
        const OV2fGeomParam::Sample &getUVs() const { return m_uvs; }
        void setUVs( const OV2fGeomParam::Sample &iUVs )
        { m_uvs = iUVs; }

        // normals
        const ON3fGeomParam::Sample &getNormals() const { return m_normals; }
        void setNormals( const ON3fGeomParam::Sample &iNormals )
        { m_normals = iNormals; }

        // bounds
        const Abc::Box3d &getSelfBounds() const { return m_selfBounds; }
        void setSelfBounds( const Abc::Box3d &iBnds )
        { m_selfBounds = iBnds; }

        // velocities accessor
        const Abc::V3fArraySample &getVelocities() const { return m_velocities; }
        void setVelocities( const Abc::V3fArraySample &iVelocities )
        { m_velocities = iVelocities; }

        // trim curves
        void setTrimCurve( const int32_t i_trim_nLoops,
                           const Abc::Int32ArraySample &i_trim_nCurves,
                           const Abc::Int32ArraySample &i_trim_n,
                           const Abc::Int32ArraySample &i_trim_order,
                           const Abc::FloatArraySample &i_trim_knot,
                           const Abc::FloatArraySample &i_trim_min,
                           const Abc::FloatArraySample &i_trim_max,
                           const Abc::FloatArraySample &i_trim_u,
                           const Abc::FloatArraySample &i_trim_v,
                           const Abc::FloatArraySample &i_trim_w )
        {
            m_trimNumLoops = i_trim_nLoops;
            m_trimNumCurves = i_trim_nCurves;
            m_trimNumVertices = i_trim_n;
            m_trimOrder = i_trim_order;
            m_trimKnot = i_trim_knot;
            m_trimMin = i_trim_min;
            m_trimMax = i_trim_max;
            m_trimU = i_trim_u;
            m_trimV = i_trim_v;
            m_trimW = i_trim_w;

            m_hasTrimCurve = true;
        }

        int32_t getTrimNumLoops() const { return m_trimNumLoops; }
        const Abc::Int32ArraySample &getTrimNumCurves() const
        { return m_trimNumCurves; }
        const Abc::Int32ArraySample &getTrimNumVertices() const
        { return m_trimNumVertices; }
        const Abc::Int32ArraySample &getTrimOrder() const
        { return m_trimOrder; }
        const Abc::FloatArraySample &getTrimKnot() const { return m_trimKnot; }
        const Abc::FloatArraySample &getTrimMin() const { return m_trimMin; }
        const Abc::FloatArraySample &getTrimMax() const { return m_trimMax; }
        const Abc::FloatArraySample &getTrimU() const { return m_trimU; }
        const Abc::FloatArraySample &getTrimV() const { return m_trimV; }
        const Abc::FloatArraySample &getTrimW() const { return m_trimW; }

        bool hasTrimCurve() const
        {
            return m_hasTrimCurve;
        }

        void reset()
        {
            m_positions.reset();
            m_velocities.reset();
            m_numU = ABC_GEOM_NUPATCH_NULL_INT_VALUE;
            m_numV = ABC_GEOM_NUPATCH_NULL_INT_VALUE;
            m_uOrder = ABC_GEOM_NUPATCH_NULL_INT_VALUE;
            m_vOrder = ABC_GEOM_NUPATCH_NULL_INT_VALUE;
            m_uKnot.reset();
            m_vKnot.reset();
            m_positionWeights.reset();
            m_normals.reset();
            m_uvs.reset();
            m_selfBounds.makeEmpty();

            // reset trim curves
            m_trimNumLoops = ABC_GEOM_NUPATCH_NULL_INT_VALUE;
            m_trimNumCurves.reset();
            m_trimNumVertices.reset();
            m_trimOrder.reset();
            m_trimKnot.reset();
            m_trimMin.reset();
            m_trimMax.reset();
            m_trimU.reset();
            m_trimV.reset();
            m_trimW.reset();
            m_hasTrimCurve = false;
        }

        bool isPartialSample() const
        {
            if( !m_positions.getData() )
            {
                if( m_uvs.getVals() || m_normals.getVals() || m_velocities.getData() )
                {
                    return true;
                }
            }

            return false;
        }

        bool hasKnotSampleData() const
        {
            if( (m_numU != ABC_GEOM_NUPATCH_NULL_INT_VALUE) ||
                (m_numV != ABC_GEOM_NUPATCH_NULL_INT_VALUE) ||
                (m_uOrder != ABC_GEOM_NUPATCH_NULL_INT_VALUE) ||
                (m_vOrder != ABC_GEOM_NUPATCH_NULL_INT_VALUE) ||
                 m_uKnot || m_vKnot)
                 return true;
            else
                return false;
        }

    protected:

        // required properties
        Abc::P3fArraySample m_positions;
        Abc::V3fArraySample m_velocities;
        int32_t m_numU;
        int32_t m_numV;
        int32_t m_uOrder;
        int32_t m_vOrder;
        Abc::FloatArraySample m_uKnot;
        Abc::FloatArraySample m_vKnot;

        // optional properties
        Abc::FloatArraySample m_positionWeights;
        ON3fGeomParam::Sample m_normals;
        OV2fGeomParam::Sample m_uvs;

        // optional trim curves
        int32_t m_trimNumLoops;
        Abc::Int32ArraySample m_trimNumCurves;
        Abc::Int32ArraySample m_trimNumVertices;
        Abc::Int32ArraySample m_trimOrder;
        Abc::FloatArraySample m_trimKnot;
        Abc::FloatArraySample m_trimMin;
        Abc::FloatArraySample m_trimMax;
        Abc::FloatArraySample m_trimU;
        Abc::FloatArraySample m_trimV;
        Abc::FloatArraySample m_trimW;
        bool m_hasTrimCurve;

        // bounds
        Abc::Box3d m_selfBounds;
    };

    //-*************************************************************************
    // NuPatch SCHEMA
    //-*************************************************************************

public:

    //! By convention we always define this_type in AbcGeom classes.
    //! Used by unspecified-bool-type conversion below
    typedef ONuPatchSchema this_type;
    typedef ONuPatchSchema::Sample sample_type;

    //-*************************************************************************
    // CONSTRUCTION, DESTRUCTION, ASSIGNMENT
    //-*************************************************************************

    //! The default constructor creates an empty ONuPatchSchema
    //! ...
    ONuPatchSchema()
    {
        m_selectiveExport = false;
        m_numSamples = 0;
        m_timeSamplingIndex = 0;
    }

    //! This constructor creates a new poly mesh writer.
    //! The first argument is an CompoundPropertyWriterPtr to use as a parent.
    //! The next is the name to give the schema which is usually the default
    //! name given by OFaceSet (.geom)   The remaining optional arguments
    //! can be used to override the ErrorHandlerPolicy, to specify
    //! MetaData, specify sparse sampling and to set TimeSampling.
    ONuPatchSchema( AbcA::CompoundPropertyWriterPtr iParent,
                     const std::string &iName,
                     const Abc::Argument &iArg0 = Abc::Argument(),
                     const Abc::Argument &iArg1 = Abc::Argument(),
                     const Abc::Argument &iArg2 = Abc::Argument(),
                     const Abc::Argument &iArg3 = Abc::Argument() );

    //! This constructor creates a new poly mesh writer.
    //! The first argument is an OCompundProperty to use as a parent, and from
    //! which the ErrorHandlerPolicy is derived.  The next is the name to give
    //! the schema which is usually the default name given by OFaceSet (.geom)
    //! The remaining optional arguments can be used to specify MetaData,
    //! specify sparse sampling and to set TimeSampling.
    ONuPatchSchema( Abc::OCompoundProperty iParent,
                     const std::string &iName,
                     const Abc::Argument &iArg0 = Abc::Argument(),
                     const Abc::Argument &iArg1 = Abc::Argument(),
                     const Abc::Argument &iArg2 = Abc::Argument() );

    //! Copy constructor.
    ONuPatchSchema(const ONuPatchSchema& iCopy)
        : OGeomBaseSchema<NuPatchSchemaInfo>()
    {
        *this = iCopy;
    }

    //-*************************************************************************
    // SCHEMA STUFF
    //-*************************************************************************

    //! Return the time sampling type, which is stored on each of the
    //! sub properties.
    AbcA::TimeSamplingPtr getTimeSampling() const
    {
        if( m_positionsProperty.valid() )
        {
            return m_positionsProperty.getTimeSampling();
        }
        else
        {
            return getObject().getArchive().getTimeSampling( 0 );
        }
    }

    void setTimeSampling( uint32_t iIndex );
    void setTimeSampling( AbcA::TimeSamplingPtr iTime );

    //-*************************************************************************
    // SAMPLE STUFF
    //-*************************************************************************

    //! Get number of samples written so far.
    //! ...
    size_t getNumSamples() const
    {
        return m_numSamples;
    }

    //! Set a sample!
    void set( const sample_type &iSamp );

    //! Set from previous sample. Will apply to each of positions,
    //! indices, and counts.
    void setFromPrevious();

    //-*************************************************************************
    // ABC BASE MECHANISMS
    // These functions are used by Abc to deal with errors, validity,
    // and so on.
    //-*************************************************************************

    //! Reset returns this function set to an empty, default
    //! state.
    void reset()
    {
        m_positionsProperty.reset();
        m_positionWeightsProperty.reset();
        m_velocitiesProperty.reset();
        m_numUProperty.reset();
        m_numVProperty.reset();
        m_uOrderProperty.reset();
        m_vOrderProperty.reset();
        m_uKnotProperty.reset();
        m_vKnotProperty.reset();

        m_normalsParam.reset();
        m_uvsParam.reset();

        // reset trim curve attributes
        m_trimNumLoopsProperty.reset();
        m_trimNumVerticesProperty.reset();
        m_trimOrderProperty.reset();
        m_trimKnotProperty.reset();
        m_trimMinProperty.reset();
        m_trimMaxProperty.reset();
        m_trimUProperty.reset();
        m_trimVProperty.reset();
        m_trimWProperty.reset();

        OGeomBaseSchema<NuPatchSchemaInfo>::reset();
    }

    //! Valid returns whether this function set is
    //! valid.
    bool valid() const
    {
        return ( ( OGeomBaseSchema<NuPatchSchemaInfo>::valid() &&
                     m_positionsProperty.valid() ) ||
                 m_selectiveExport );
    }

    //! unspecified-bool-type operator overload.
    //! ...
    ALEMBIC_OVERRIDE_OPERATOR_BOOL( ONuPatchSchema::valid() );

protected:
    void init( const AbcA::index_t iTsIdx, bool isSparse );

    //! Set only some property data. Does not need to be a valid schema sample
    //! This is to be used when created a file which will be layered in to
    //! another file.
    void selectiveSet( const Sample &iSamp );

    // Write out only some properties (UVs, normals).
    // This is to export data to layer into another file later.
    bool m_selectiveExport;

    // Number of times OPolyMeshSchema::set() has been called
    size_t m_numSamples;

    AbcA::index_t m_timeSamplingIndex;

    void createPositionProperties();
    void createKnotProperties();
    void createVelocityProperty();
    void createUVsProperty( const Sample &iSamp );
    void createNormalsProperty( const Sample &iSamp );
    void createPositionWeightsProperty();
    void createTrimPropreties();


    // point data
    Abc::OP3fArrayProperty m_positionsProperty;

    // required properties
    Abc::OInt32Property m_numUProperty;
    Abc::OInt32Property m_numVProperty;
    Abc::OInt32Property m_uOrderProperty;
    Abc::OInt32Property m_vOrderProperty;
    Abc::OFloatArrayProperty m_uKnotProperty;
    Abc::OFloatArrayProperty m_vKnotProperty;

    // optional properties
    Abc::OFloatArrayProperty m_positionWeightsProperty;
    ON3fGeomParam m_normalsParam;
    OV2fGeomParam m_uvsParam;
    Abc::OV3fArrayProperty m_velocitiesProperty;

    // optional trim curves
    Abc::OInt32Property m_trimNumLoopsProperty;
    Abc::OInt32ArrayProperty m_trimNumCurvesProperty;
    Abc::OInt32ArrayProperty m_trimNumVerticesProperty;
    Abc::OInt32ArrayProperty m_trimOrderProperty;
    Abc::OFloatArrayProperty m_trimKnotProperty;
    Abc::OFloatArrayProperty m_trimMinProperty;
    Abc::OFloatArrayProperty m_trimMaxProperty;
    Abc::OFloatArrayProperty m_trimUProperty;
    Abc::OFloatArrayProperty m_trimVProperty;
    Abc::OFloatArrayProperty m_trimWProperty;

};

//-*****************************************************************************
// SCHEMA OBJECT
//-*****************************************************************************
typedef Abc::OSchemaObject<ONuPatchSchema> ONuPatch;

typedef Util::shared_ptr< ONuPatch > ONuPatchPtr;

} // End namespace ALEMBIC_VERSION_NS

using namespace ALEMBIC_VERSION_NS;

} // End namespace AbcGeom
} // End namespace Alembic

#endif
