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

#ifndef PXR_USD_PLUGIN_SDR_OSL_OSL_PARSER_H
#define PXR_USD_PLUGIN_SDR_OSL_OSL_PARSER_H

/// \file sdrOsl/oslParser.h

#include "pxr/pxr.h"
#include "pxr/usd/plugin/sdrOsl/api.h"
#include "pxr/base/vt/value.h"
#include "pxr/usd/sdr/parserPlugin.h"
#include "pxr/usd/sdr/declare.h"
#include <OSL/oslquery.h>

PXR_NAMESPACE_OPEN_SCOPE

// Forward declarations
class SdrShaderNode;
struct SdrShaderNodeDiscoveryResult;

/// \class SdrOslParserPlugin
///
/// Parses OSL nodes. For more information on parser plugins, see the
/// documentation for `SdrParserPlugin`.
///
/// \section metadata_schema Metadata Schema
/// The following metadata is pulled out of the shader and available on the
/// destination as outlined below.
///
/// \subsection node_metadata Node Metadata
///
/// OSL Metadata Key      | Destination
/// --------------------- | ------------
/// category              | GetCategory()
/// departments           | GetDepartments()
/// help                  | GetHelp()
/// label                 | GetLabel()
/// \ref pvars "primvars" | GetPrimvars(), GetAdditionalPrimvarProperties()
/// _everything else_     | GetMetadata()
///
///
/// \subsection property_metadata Property Metadata
///
/// OSL Metadata Key    | Destination
/// ------------------- | ------------
/// connectable         | IsConnectable()
/// sdrDefinitionName   | renames parameter, sends original osl param name to 
///                     | SdrShaderProperty::GetImplementationName()
/// page                | GetPage()
/// help                | GetHelp()
/// label               | GetLabel()
/// isDynamicArray      | IsDynamicArray()
/// \ref opts "options" | GetOptions()
/// vstructMemberName   | GetVStructMemberName()
/// vstructMemberOf     | GetVStructMemberOf()
/// _everything else_   | GetMetadata()
///
///
/// \subsection opts Options Format
/// Multiple options are separated with the pipe '|' character; options can be
/// specified in one of two ways:
/// <ul>
///     <li>
///         key1|key2|key3|...
///     </li>
///     <li>
///         key1:value1|key2:value2|...
///     </li>
/// </ul>
///
/// In the first case, where the values are not specified, the value of the
/// option will be available on the node as an empty string.
///
///
/// \subsection pvars Primvar Format
/// Multiple primvars are separated with the pipe '|' character; primvars can be
/// specified in one of two ways:
/// <ul>
///     <li>
///         A string _not_ prefixed with a '$'; this indicates a normal primvar
///     </li>
///     <li>
///         A string prefixed with '$'; the part after the '$' indicates the
///         name of a string-typed input on the node whose value contains
///         additional primvars
///     </li>
/// </ul>
///
class SdrOslParserPlugin : public SdrParserPlugin
{
public:
    typedef OSL::OSLQuery::Parameter OslParameter;

    SDROSL_API
    SdrOslParserPlugin();
    SDROSL_API
    ~SdrOslParserPlugin();

    SDROSL_API
    SdrShaderNodeUniquePtr ParseShaderNode(
        const SdrShaderNodeDiscoveryResult& discoveryResult) override;

    SDROSL_API
    const SdrTokenVec& GetDiscoveryTypes() const override;

    SDROSL_API
    const TfToken& GetShadingSystem() const override;

private:
    // Sets the shader's sdr metadata context.
    // - If the metadata already has a "context" item, this does nothing.
    // - If the metadata has a "schemaBase" item, sets "context" mapped
    //   from the "schemaBase" item value.
    // - Otherwise, does nothing
    void _setSdrContext(SdrTokenMap& metadata) const;

    // Gets a vector of properties that are present on the specified OSL
    // query object
    SdrShaderPropertyUniquePtrVec _getNodeProperties(
        const OSL::OSLQuery &query,
        const SdrShaderNodeDiscoveryResult& discoveryResult,
        const std::string& fallbackprefix) const;

    // Gets all metadata for the node that's present on the specified OSL
    // query object
    SdrTokenMap _getNodeMetadata(const OSL::OSLQuery &query,
                                 const SdrTokenMap &baseMetadata) const;

    // Gets all metadata for the specified OSL parameter
    SdrTokenMap _getPropertyMetadata(const OslParameter* param,
        const SdrShaderNodeDiscoveryResult& discoveryResult) const;

    // Injects any metadata that is generated by the parser. This type of
    // metadata provides additional hints to the property.
    void _injectParserMetadata(SdrTokenMap& metadata,
        const TfToken& typeName) const;

    // Gets the specified parameter's value as a string. Can handle string,
    // float, and integer values (if not one of these types, an empty string
    // is returned).
    std::string _getParamAsString(const OslParameter& param) const;

    // Gets a common type + array size (if array) from the OSL parameter
    std::tuple<TfToken, size_t> _getTypeName(
        const OslParameter* param,
        const SdrTokenMap& metadata) const;

    // Gets the default value of the specified param.
    VtValue _getDefaultValue(
        const SdrOslParserPlugin::OslParameter& param,
        const std::string& oslType,
        size_t arraySize,
        const SdrTokenMap& metadata
    ) const;
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif // PXR_USD_PLUGIN_SDR_OSL_OSL_PARSER_H
