//
// Copyright 2025 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//

#ifndef PXR_USD_SDR_SHADER_PROPERTY_METADATA_H
#define PXR_USD_SDR_SHADER_PROPERTY_METADATA_H

#include "pxr/pxr.h"
#include "pxr/base/tf/staticTokens.h"
#include "pxr/base/vt/dictionary.h"
#include "pxr/usd/sdr/api.h"
#include "pxr/usd/sdr/declare.h"

PXR_NAMESPACE_OPEN_SCOPE

// Note: Metadata keys that are generated by parsers should start with
// "__SDR__" to reduce the risk of collision with metadata actually in the
// shader.
#define SDR_PROPERTY_METADATA_TOKENS                 \
    ((Label, "label"))                               \
    ((Help, "help"))                                 \
    ((Page, "page"))                                 \
    ((RenderType, "renderType"))                     \
    ((Role, "role"))                                 \
    ((Widget, "widget"))                             \
    ((Hints, "hints"))                               \
    ((Options, "options"))                           \
    ((IsDynamicArray, "isDynamicArray"))             \
    ((TupleSize, "tupleSize"))                       \
    ((Connectable, "connectable"))                   \
    ((Tag, "tag"))                                   \
    ((ShownIf, "shownIf"))                           \
    ((ValidConnectionTypes, "validConnectionTypes")) \
    ((VstructMemberOf, "vstructMemberOf"))           \
    ((VstructMemberName, "vstructMemberName"))       \
    ((VstructConditionalExpr, "vstructConditionalExpr"))\
    ((IsAssetIdentifier, "__SDR__isAssetIdentifier"))\
    ((ImplementationName, "__SDR__implementationName"))\
    ((SdrUsdDefinitionType, "sdrUsdDefinitionType"))\
    ((DefaultInput, "__SDR__defaultinput"))          \
    ((Target, "__SDR__target"))                      \
    ((Colorspace, "__SDR__colorspace"))


// The following tokens are valid values for the metadata "role"
#define SDR_PROPERTY_ROLE_TOKENS \
    ((None, "none"))

#define SDR_PROPERTY_TOKENS \
    ((PageDelimiter, ":"))

/// SdrPropertyMetadata contains named metadata keys that should
/// have consistent types and semantics across all clients
///
/// \deprecated
/// Certain fields in SdrPropertyMetadata are deprecated:
/// - SdrPropertyMetadata->Hints
/// - SdrPropertyMetadata->Options
/// - SdrPropertyMetadata->Tag
/// - SdrPropertyMetadata->Target
TF_DECLARE_PUBLIC_TOKENS(SdrPropertyMetadata, SDR_API, 
                         SDR_PROPERTY_METADATA_TOKENS);
TF_DECLARE_PUBLIC_TOKENS(SdrPropertyRole, SDR_API,
                         SDR_PROPERTY_ROLE_TOKENS);
TF_DECLARE_PUBLIC_TOKENS(SdrPropertyTokens, SDR_API, SDR_PROPERTY_TOKENS);

/// SdrShaderPropertyMetadata contains generic and named metadata for
/// SdrShaderProperty.
///
/// Named metadata are key-value items that are standardized and
/// documented for interchange. Named metadata items are defined to
/// have certain types that expect certain encodings.
/// The Has/Set/Get methods for named key-value items provide views
/// into the requested metadata item's unpacked VtValues.
///
/// NOTE: This metadata is retrievable from SdrShaderProperty with
/// SdrShaderProperty::GetMetadataObject
class SdrShaderPropertyMetadata
{
public:
    /// Ingest metadata from the legacy SdrTokenMap structure.
    ///
    /// For named metadata items, conversions to richer types from
    /// the SdrTokenMap values are attempted.
    SDR_API
    SdrShaderPropertyMetadata(const SdrTokenMap& legacyMetadata);

    /// Ingest metadata from an initializer list for the legacy SdrTokenMap.
    ///
    /// This helps provide automatic, implicit backwards compatibility for
    /// users that construct SdrTokenMap metadata via an initializer list
    /// in the SdrShaderProperty constructor.
    SdrShaderPropertyMetadata(
        const std::initializer_list<std::pair<TfToken, std::string>>& init
    ): SdrShaderPropertyMetadata(_LegacyCtorFromInitializer(init)) {}

    explicit SdrShaderPropertyMetadata(const VtDictionary& items)
        : _items(items) {}

    explicit SdrShaderPropertyMetadata(VtDictionary&& items)
        : _items(std::move(items)) {}

    SdrShaderPropertyMetadata() {}

    /// Returns whether this metadata contains an item with the given key.
    SDR_API
    bool HasItem(const TfToken& key) const;

    /// Set a key-value item for this metadata.
    ///
    /// If the key-value item already exists, it will be overwritten by the
    /// provided arguments.
    ///
    /// Issues a TF_CODING_ERROR if the value is empty or if the key is in
    /// "named metadata" and the value's contained type does not match the type
    /// registered by the named metadata item.
    SDR_API
    void SetItem(const TfToken& key, const VtValue& value);

    /// \overload
    template <typename T>
    void SetItem(const TfToken& key, const T& value) {
        SetItem(key, VtValue(value));
    }

    /// Get the VtValue for the given key.
    ///
    /// If the value is found in the legacy SdrTokenMap metadata, the returned
    /// value will be a VtValue holding std::string.
    ///
    /// If a key-value item doesn't exist for the given key in this metadata,
    /// returns an empty VtValue.
    SDR_API
    VtValue GetItemValue(const TfToken& key) const;

    /// Convenience to get an item value as T.
    ///
    /// If any value fails conversion or doesn't exist in the metadata, a
    /// default constructed value for T is returned.
    template <typename T>
    T GetItemValueAs(const TfToken& key) const {
        const VtValue v = GetItemValue(key);
        if (!v.IsEmpty()) {
            const VtValue converted = VtValue::Cast<T>(v);
            if (!converted.IsEmpty()) {
                return converted.UncheckedGet<T>();
            }
        }

        return {};
    }

    /// Clear the metadata item for the given key if it exists.
    SDR_API
    void ClearItem(const TfToken& key);

    /// Get all key-value items.
    const VtDictionary& GetItems() const & { return _items; }

    /// Get all key-value items by-value.
    VtDictionary GetItems() && { return std::move(_items); }

    /// Named metadata
    ///
    /// {@
    
    SDR_API
    bool HasLabel() const;
    SDR_API
    TfToken GetLabel() const;
    SDR_API
    void SetLabel(const TfToken& v);
    SDR_API
    void ClearLabel();

    SDR_API
    bool HasHelp() const;
    SDR_API
    std::string GetHelp() const;
    SDR_API
    void SetHelp(const std::string& v);
    SDR_API
    void ClearHelp();

    SDR_API
    bool HasPage() const;
    SDR_API
    TfToken GetPage() const;
    SDR_API
    void SetPage(const TfToken& v);
    SDR_API
    void ClearPage();

    SDR_API
    bool HasRenderType() const;
    SDR_API
    std::string GetRenderType() const;
    SDR_API
    void SetRenderType(const std::string& v);
    SDR_API
    void ClearRenderType();

    SDR_API
    bool HasRole() const;
    SDR_API
    std::string GetRole() const;
    SDR_API
    void SetRole(const std::string& v);
    SDR_API
    void ClearRole();

    SDR_API
    bool HasWidget() const;
    SDR_API
    TfToken GetWidget() const;
    SDR_API
    void SetWidget(const TfToken& v);
    SDR_API
    void ClearWidget();

    SDR_API
    bool HasIsDynamicArray() const;
    SDR_API
    bool GetIsDynamicArray() const;
    SDR_API
    void SetIsDynamicArray(const bool& v);
    SDR_API
    void ClearIsDynamicArray();

    SDR_API
    bool HasTupleSize() const;
    SDR_API
    int GetTupleSize() const;
    SDR_API
    void SetTupleSize(const int& v);
    SDR_API
    void ClearTupleSize();

    SDR_API
    bool HasConnectable() const;
    SDR_API
    bool GetConnectable() const;
    SDR_API
    void SetConnectable(const bool& v);
    SDR_API
    void ClearConnectable();

    SDR_API
    bool HasShownIf() const;
    SDR_API
    std::string GetShownIf() const;
    SDR_API
    void SetShownIf(const std::string& v);
    SDR_API
    void ClearShownIf();

    SDR_API
    bool HasValidConnectionTypes() const;
    SDR_API
    SdrTokenVec GetValidConnectionTypes() const;
    SDR_API
    void SetValidConnectionTypes(const SdrTokenVec& v);
    SDR_API
    void ClearValidConnectionTypes();

    SDR_API
    bool HasIsAssetIdentifier() const;
    SDR_API
    bool GetIsAssetIdentifier() const;
    SDR_API
    void SetIsAssetIdentifier(const bool& v);
    SDR_API
    void ClearIsAssetIdentifier();

    SDR_API
    bool HasImplementationName() const;
    SDR_API
    std::string GetImplementationName() const;
    SDR_API
    void SetImplementationName(const std::string& v);
    SDR_API
    void ClearImplementationName();

    SDR_API
    bool HasSdrUsdDefinitionType() const;
    SDR_API
    TfToken GetSdrUsdDefinitionType() const;
    SDR_API
    void SetSdrUsdDefinitionType(const TfToken& v);
    SDR_API
    void ClearSdrUsdDefinitionType();

    SDR_API
    bool HasDefaultInput() const;
    SDR_API
    bool GetDefaultInput() const;
    SDR_API
    void SetDefaultInput(const bool& v);
    SDR_API
    void ClearDefaultInput();

    SDR_API
    bool HasColorspace() const;
    SDR_API
    TfToken GetColorspace() const;
    SDR_API
    void SetColorspace(const TfToken& v);
    SDR_API
    void ClearColorspace();

    /// @}

private:
    friend class SdrShaderProperty;

    // Deprecated function for legacy metadata support.
    //
    // Unnamed metadata with non-string values are not returned in
    // the legacy map. 
    SdrTokenMap _EncodeLegacyMetadata() const;

    static
    SdrTokenMap _LegacyCtorFromInitializer(
        std::initializer_list<std::pair<TfToken, std::string>> f)
    {
        return SdrTokenMap(f.begin(), f.end());
    }

    VtDictionary _items;
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif // PXR_USD_SDR_SHADER_PROPERTY_METADATA_H
