//
// Copyright 2016 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef PXR_USD_SDF_LAYER_H
#define PXR_USD_SDF_LAYER_H

/// \file sdf/layer.h

#include "pxr/pxr.h"
#include "pxr/usd/sdf/api.h"
#include "pxr/usd/sdf/data.h"
#include "pxr/usd/sdf/declareHandles.h"
#include "pxr/usd/sdf/identity.h"
#include "pxr/usd/sdf/layerOffset.h"
#include "pxr/usd/sdf/namespaceEdit.h"
#include "pxr/usd/sdf/path.h"
#include "pxr/usd/sdf/proxyTypes.h"
#include "pxr/usd/sdf/spec.h"
#include "pxr/usd/sdf/types.h"
#include "pxr/usd/ar/assetInfo.h"
#include "pxr/base/tf/declarePtrs.h"
#include "pxr/base/vt/value.h"

#include <boost/optional.hpp>

#include <atomic>
#include <functional>
#include <memory>
#include <set>
#include <string>
#include <vector>

PXR_NAMESPACE_OPEN_SCOPE

TF_DECLARE_WEAK_PTRS(SdfFileFormat);
TF_DECLARE_WEAK_AND_REF_PTRS(SdfLayerStateDelegateBase);

struct Sdf_AssetInfo;

/// \class SdfLayer 
///
/// A unit of scene description that you combine with other units of scene
/// description to form a shot, model, set, shader, and so on.
///
/// SdfLayer objects provide a persistent way to store layers on the
/// filesystem in .menva files.  Currently the supported file format is 
/// <c>.menva</c>, the ASCII file format. 
///
/// The FindOrOpen() method returns a new SdfLayer object with scene description
/// from a <c>.menva</c> file. Once read, a layer remembers which asset it was
/// read from. The Save() method saves the layer back out to the original file.
/// You can use the Export() method to write the layer to a different
/// location. You can use the GetIdentifier() method to get the layer's Id or
/// GetRealPath() to get the resolved, full file path.
///
/// Layers can have a timeCode range (startTimeCode and endTimeCode). This range
/// represents the suggested playback range, but has no impact on the extent of 
/// the animation data that may be stored in the layer. The metadatum 
/// "timeCodesPerSecond" is used to annotate how the time ordinate for samples
/// contained in the file scales to seconds. For example, if timeCodesPerSecond
/// is 24, then a sample at time ordinate 24 should be viewed exactly one second
/// after the sample at time ordinate 0.
/// 
/// Compared to Menv2x, layers are most closely analogous to 
/// hooksets, <c>.hook</c> files, and <c>.cue</c> files.
///
/// \todo 
/// \li Insert a discussion of subLayers semantics here.
///
/// \todo
/// \li Should have validate... methods for rootPrims
///
class SdfLayer 
    : public TfRefBase
    , public TfWeakBase
{
public:
    /// Destructor
    SDF_API
    virtual ~SdfLayer(); 

    /// Noncopyable
    SdfLayer(const SdfLayer&) = delete;
    SdfLayer& operator=(const SdfLayer&) = delete;

    ///
    /// \name Primary API
    /// @{

    /// Returns the schema this layer adheres to. This schema provides details
    /// about the scene description that may be authored in this layer.
    SDF_API const SdfSchemaBase& GetSchema() const;

    /// Returns the file format used by this layer.
    SDF_API const SdfFileFormatConstPtr& GetFileFormat() const;

    /// Type for specifying additional file format-specific arguments to
    /// layer API.
    typedef std::map<std::string, std::string> FileFormatArguments;

    /// Returns the file format-specific arguments used during the construction
    /// of this layer.
    SDF_API const FileFormatArguments& GetFileFormatArguments() const;

    /// Creates a new empty layer with the given identifier.
    ///
    /// The \p identifier must be either a real filesystem path or an asset
    /// path without version modifier. Attempting to create a layer using an
    /// identifier with a version specifier (e.g.  layer.menva@300100,
    /// layer.menva#5) raises a coding error, and returns a null layer
    /// pointer.
    ///
    /// Additional arguments may be supplied via the \p args parameter.
    /// These arguments may control behavior specific to the layer's
    /// file format.
    SDF_API
    static SdfLayerRefPtr CreateNew(const std::string &identifier,
                                    const std::string &realPath = std::string(),
                                    const FileFormatArguments &args =
                                    FileFormatArguments());

    /// Creates a new empty layer with the given identifier for a given file
    /// format class.
    ///
    /// This function has the same behavior as the other CreateNew function,
    /// but uses the explicitly-specified \p fileFormat instead of attempting
    /// to discern the format from \p identifier.
    SDF_API
    static SdfLayerRefPtr CreateNew(const SdfFileFormatConstPtr& fileFormat,
                                    const std::string &identifier,
                                    const std::string &realPath = std::string(),
                                    const FileFormatArguments &args =
                                    FileFormatArguments());

    /// Creates a new empty layer with the given identifier for a given file
    /// format class.
    ///
    /// This is so that Python File Format classes can create layers
    /// (CreateNew() doesn't work, because it already saves during
    /// construction of the layer. That is something specific (python
    /// generated) layer types may choose to not do.)
    ///
    /// The new layer will not be dirty.
    ///
    /// Additional arguments may be supplied via the \p args parameter. 
    /// These arguments may control behavior specific to the layer's
    /// file format.
    SDF_API
    static SdfLayerRefPtr New(const SdfFileFormatConstPtr& fileFormat,
                              const std::string &identifier,
                              const std::string &realPath = std::string(),
                              const FileFormatArguments &args = 
                              FileFormatArguments());

    /// Returns the layer for the given path if found in the layer registry.
    /// If the layer cannot be found, a null handle is returned.
    SDF_API
    static SdfLayerHandle Find(
        const std::string &identifier,
        const FileFormatArguments &args = FileFormatArguments());

    /// Returns the layer for \p layerPath, assumed to be relative to the path
    /// of the \p anchor layer. If the \p anchor layer is invalid, a coding
    /// error is raised, and a null handle is returned. If \p layerPath is not
    /// relative, this method is equivalent to \c Find(layerPath).
    SDF_API
    static SdfLayerHandle FindRelativeToLayer(
        const SdfLayerHandle &anchor,
        const std::string &layerPath,
        const FileFormatArguments &args = FileFormatArguments());

    /// Return an existing layer with the given \p identifier and \p args, or 
    /// else load it from disk. If the layer can't be found or loaded, 
    /// an error is posted and a null layer is returned.
    ///
    /// Arguments in \p args will override any arguments specified in
    /// \p identifier.
    SDF_API
    static SdfLayerRefPtr FindOrOpen(
        const std::string &identifier,
        const FileFormatArguments &args = FileFormatArguments());
        
    /// Load the given layer from disk as a new anonymous layer. If the
    /// layer can't be found or loaded, an error is posted and a null
    /// layer is returned.
    ///
    /// The anonymous layer does not retain any knowledge of the backing
    /// file on the filesystem.
    ///
    /// \p metadataOnly is a flag that asks for only the layer metadata
    /// to be read in, which can be much faster if that is all that is
    /// required.  Note that this is just a hint: some FileFormat readers
    /// may disregard this flag and still fully populate the layer contents.
    ///
    /// An optional \p tag may be specified.  See CreateAnonymous for details.
    SDF_API
    static SdfLayerRefPtr OpenAsAnonymous(
        const std::string &layerPath,
        bool metadataOnly = false,
        const std::string& tag = std::string());

    /// Returns the data from the absolute root path of this layer.
    SDF_API
    SdfDataRefPtr GetMetadata() const;

    /// Returns handles for all layers currently held by the layer registry.
    SDF_API
    static SdfLayerHandleSet GetLoadedLayers();

    /// Returns whether this layer has no significant data.
    SDF_API
    bool IsEmpty() const;

    /// Copies the content of the given layer into this layer.
    /// Source layer is unmodified.
    SDF_API
    void TransferContent(const SdfLayerHandle& layer);

    /// Creates a new \e anonymous layer with an optional \p tag. An anonymous
    /// layer is a layer with a system assigned identifier, that cannot be
    /// saved to disk via Save(). Anonymous layers have an identifier, but no
    /// real path or other asset information fields.
    ///
    /// Anonymous layers may be tagged, which can be done to aid debugging
    /// subsystems that make use of anonymous layers.  The tag becomes the
    /// display name of an anonymous layer, and is also included in the
    /// generated identifier. Untagged anonymous layers have an empty display
    /// name.
    ///
    /// Additional arguments may be supplied via the \p args parameter.
    /// These arguments may control behavior specific to the layer's
    /// file format.
    SDF_API
    static SdfLayerRefPtr CreateAnonymous(
        const std::string& tag = std::string(),
        const FileFormatArguments& args = FileFormatArguments());

    /// Create an anonymous layer with a specific \p format.
    SDF_API
    static SdfLayerRefPtr CreateAnonymous(
        const std::string &tag, const SdfFileFormatConstPtr &format,
        const FileFormatArguments& args = FileFormatArguments());

    /// Returns true if this layer is an anonymous layer.
    SDF_API
    bool IsAnonymous() const;

    /// Returns true if the \p identifier is an anonymous layer unique
    /// identifier.
    SDF_API
    static bool IsAnonymousLayerIdentifier(const std::string& identifier);

    /// Returns the display name for the given \p identifier, using the same 
    /// rules as GetDisplayName.
    SDF_API
    static std::string GetDisplayNameFromIdentifier(
        const std::string& identifier);

    /// @}
    /// \name File I/O
    /// @{

    /// Converts \e layerPath to a file system path.
    SDF_API
    static std::string ComputeRealPath(const std::string &layerPath);

    /// Returns \c true if successful, \c false if an error occurred.
    /// Returns \c false if the layer has no remembered file name or the 
    /// layer type cannot be saved. The layer will not be overwritten if the 
    /// file exists and the layer is not dirty unless \p force is true.
    SDF_API
    bool Save(bool force = false) const;

    /// Exports this layer to a file.
    /// Returns \c true if successful, \c false if an error occurred.
    ///
    /// If \p comment is not empty, the layer gets exported with the given
    /// comment. Additional arguments may be supplied via the \p args parameter.
    /// These arguments may control behavior specific to the exported layer's
    /// file format.
    ///
    /// Note that the file name or comment of the original layer is not
    /// updated. This only saves a copy of the layer to the given filename.
    /// Subsequent calls to Save() will still save the layer to it's
    /// previously remembered file name.
    SDF_API
    bool Export(const std::string& filename, 
                const std::string& comment = std::string(),
                const FileFormatArguments& args = FileFormatArguments()) const;

    /// Writes this layer to the given string.
    ///
    /// Returns \c true if successful and sets \p result, otherwise
    /// returns \c false.
    SDF_API
    bool ExportToString(std::string* result) const;

    /// Reads this layer from the given string.
    ///
    /// Returns \c true if successful, otherwise returns \c false.
    SDF_API
    bool ImportFromString(const std::string &string);

    /// Clears the layer of all content.
    ///
    /// This restores the layer to a state as if it had just been created
    /// with CreateNew().  This operation is Undo-able.
    ///
    /// The fileName and whether journaling is enabled are not affected
    /// by this method.
    SDF_API
    void Clear();

    /// Reloads the layer from its persistent representation.
    ///
    /// This restores the layer to a state as if it had just been created
    /// with FindOrOpen().  This operation is Undo-able.
    ///
    /// The fileName and whether journaling is enabled are not affected
    /// by this method.
    ///
    /// When called with force = false (the default), Reload attempts to
    /// avoid reloading layers that have not changed on disk. It does so
    /// by comparing the file's modification time (mtime) to when the
    /// file was loaded. If the layer has unsaved modifications, this
    /// mechanism is not used, and the layer is reloaded from disk.
    ///
    /// Passing true to the \p force parameter overrides this behavior,
    /// forcing the layer to be reloaded from disk regardless of whether
    /// it has changed.
    SDF_API
    bool Reload(bool force = false);

    /// Reloads the specified layers.
    ///
    /// Returns \c false if one or more layers failed to reload.
    ///
    /// See \c Reload() for a description of the \p force flag.
    ///
    SDF_API
    static bool ReloadLayers(const std::set<SdfLayerHandle>& layers,
                             bool force = false);

    /// Imports the content of the given layer path, replacing the content
    /// of the current layer.
    /// Note: If the layer path is the same as the current layer's real path,
    /// no action is taken (and a warning occurs). For this case use
    /// Reload().
    SDF_API
    bool Import(const std::string &layerPath);

    /// @}
    /// \name External references
    /// @{

    /// Return paths of all external references of layer.
    SDF_API
    std::set<std::string> GetExternalReferences();

    /// Updates the external references of the layer.
    ///
    /// If only the old asset path is given, this update works as delete, 
    /// removing any sublayers or prims referencing the pathtype using the
    /// old asset path as reference.
    /// 
    /// If new asset path is supplied, the update works as "rename", updating
    /// any occurrence of the old reference to the new reference.
    SDF_API
    bool UpdateExternalReference(
        const std::string &oldAssetPath,
        const std::string &newAssetPath=std::string());

    /// @}
    /// \name Identification
    ///
    /// A layer's identifier is a string that uniquely identifies a layer.
    /// At minimum, it is the string by which the layer was created, either
    /// via FindOrOpen or CreateNew. If additional arguments were passed
    /// to those functions, those arguments will be encoded in the identifier.
    /// 
    /// For example: 
    ///     FindOrOpen('foo.sdf', args={'a':'b', 'c':'d'}).identifier
    ///         => "foo.sdf?sdf_args:a=b&c=d"
    ///
    /// Note that this means the identifier may in general not be a path.
    ///
    /// The identifier format is subject to change; consumers should NOT
    /// parse layer identifiers themselves, but should use the supplied
    /// SplitIdentifier and CreateIdentifier helper functions.
    ///
    /// @{

    /// Splits the given layer identifier into its constituent layer path
    /// and arguments.
    SDF_API
    static bool SplitIdentifier(
        const std::string& identifier,
        std::string* layerPath,
        FileFormatArguments* arguments);

    /// Joins the given layer path and arguments into an identifier.
    SDF_API
    static std::string CreateIdentifier(
        const std::string& layerPath,
        const FileFormatArguments& arguments);

    /// Returns the layer identifier.
    SDF_API
    const std::string& GetIdentifier() const;

    /// Sets the layer identifier. 
    /// Note that the new identifier must have the same arguments (if any)
    /// as the old identifier.
    SDF_API
    void SetIdentifier(const std::string& identifier);

    /// Update layer asset information. Calling this method re-resolves the
    /// layer identifier, which updates asset information such as the layer
    /// file revision, real path, and repository path. If \p fileVersion is
    /// supplied, it is used as the layer version if the identifier does not
    /// have a version or label specifier. This is typically used to tell Sd
    /// what the version of a layer is after submitting a new revision to the
    /// asset system.
    SDF_API
    void UpdateAssetInfo(const std::string& fileVersion = std::string());

    /// Returns the layer's display name.
    ///
    /// The display name is the base filename of the identifier.
    SDF_API
    std::string GetDisplayName() const;

    /// Returns the file system path where this layer exists or may exist
    /// after a call to Save.
    SDF_API
    const std::string& GetRealPath() const;

    /// Returns the file extension to use for this layer.
    /// If this layer was loaded from disk, it should match the extension
    /// of the file format it was loaded as; if this is an anonymous
    /// in-memory layer it will be the default extension.
    SDF_API
    std::string GetFileExtension() const;

    /// Returns the asset system version of this layer. If a layer is loaded
    /// from a location that is not version managed, or a configured asset
    /// system is not present when the layer is loaded or created, the version
    /// is empty. By default, asset version tracking is disabled; this method
    /// returns empty unless asset version tracking is enabled.
    SDF_API
    const std::string& GetVersion() const;

    /// Returns the layer identifier in asset path form. In the presence of a
    /// properly configured path resolver, the asset path is a double-slash
    /// prefixed depot path. If the path resolver is not configured, the asset
    /// path of a layer is empty.
    SDF_API
    const std::string& GetRepositoryPath() const;

    /// Returns the asset name associated with this layer.
    SDF_API
    const std::string& GetAssetName() const;

    /// Returns resolve information from the last time the layer identifier
    /// was resolved.
    SDF_API
    const VtValue& GetAssetInfo() const;

    /// Make the given \p relativePath absolute using the identifier of this
    /// layer.  If this layer does not have an identifier, or if the layer
    /// identifier is itself relative, \p relativePath is returned unmodified.
    SDF_API
    std::string ComputeAbsolutePath(const std::string &relativePath);

    /// @}

    /// \name Fields
    ///
    /// All scene description for a given object is stored as a set of
    /// key/value pairs called fields. These methods provide direct access to
    /// those fields, though most clients should use the Spec API to ensure
    /// data consistency.
    ///
    /// These methods all take SdfPath to identify the queried spec.
    ///
    /// @{

    /// Return the specifiers for \a path. This returns default constructed
    /// specifiers if no spec exists at \a path.
    SDF_API
    SdfSpecType GetSpecType(const SdfPath& path) const;

    /// Return whether a spec exists at \a path.
    SDF_API
    bool HasSpec(const SdfPath& path) const;

    /// Return the names of all the fields that are set at \p path.
    SDF_API
    std::vector<TfToken> ListFields(const SdfPath& path) const;

    /// Return whether a value exists for the given \a path and \a fieldName.
    /// Optionally returns the value if it exists.
    SDF_API
    bool HasField(const SdfPath& path, const TfToken& fieldName,
                  VtValue *value=NULL) const;
    SDF_API
    bool HasField(const SdfPath& path, const TfToken& fieldName,
                  SdfAbstractDataValue *value) const;

    /// Returns \c true if the object has a non-empty value with name
    /// \p name and type \p T.  If value ptr is provided, returns the
    /// value found.
    template <class T>
    bool HasField(const SdfPath& path, const TfToken &name, 
        T* value) const
    {
        if (!value) {
            return HasField(path, name, static_cast<VtValue *>(NULL));
        }

        SdfAbstractDataTypedValue<T> outValue(value);
        const bool hasValue = HasField(
            path, name, static_cast<SdfAbstractDataValue *>(&outValue));

        if (std::is_same<T, SdfValueBlock>::value) {
            return hasValue && outValue.isValueBlock;
        }

        return hasValue && (!outValue.isValueBlock);
    }

    /// Return the type of the value for \p name on spec \p path.  If no such
    /// field exists, return typeid(void).
    std::type_info const &GetFieldTypeid(
        const SdfPath &path, const TfToken &name) const {
        return _data->GetTypeid(path, name);
    }

    /// Return whether a value exists for the given \a path and \a fieldName and
    /// \a keyPath.  The \p keyPath is a ':'-separated path addressing an
    /// element in sub-dictionaries.  Optionally returns the value if it exists.
    SDF_API
    bool HasFieldDictKey(const SdfPath& path,
                         const TfToken &fieldName,
                         const TfToken &keyPath,
                         VtValue *value=NULL) const;
    SDF_API
    bool HasFieldDictKey(const SdfPath& path,
                         const TfToken &fieldName,
                         const TfToken &keyPath,
                         SdfAbstractDataValue *value) const;

    /// Returns \c true if the object has a non-empty value with name \p name
    /// and \p keyPath and type \p T.  If value ptr is provided, returns the
    /// value found.  The \p keyPath is a ':'-separated path addressing an
    /// element in sub-dictionaries.
    template <class T>
    bool HasFieldDictKey(const SdfPath& path, const TfToken &name,
                         const TfToken &keyPath, T* value) const
    {
        if (!value) {
            return HasFieldDictKey(path, name, keyPath,
                                   static_cast<VtValue *>(NULL));
        }

        SdfAbstractDataTypedValue<T> outValue(value);
        return HasFieldDictKey(path, name, keyPath,
                               static_cast<SdfAbstractDataValue *>(&outValue));
    }


    /// Return the value for the given \a path and \a fieldName. Returns an
    /// empty value if none is set.
    SDF_API
    VtValue GetField(const SdfPath& path,
                     const TfToken& fieldName) const;

    /// Return the value for the given \a path and \a fieldName. Returns the
    /// provided \a defaultValue value if none is set.
    template <class T>
    inline T GetFieldAs(const SdfPath& path, 
        const TfToken& fieldName, const T& defaultValue = T()) const
    {
        return _data->GetAs<T>(path, fieldName, defaultValue);
    }

    /// Return the value for the given \a path and \a fieldName at \p
    /// keyPath. Returns an empty value if none is set.  The \p keyPath is a
    /// ':'-separated path addressing an element in sub-dictionaries.
    SDF_API
    VtValue GetFieldDictValueByKey(const SdfPath& path,
                                   const TfToken& fieldName,
                                   const TfToken& keyPath) const;

    /// Set the value of the given \a path and \a fieldName.
    SDF_API
    void SetField(const SdfPath& path, const TfToken& fieldName,
        const VtValue& value);
    SDF_API
    void SetField(const SdfPath& path, const TfToken& fieldName,
        const SdfAbstractDataConstValue& value);

    /// Set the value of the given \a path and \a fieldName.
    template <class T>
    void SetField(const SdfPath& path, const TfToken& fieldName, 
        const T& val) 
    {
        // Ideally, this would make use of the SdfAbstractDataConstValue
        // API to avoid unnecessarily copying the value into a VtValue.
        // However, Sdf needs to create a VtValue for change processing.
        // If the underlying SdAbstractData implementation also needs a 
        // VtValue, using the SdfAbstractDataConstValue API would cause
        // another copy to be made. So, it's more efficient to just create 
        // the VtValue once here and push that along.
        SetField(path, fieldName, VtValue(val));
    }

    /// Set the value of the given \a path and \a fieldName.  The \p keyPath is a
    /// ':'-separated path addressing an element in sub-dictionaries.
    SDF_API
    void SetFieldDictValueByKey(const SdfPath& path,
                                const TfToken& fieldName,
                                const TfToken& keyPath,
                                const VtValue& value);
    SDF_API
    void SetFieldDictValueByKey(const SdfPath& path,
                                const TfToken& fieldName,
                                const TfToken& keyPath,
                                const SdfAbstractDataConstValue& value);

    /// Set the value of the given \a path and \a fieldName.  The \p keyPath is
    /// a ':'-separated path addressing an element in sub-dictionaries.
    template <class T>
    void SetFieldDictValueByKey(const SdfPath& path,
                                const TfToken& fieldName,
                                const TfToken& keyPath,
                                const T& val)
    {
        // Ideally, this would make use of the SdfAbstractDataConstValue
        // API to avoid unnecessarily copying the value into a VtValue.
        // However, Sdf needs to create a VtValue for change processing.
        // If the underlying SdAbstractData implementation also needs
        // VtValue, using the SdfAbstractDataConstValue API would cause
        // another copy to be made. So, it's more efficient to just create
        // the VtValue once here and push that along.
        SetFieldDictValueByKey(path, fieldName, keyPath, VtValue(val));
    }

    /// Remove the field at \p path and \p fieldName, if one exists.
    SDF_API
    void EraseField(const SdfPath& path, const TfToken& fieldName);

    /// Remove the field at \p path and \p fieldName and \p keyPath, if one
    /// exists.  The \p keyPath is a ':'-separated path addressing an
    /// element in sub-dictionaries.
    SDF_API
    void EraseFieldDictValueByKey(const SdfPath& path,
                                  const TfToken& fieldName,
                                  const TfToken& keyPath);

    /// \name Traversal
    /// @{

    /// Callback function for Traverse. This callback will be invoked with
    /// the path of each spec that is visited.
    /// \sa Traverse
    typedef std::function<void(const SdfPath&)> TraversalFunction;

    // Traverse will perform a traversal of the scene description hierarchy
    // rooted at \a path, calling \a func on each spec that it finds.
    SDF_API
    void Traverse(const SdfPath& path, const TraversalFunction& func);

    /// @}

    /// \name Metadata
    /// @{

    /// Returns the color configuration asset-path for this layer.
    ///
    /// The default value is an empty asset-path.
    SDF_API
    SdfAssetPath GetColorConfiguration() const;

    /// Sets the color configuration asset-path for this layer.
    SDF_API
    void SetColorConfiguration(const SdfAssetPath &colorConfiguration);

    /// Returns true if color configuration metadata is set in this layer.
    /// \sa GetColorConfiguration(), SetColorConfiguration()
    SDF_API 
    bool HasColorConfiguration() const;
    
    /// Clears the color configuration metadata authored in this layer. 
    /// \sa HasColorConfiguration(), SetColorConfiguration()
    SDF_API
    void ClearColorConfiguration();

    /// Returns the color management system used to interpret the color 
    /// configuration asset-path authored in this layer.
    ///
    /// The default value is an empty token, which implies that the clients 
    /// will have to determine the color management system from the color 
    /// configuration asset path (i.e. from its file extension), if it's 
    /// specified. 
    SDF_API
    TfToken GetColorManagementSystem() const;

    /// Sets the color management system used to interpret the color 
    /// configuration asset-path authored this layer.
    SDF_API
    void SetColorManagementSystem(const TfToken &cms);

    /// Returns true if colorManagementSystem metadata is set in this layer.
    /// \sa GetColorManagementSystem(), SetColorManagementSystem()
    SDF_API 
    bool HasColorManagementSystem() const;
    
    /// Clears the 'colorManagementSystem' metadata authored in this layer. 
    /// \sa HascolorManagementSystem(), SetColorManagementSystem()
    SDF_API
    void ClearColorManagementSystem();

    /// Returns the comment string for this layer.
    ///
    /// The default value for comment is "".
    SDF_API
    std::string GetComment() const;

    /// Sets the comment string for this layer.
    SDF_API
    void SetComment(const std::string &comment);
    
    /// Return the defaultPrim metadata for this layer.  This field
    /// indicates the name of which root prim should be targeted by a reference
    /// or payload to this layer that doesn't specify a prim path.
    ///
    /// The default value is the empty token.
    SDF_API
    TfToken GetDefaultPrim() const;

    /// Set the default prim metadata for this layer.  The root prim with this
    /// name will be targeted by a reference or a payload to this layer that
    /// doesn't specify a prim path.  Note that this must be a root prim
    /// <b>name</b> not a path.  E.g. "rootPrim" rather than "/rootPrim".  See
    /// GetDefaultPrim().
    SDF_API
    void SetDefaultPrim(const TfToken &name);

    /// Clear the default prim metadata for this layer.  See GetDefaultPrim()
    /// and SetDefaultPrim().
    SDF_API
    void ClearDefaultPrim();

    /// Return true if the default prim metadata is set in this layer.  See
    /// GetDefaultPrim() and SetDefaultPrim().
    SDF_API
    bool HasDefaultPrim();

    /// Returns the documentation string for this layer.
    ///
    /// The default value for documentation is "".
    SDF_API
    std::string GetDocumentation() const;

    /// Sets the documentation string for this layer.
    SDF_API
    void SetDocumentation(const std::string &documentation);

    /// Returns the layer's start timeCode.
    ///
    /// The start and end timeCodes of a layer represent the suggested playback 
    /// range.  However, time-varying content is not limited to the timeCode range 
    /// of the layer.
    ///
    /// The default value for startTimeCode is 0.
    SDF_API
    double GetStartTimeCode() const;

    /// Sets the layer's start timeCode.
    SDF_API
    void SetStartTimeCode(double startTimecode);

    /// Returns true if the layer has a startTimeCode opinion.
    SDF_API
    bool HasStartTimeCode() const;

    /// Clear the startTimeCode opinion.
    SDF_API
    void ClearStartTimeCode();
    
    /// Returns the layer's end timeCode.
    /// The start and end timeCode of a layer represent a suggested playback range.  
    /// However, time-varying content is not limited to the timeCode range of the 
    /// layer.
    ///
    /// The default value for endTimeCode is 0.
    SDF_API
    double GetEndTimeCode() const;

    /// Sets the layer's end timeCode.
    SDF_API
    void SetEndTimeCode(double endTimeCode);

    /// Returns true if the layer has an endTimeCode opinion.
    SDF_API
    bool HasEndTimeCode() const;

    /// Clear the endTimeCode opinion.
    SDF_API
    void ClearEndTimeCode();
    
    /// Returns the layer's timeCodes per second.
    /// 
    /// Scales the time ordinate for samples contained in the file to seconds.  
    /// If timeCodesPerSecond is 24, then a sample at time ordinate 24 should 
    /// be viewed exactly one second after the sample at time ordinate 0.
    ///
    /// If this layer doesn't have an authored value for timeCodesPerSecond, but
    /// it does have an authored value for framesPerSecond, this method will
    /// return the value of framesPerSecond.  This "dynamic fallback" allows
    /// layers to lock framesPerSecond and timeCodesPerSecond to the same value
    /// by specifying only framesPerSecond.
    /// 
    /// The default value of timeCodesPerSecond (which is used only if there is
    /// no authored value for either timeCodesPerSecond or framesPerSecond) is
    /// 24.
    SDF_API
    double GetTimeCodesPerSecond() const;

    /// Sets the layer's timeCodes per second
    SDF_API
    void SetTimeCodesPerSecond(double timeCodesPerSecond);

    /// Returns true if the layer has a timeCodesPerSecond opinion.
    SDF_API
    bool HasTimeCodesPerSecond() const;

    /// Clear the timeCodesPerSecond opinion.
    SDF_API
    void ClearTimeCodesPerSecond();

    /// Returns the layer's frames per second.
    /// 
    /// This makes an advisory statement about how the contained data can be 
    /// most usefully consumed and presented.  It's primarily an indication of 
    /// the expected playback rate for the data, but a timeline editing tool 
    /// might also want to use this to decide how to scale and label its 
    /// timeline.  
    /// 
    /// The default  value for framesPerSecond is 24.
    SDF_API
    double GetFramesPerSecond() const;

    /// Sets the layer's frames per second
    SDF_API
    void SetFramesPerSecond(double framesPerSecond);

    /// Returns true if the layer has a frames per second opinion.
    SDF_API
    bool HasFramesPerSecond() const;

    /// Clear the framesPerSecond opinion.
    SDF_API
    void ClearFramesPerSecond();
    
    /// Returns the layer's frame precision.
    SDF_API
    int GetFramePrecision() const;

    /// Sets the layer's frame precision.
    SDF_API
    void SetFramePrecision(int framePrecision);

    /// Returns true if the layer has a frames precision opinion.
    SDF_API
    bool HasFramePrecision() const;

    /// Clear the framePrecision opinion.
    SDF_API
    void ClearFramePrecision();

    /// Returns the layer's owner.
    SDF_API
    std::string GetOwner() const;

    /// Sets the layer's owner.
    SDF_API
    void SetOwner(const std::string& owner);

    /// Returns true if the layer has an owner opinion.
    SDF_API
    bool HasOwner() const;

    /// Clear the owner opinion.
    SDF_API
    void ClearOwner();

    /// Returns the layer's session owner.
    /// Note: This should only be used by session layers.
    SDF_API
    std::string GetSessionOwner() const;

    /// Sets the layer's session owner.
    /// Note: This should only be used by session layers.
    SDF_API
    void SetSessionOwner(const std::string& owner);

    /// Returns true if the layer has a session owner opinion.
    SDF_API
    bool HasSessionOwner() const;

    // Clear the session owner opinion.
    SDF_API
    void ClearSessionOwner();

    /// Returns true if the layer's sublayers are expected to have owners.
    SDF_API
    bool GetHasOwnedSubLayers() const;

    /// Sets whether the layer's sublayers are expected to have owners.
    SDF_API
    void SetHasOwnedSubLayers(bool);

    /// Returns the CustomLayerData dictionary associated with this layer.
    /// 
    /// This is a dictionary is custom metadata that is associated with
    /// this layer. It allows users to encode any set of information for
    /// human or program consumption.
    SDF_API
    VtDictionary GetCustomLayerData() const;

    /// Sets the CustomLayerData dictionary associated with this layer.
    SDF_API
    void SetCustomLayerData(const VtDictionary& value);

    /// Returns true if CustomLayerData is authored on the layer.
    SDF_API
    bool HasCustomLayerData() const;

    /// Clears out the CustomLayerData dictionary associated with this layer.
    SDF_API
    void ClearCustomLayerData();

    /// @}
    /// \name Prims
    /// @{

    // Type for root prims view.
    typedef SdfPrimSpecView RootPrimsView;

    /// Returns a vector of the layer's root prims
    SDF_API
    RootPrimsView GetRootPrims() const;

    /// Sets a new vector of root prims.
    /// You can re-order, insert and remove prims but cannot 
    /// rename them this way.  If any of the listed prims have 
    /// an existing owner, they will be reparented.
    SDF_API
    void SetRootPrims(const SdfPrimSpecHandleVector &rootPrims);

    /// Adds a new root prim at the given index.
    /// If the index is -1, the prim is inserted at the end.
    /// The layer will take ownership of the prim, via a TfRefPtr.
    /// Returns true if successful, false if failed (for example,
    /// due to a duplicate name).
    SDF_API
    bool InsertRootPrim(const SdfPrimSpecHandle &prim, int index = -1);

    /// Remove a root prim.
    SDF_API
    void RemoveRootPrim(const SdfPrimSpecHandle &prim);

    /// Cause \p spec to be removed if it no longer affects the scene when the 
    /// last change block is closed, or now if there are no change blocks.
    SDF_API
    void ScheduleRemoveIfInert(const SdfSpec& spec);

    /// Removes scene description that does not affect the scene in the 
    /// layer namespace beginning with \p prim.
    ///
    /// Calling this method on a prim will only clean up prims with specifier
    /// 'over' that are not contributing any opinions.  The \p prim will only
    /// be removed if all of its nameChildren are also inert. The hierarchy 
    /// \p prim is defined in will be pruned up to the layer root for each 
    /// successive inert parent that has specifier 'over'.
    /// 
    /// note: PrimSpecs that contain any PropertySpecs, even PropertySpecs with 
    ///       required fields only (see PropertySpec::HasRequiredFieldsOnly) 
    ///       are not considered inert, and thus the prim won't be removed.
    SDF_API
    void RemovePrimIfInert(SdfPrimSpecHandle prim);

    /// Removes prop if it has only required fields (i.e. is not 
    /// contributing any opinions to the scene other than property 
    /// instantiation).
    /// 
    /// The hierarchy \p prop is defined in will then be pruned up to the 
    /// layer root for each successive inert parent.
    SDF_API
    void RemovePropertyIfHasOnlyRequiredFields(SdfPropertySpecHandle prop);

    /// Removes all scene description in this layer that does not affect the
    /// scene.
    ///
    /// This method walks the layer namespace hierarchy and removes any prims
    /// and that are not contributing any opinions.
    SDF_API
    void RemoveInertSceneDescription();

    /// Returns the list of prim names for this layer's reorder rootPrims
    /// statement.
    ///
    /// See SetRootPrimOrder() for more info.
    SDF_API
    SdfNameOrderProxy GetRootPrimOrder() const;
   
    /// Given a list of (possible sparse) prim names, authors a reorder
    /// rootPrims statement for this prim. 
    ///
    /// This reorder statement can modify the order of root prims that have 
    /// already been explicitly ordered with InsertRootPrim() or SetRootPrims();
    /// but only during composition.  Therefore, GetRootPrims(), 
    /// InsertRootPrim(), SetRootPrims(), etc. do not read, author, or pay any 
    /// attention to this statement.
    SDF_API
    void SetRootPrimOrder(const std::vector<TfToken>& names);

    /// Adds a new root prim name in the root prim order.
    /// If the index is -1, the name is inserted at the end.
    SDF_API
    void InsertInRootPrimOrder(const TfToken &name, int index = -1);

    /// Removes a root prim name from the root prim order.
    SDF_API
    void RemoveFromRootPrimOrder(const TfToken & name);

    /// Removes a root prim name from the root prim order by index.
    SDF_API
    void RemoveFromRootPrimOrderByIndex(int index);

    /// Reorders the given list of prim names according to the reorder rootPrims
    /// statement for this layer.
    ///
    /// This routine employs the standard list editing operations for ordered
    /// items in a ListEditor.
    SDF_API
    void ApplyRootPrimOrder(std::vector<TfToken> *vec) const;

    /// @}
    /// \name Sublayers
    /// @{

    /// Returns a proxy for this layer's sublayers.
    ///
    /// Sub-layers are the weaker layers directly included by this layer.
    /// They're in order from strongest to weakest and they're all weaker
    /// than this layer.
    ///
    /// Edits through the proxy changes the sublayers.  If this layer does
    /// not have any sublayers the proxy is empty.
    SDF_API
    SdfSubLayerProxy GetSubLayerPaths() const;

    /// Sets the paths of the layer's sublayers.
    SDF_API
    void SetSubLayerPaths(const std::vector<std::string>& newPaths);

    /// Returns the number of sublayer paths (and offsets).
    SDF_API
    size_t GetNumSubLayerPaths() const;

    /// Inserts new sublayer path at the given index.
    ///
    /// The default index of -1 means to insert at the end.
    SDF_API
    void InsertSubLayerPath(const std::string& path, int index = -1);

    /// Removes sublayer path at the given index.
    SDF_API
    void RemoveSubLayerPath(int index);

    /// Returns the layer offsets for all the subLayer paths.
    SDF_API
    SdfLayerOffsetVector GetSubLayerOffsets() const;

    /// Returns the layer offset for the subLayer path at the given index.
    SDF_API
    SdfLayerOffset GetSubLayerOffset(int index) const;

    /// Sets the layer offset for the subLayer path at the given index.
    SDF_API
    void SetSubLayerOffset(const SdfLayerOffset& offset, int index);

    /// @}
    /// \name Muting
    /// @{

    /// Returns the set of muted layer paths.
    SDF_API
    static std::set<std::string> GetMutedLayers();

    /// Returns \c true if the current layer is muted.
    SDF_API
    bool IsMuted() const;

    /// Returns \c true if the specified layer path is muted.
    SDF_API
    static bool IsMuted(const std::string &path);

    /// Mutes the current layer if \p muted is \c true, and unmutes it
    /// otherwise.
    SDF_API
    void SetMuted(bool muted);

    /// Add the specified path to the muted layers set.
    SDF_API
    static void AddToMutedLayers(const std::string &mutedPath);

    /// Remove the specified path from the muted layers set.
    SDF_API
    static void RemoveFromMutedLayers(const std::string &mutedPath);

    /// @}
    /// \name Lookup
    /// @{

    /// Returns the layer's pseudo-root prim.
    ///
    /// The layer's root prims are namespace children of the pseudo-root.
    /// The pseudo-root exists to make the namespace hierarchy a tree
    /// instead of a forest.  This simplifies the implementation of
    /// some algorithms.
    ///
    /// A layer always has a pseudo-root prim.
    SDF_API
    SdfPrimSpecHandle GetPseudoRoot() const;

    /// Returns the object at the given \p path.
    ///
    /// There is no distinction between an absolute and relative path
    /// at the SdLayer level.
    ///
    /// Returns \c NULL if there is no object at \p path.
    SDF_API
    SdfSpecHandle GetObjectAtPath(const SdfPath &path);

    /// Returns the prim at the given \p path.
    ///
    /// Returns \c NULL if there is no prim at \p path.
    /// This is simply a more specifically typed version of
    /// \c GetObjectAtPath().
    SDF_API
    SdfPrimSpecHandle GetPrimAtPath(const SdfPath &path);

    /// Returns a property at the given \p path.
    ///
    /// Returns \c NULL if there is no property at \p path.
    /// This is simply a more specifically typed version of
    /// \c GetObjectAtPath().
    SDF_API
    SdfPropertySpecHandle GetPropertyAtPath(const SdfPath &path);

    /// Returns an attribute at the given \p path.
    ///
    /// Returns \c NULL if there is no attribute at \p path.
    /// This is simply a more specifically typed version of
    /// \c GetObjectAtPath().
    SDF_API
    SdfAttributeSpecHandle GetAttributeAtPath(const SdfPath &path);

    /// Returns a relationship at the given \p path.
    ///
    /// Returns \c NULL if there is no relationship at \p path.
    /// This is simply a more specifically typed version of
    /// \c GetObjectAtPath().
    SDF_API
    SdfRelationshipSpecHandle GetRelationshipAtPath(const SdfPath &path);

    /// @}
    /// \name Permissions
    /// @{

    /// Returns true if the caller is allowed to modify the layer and 
    /// false otherwise.  A layer may have to perform some action to acquire 
    /// permission to be edited.
    SDF_API
    bool PermissionToEdit() const;

    /// Returns true if the caller is allowed to save the layer to its 
    /// existing fileName and false otherwise.
    SDF_API
    bool PermissionToSave() const;

    /// Sets permission to edit.
    SDF_API
    void SetPermissionToEdit(bool allow);

    /// Sets permission to save.
    SDF_API
    void SetPermissionToSave(bool allow);

    /// @}
    /// \name Batch namespace editing
    /// @{

    /// Check if a batch of namespace edits will succeed.  This returns
    /// \c SdfNamespaceEditDetail::Okay if they will succeed as a batch,
    /// \c SdfNamespaceEditDetail::Unbatched if the edits will succeed but
    /// will be applied unbatched, and \c SdfNamespaceEditDetail::Error
    /// if they will not succeed.  No edits will be performed in any case.
    ///
    /// If \p details is not \c NULL and the method does not return \c Okay
    /// then details about the problems will be appended to \p details.  A
    /// problem may cause the method to return early, so \p details may not
    /// list every problem.
    ///
    /// Note that Sdf does not track backpointers so it's unable to fix up
    /// targets/connections to namespace edited objects.  Clients must fix
    /// those to prevent them from falling off.  In addition, this method
    /// will report failure if any relational attribute with a target to
    /// a namespace edited object is subsequently edited (in the same
    /// batch).  Clients should perform edits on relational attributes
    /// first.
    ///
    /// Clients may wish to report unbatch details to the user to confirm
    /// that the edits should be applied unbatched.  This will give the
    /// user a chance to correct any problems that cause batching to fail
    /// and try again.
    SDF_API
    SdfNamespaceEditDetail::Result
    CanApply(const SdfBatchNamespaceEdit&,
             SdfNamespaceEditDetailVector* details = NULL) const;

    /// Performs a batch of namespace edits.  Returns \c true on success
    /// and \c false on failure.  On failure, no namespace edits will have
    /// occurred.
    SDF_API
    bool Apply(const SdfBatchNamespaceEdit&);

    /// @}
    /// \name Layer state
    /// @{

    /// Returns the state delegate used to manage this layer's authoring
    /// state.
    SDF_API
    SdfLayerStateDelegateBasePtr GetStateDelegate() const;

    /// Sets the state delegate used to manage this layer's authoring
    /// state. The 'dirty' state of this layer will be transferred to
    /// the new delegate.
    SDF_API
    void SetStateDelegate(const SdfLayerStateDelegateBaseRefPtr& delegate);

    /// Returns \c true if the layer is dirty, i.e. has changed from
    /// its persistent representation.
    SDF_API
    bool IsDirty() const;

    /// @}

    /// \name Time-sample API
    /// @{
    SDF_API
    std::set<double> ListAllTimeSamples() const;
    
    SDF_API
    std::set<double> 
    ListTimeSamplesForPath(const SdfPath& path) const;

    SDF_API
    bool GetBracketingTimeSamples(double time, double* tLower, double* tUpper);

    SDF_API
    size_t GetNumTimeSamplesForPath(const SdfPath& path) const;

    SDF_API
    bool GetBracketingTimeSamplesForPath(const SdfPath& path, 
                                         double time,
                                         double* tLower, double* tUpper);

    SDF_API
    bool QueryTimeSample(const SdfPath& path, double time, 
                         VtValue *value=NULL) const;
    SDF_API
    bool QueryTimeSample(const SdfPath& path, double time, 
                         SdfAbstractDataValue *value) const;

    template <class T>
    bool QueryTimeSample(const SdfPath& path, double time, 
                         T* data) const
    {
        if (!data) {
            return QueryTimeSample(path, time);
        }

        SdfAbstractDataTypedValue<T> outValue(data);
        const bool hasValue = QueryTimeSample(
            path, time, static_cast<SdfAbstractDataValue *>(&outValue));

        if (std::is_same<T, SdfValueBlock>::value) {
            return hasValue && outValue.isValueBlock;
        }

        return hasValue && (!outValue.isValueBlock);
    }

    SDF_API
    void SetTimeSample(const SdfPath& path, double time, 
                       const VtValue & value);
    SDF_API
    void SetTimeSample(const SdfPath& path, double time, 
                       const SdfAbstractDataConstValue& value);

    template <class T>
    void SetTimeSample(const SdfPath& path, double time, 
                       const T& value)
    {
        const SdfAbstractDataConstTypedValue<T> inValue(&value);
        const SdfAbstractDataConstValue& untypedInValue = inValue;
        return SetTimeSample(path, time, untypedInValue);
    }

    SDF_API
    void EraseTimeSample(const SdfPath& path, double time);

    /// @}

    // Debugging
    // @{

    SDF_API
    static void DumpLayerInfo();

    // Write this layer's SdfData to a file in a simple generic format.
    SDF_API
    bool WriteDataFile(const std::string &filename);

    // @}

protected:
    // Private constructor -- use New(), FindOrCreate(), etc.
    // Precondition: _layerRegistryMutex must be locked.
    SdfLayer(const SdfFileFormatConstPtr& fileFormat,
             const std::string &identifier,
             const std::string &realPath = std::string(),
             const ArAssetInfo& assetInfo = ArAssetInfo(),
             const FileFormatArguments &args = FileFormatArguments(),
             bool validateAuthoring = false);

private:
    // Create a new layer.
    // Precondition: _layerRegistryMutex must be locked.
    static SdfLayerRefPtr _CreateNew(
        SdfFileFormatConstPtr fileFormat,
        const std::string& identifier,
        const std::string& realPath,
        const ArAssetInfo& assetInfo = ArAssetInfo(),
        const FileFormatArguments& args = FileFormatArguments());

    static SdfLayerRefPtr _CreateNewWithFormat(
        const SdfFileFormatConstPtr &fileFormat,
        const std::string& identifier,
        const std::string& realPath,
        const ArAssetInfo& assetInfo = ArAssetInfo(),
        const FileFormatArguments& args = FileFormatArguments());

    static SdfLayerRefPtr _CreateAnonymousWithFormat(
        const SdfFileFormatConstPtr &fileFormat,
        const std::string& tag,
        const FileFormatArguments& args);

    // Finish initializing this layer (which may have succeeded or not)
    // and publish the results to other threads by unlocking the mutex.
    // Sets _initializationWasSuccessful and unlocks _initializationMutex.
    void _FinishInitialization(bool success);

    // Layers retrieved from the layer registry may still be in the
    // process of having their contents initialized.  Other threads
    // retrieving layers from the registry must wait until initialization
    // is complete, using this method.  See _initializationMutex.
    // Returns _initializationWasSuccessful.
    //
    // Callers *must* be holding an SdfLayerRefPtr to this layer to
    // ensure that it is not deleted out from under them, in
    // case initialization fails.  (This method cannot acquire the
    // reference itself internally without being susceptible to a race.)
    bool _WaitForInitializationAndCheckIfSuccessful();

    // Returns whether or not this menv layer should post change 
    // notification.  This simply returns (!_GetIsLoading())
    bool _ShouldNotify() const;

    // This function keeps track of the last state of IsDirty() before
    // updating it. It returns false if the last saved dirty state is the
    // same than the current state. It returns true if the state differs and
    // will update the 'last dirty state' to the current state. So, after
    // returning true, it would return false for subsequent calls until the
    // IsDirty() state would change again...
    bool _UpdateLastDirtinessState() const;

    // Returns a handle to the spec at the given path if it exists and matches
    // type T.
    template <class T>
    SdfHandle<T> _GetSpecAtPath(const SdfPath& path);

    // Returns true if a spec can be retrieved at the given path, false
    // otherwise. This function will return the canonicalized path to the
    // spec as well as the spec type.
    bool _CanGetSpecAtPath(const SdfPath& path, 
                           SdfPath* canonicalPath, SdfSpecType* specType) const;

    /// Initialize layer internals that are based on it's path.
    /// This includes the asset path and show path the layer to be loaded
    /// reflects at the point of initialization.
    void _InitializeFromIdentifier(
        const std::string &identifier,
        const std::string &realPath = std::string(),
        const std::string &fileVersion = std::string(),
        const ArAssetInfo& assetInfo = ArAssetInfo());

    // Helper for computing the necessary information to lookup a layer
    // in the registry or open the layer.
    struct _FindOrOpenLayerInfo;
    static bool _ComputeInfoToFindOrOpenLayer(
        const std::string& identifier,
        const SdfLayer::FileFormatArguments& args,
        _FindOrOpenLayerInfo* info,
        bool computeAssetInfo = false);

    // Open a layer, adding an entry to the registry and releasing
    // the registry lock.
    // Precondition: _layerRegistryMutex must be locked.
    template <class Lock>
    static SdfLayerRefPtr _OpenLayerAndUnlockRegistry(
        Lock &lock,
        const _FindOrOpenLayerInfo& info,
        bool metadataOnly);

    // Helper function to try to find the layer with \p identifier and
    // pre-resolved path \p resolvedPath in the registry.  Caller must hold
    // registry \p lock for reading.  If \p retryAsWriter is false, lock is
    // released upon return.  Otherwise the lock is released upon return if a
    // layer is found successfully.  If no layer is found then the lock is
    // upgraded to a writer lock upon return.  Note that this upgrade may not be
    // atomic, but this function ensures that if upon return there does not
    // exist a matching layer in the registry.
    template <class ScopedLock>
    static SdfLayerRefPtr
    _TryToFindLayer(const std::string &identifier,
                    const std::string &resolvedPath,
                    ScopedLock &lock, bool retryAsWriter);

    /// Returns true if the spec at the specified path has no effect on the 
    /// scene.
    /// 
    /// If ignoreChildren is true, this will ignore prim and property
    /// children of prim specs. Property specs are always considered to be 
    /// non-inert unless they have only required fields and 
    /// requiredFieldOnlyPropertiesareInert is set to false.
    bool _IsInert(const SdfPath &path, bool ignoreChildren, 
                  bool requiredFieldOnlyPropertiesAreInert = false) const;

    /// Return true if the entire subtree rooted at \a path does not affect the 
    /// scene. For this purpose, property specs that have only required fields 
    /// are considered inert.
    ///
    /// If this function returns true and \p inertSpecs is given, it will be 
    /// populated with the paths to all inert prim and property specs at and
    /// beneath \p path. These paths will be sorted so that child paths
    /// appear before their parent path.
    bool _IsInertSubtree(const SdfPath &path,
                         std::vector<SdfPath>* inertSpecs = nullptr);

    /// Cause \p spec to be removed if it does not affect the scene. This 
    /// removes any empty descendants before checking if \p spec itself is 
    /// inert. Property specs are always considered non-inert, so this will 
    /// remove them if they have only required fields (see 
    /// PropertySpec::HasOnlyRequiredFields). This also removes inert ancestors.
    void _RemoveIfInert(const SdfSpec& spec);

    /// Performs a depth first search of the namespace hierarchy, beginning at
    /// \p prim, removing prims that do not affect the scene. The return value 
    /// indicates whether the prim passed in is now inert as a result of this 
    /// call, and can itself be removed.
    bool _RemoveInertDFS(SdfPrimSpecHandle prim);

    /// If \p prim is inert (has no affect on the scene), removes prim, then 
    /// prunes inert parent prims back to the root.
    void _RemoveInertToRootmost(SdfPrimSpecHandle prim);

    /// Returns whether this layer is validating authoring operations.
    bool _ValidateAuthoring() const { return _validateAuthoring; }

    /// Returns the path used in the muted layers set.
    std::string _GetMutedPath() const;

    // If old and new asset path is given, rename all external prim
    // references referring to the old path.
    void _UpdateReferencePaths(const SdfPrimSpecHandle &parent,
                               const std::string &oldLayerPath,
                               const std::string &newLayerPath);

    // Set the clean state to the current state.
    void _MarkCurrentStateAsClean() const;

    // Return the field definition for \p fieldName if \p fieldName is a
    // required field for the spec type identified by \p path.
    inline SdfSchema::FieldDefinition const *
    _GetRequiredFieldDef(const SdfPath &path,
                         const TfToken &fieldName,
                         SdfSpecType specType = SdfSpecTypeUnknown) const;

    // Set a value.
    template <class T>
    void _SetValue(const TfToken& key, T value);

    // Get a value.
    template <class T>
    T _GetValue(const TfToken& key) const;

    enum _ReloadResult { _ReloadFailed, _ReloadSucceeded, _ReloadSkipped };
    _ReloadResult _Reload(bool force);

    // Reads contents of asset specified by \p identifier with resolved
    // path \p resolvedPath into this layer.
    bool _Read(const std::string& identifier, 
               const std::string& resolvedPath, 
               bool metadataOnly);
    
    // Saves this layer if it is dirty or the layer doesn't already exist
    // on disk. If \p force is true, the layer will be written out
    // regardless of those conditions.
    bool _Save(bool force) const;

    // A helper method used by Save and Export.
    // This method allows Save to specify the existing file format and Export
    // to use the format provided by the file extension in newFileName. If no
    // file format can be discovered from the file name, the existing file
    // format associated with the layer will be used in both cases. This allows
    // users to export and save to any file name, regardless of extension.
    bool _WriteToFile(const std::string& newFileName, 
                      const std::string& comment, 
                      SdfFileFormatConstPtr fileFormat = TfNullPtr,
                      const FileFormatArguments& args = FileFormatArguments())
                      const;

    // Swap contents of _data and data. This operation does not register
    // inverses or emit change notification.
    void _SwapData(SdfAbstractDataRefPtr &data);

    // Set _data to match data, calling other primitive setter methods
    // to provide fine-grained inverses and notification.
    void _SetData(const SdfAbstractDataPtr &data);

    // Returns const handle to _data.
    SdfAbstractDataConstPtr _GetData() const;

    // Inverse primitive for setting a single field.
    template <class T>
    void _PrimSetField(const SdfPath& path, 
                       const TfToken& fieldName,
                       const T& value,
                       const VtValue *oldValue = NULL,
                       bool useDelegate = true);

    // Inverse primitive for setting a single key in a dict-valued field.
    template <class T>
    void _PrimSetFieldDictValueByKey(const SdfPath& path,
                                     const TfToken& fieldName,
                                     const TfToken& keyPath,
                                     const T& value,
                                     const VtValue *oldValue = NULL,
                                     bool useDelegate = true);

    // Primitive for appending a child to the list of children.
    template <class T>
    void _PrimPushChild(const SdfPath& parentPath,
                        const TfToken& fieldName,
                        const T& value,
                        bool useDelegate = true);
    template <class T>
    void _PrimPopChild(const SdfPath& parentPath,
                       const TfToken& fieldName,
                       bool useDelegate = true);

    // Move all the fields at all paths at or below \a oldPath to be
    // at a corresponding location at or below \a newPath. This does
    // not update the children fields of the parents of these paths.
    bool _MoveSpec(const SdfPath &oldPath, const SdfPath &newPath);

    // Inverse primitive for moving a spec.
    void _PrimMoveSpec(const SdfPath &oldPath, const SdfPath &newPath,
                       bool useDelegate = true);

    // Create a new spec of type \p specType at \p path.
    // Returns true if spec was successfully created, false otherwise.
    bool _CreateSpec(const SdfPath& path, SdfSpecType specType, bool inert);

    // Delete all the fields at or below the specified path. This does
    // not update the children field of the parent of \a path.
    bool _DeleteSpec(const SdfPath &path);

    // Inverse primitive for deleting a spec.
    void _PrimCreateSpec(const SdfPath &path, SdfSpecType specType, bool inert,
                         bool useDelegate = true);

    // Inverse primitive for deleting a spec.
    void _PrimDeleteSpec(const SdfPath &path, bool inert, 
                         bool useDelegate = true);

    // Inverse primitive for setting time samples.
    template <class T>
    void _PrimSetTimeSample(const SdfPath& path, double time,
                            const T& value,
                            bool useDelegate = true);

    // Helper method for Traverse. Visits the children of \a path using the
    // specified \a ChildPolicy.
    template <typename ChildPolicy>
    void _TraverseChildren(const SdfPath &path, const TraversalFunction &func);

private:
    SdfLayerHandle _self;

    // File format and arguments for this layer.
    SdfFileFormatConstPtr _fileFormat;
    FileFormatArguments _fileFormatArgs;

    // Registry of Sdf Identities
    mutable Sdf_IdentityRegistry _idRegistry;

    // The underlying SdfData which stores all the data in the layer.
    SdfAbstractDataRefPtr _data;

    // The state delegate for this layer.
    SdfLayerStateDelegateBaseRefPtr _stateDelegate;

    // Atomic variable protecting layer initialization -- the interval between
    // adding a layer to the layer registry and finishing the process of
    // initializing its contents, at which point we can truly publish the layer
    // for consumption by concurrent threads. We add the layer to the registry
    // before initialization completes so that other threads can discover and
    // wait for it to finish initializing.
    std::atomic<bool> _initializationComplete;

    // This is an optional<bool> that is only set once initialization
    // is complete, while _initializationMutex is locked.  If the
    // optional<bool> is unset, initialization is still underway.
    boost::optional<bool> _initializationWasSuccessful;

    // remembers the last 'IsDirty' state.
    mutable bool _lastDirtyState;

    // Asset information for this layer.
    std::unique_ptr<Sdf_AssetInfo> _assetInfo;

    // Modification timestamp of the backing file asset when last read.
    mutable VtValue _assetModificationTime;

    // Mutable revision number for cache invalidation.
    mutable size_t _mutedLayersRevisionCache;

    // Cache of whether or not this layer is muted.  Only valid if
    // _mutedLayersRevisionCache is up-to-date with the global revision number.
    mutable bool _isMutedCache;

    // Layer permission bits.
    bool _permissionToEdit;
    bool _permissionToSave;

    // Whether layer edits are validated.
    bool _validateAuthoring;

    // Allow access to _ValidateAuthoring() and _IsInert().
    friend class SdfSpec;
    friend class SdfPropertySpec;
    friend class SdfAttributeSpec;

    friend class Sdf_ChangeManager;

    // Allow access to _CreateSpec and _DeleteSpec and _MoveSpec
    template <class ChildPolicy> friend class Sdf_ChildrenUtils;

    // Give the file format access to our data.  Limit breaking encapsulation
    // to the base SdFileFormat class so we don't have to friend every
    // implementation here.
    friend class SdfFileFormat;

    // Give layer state delegates access to our data as well as to
    // the various _Prim functions.
    friend class SdfLayerStateDelegateBase;
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif // PXR_USD_SDF_LAYER_H
