//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//

#ifndef MATERIALX_MDLSHADERGENERATOR_H
#define MATERIALX_MDLSHADERGENERATOR_H

/// @file
/// MDL shading language generator

#include <MaterialXGenMdl/Export.h>

#include <MaterialXGenShader/ShaderGenerator.h>

MATERIALX_NAMESPACE_BEGIN

/// Generator context data class to pass strings.
class MX_GENMDL_API GenMdlOptions : public GenUserData
{
  public:
    /// MDL Versions supported by the Code Generator
    enum class MdlVersion
    {
        MDL_1_6,
        MDL_1_7,
        MDL_1_8,
        MDL_1_9,
        MDL_1_10,
        MDL_LATEST = MDL_1_10
    };

    /// Create MDL code generator options with default values.
    GenMdlOptions() :
        targetVersion(MdlVersion::MDL_LATEST) { }

    /// Unique identifier for the MDL options on the GenContext object.
    static const string GEN_CONTEXT_USER_DATA_KEY;

    /// The MDL version number the generated module will have.
    /// Allows to generate MDL for older applications by limiting support according
    /// to the corresponding specification. By default this option is MDL_LATEST.
    MdlVersion targetVersion;
};

/// Shared pointer to GenMdlOptions
using GenMdlOptionsPtr = shared_ptr<class GenMdlOptions>;

/// Shared pointer to an MdlShaderGenerator
using MdlShaderGeneratorPtr = shared_ptr<class MdlShaderGenerator>;

/// @class MdlShaderGenerator
/// Shader generator for MDL (Material Definition Language).
class MX_GENMDL_API MdlShaderGenerator : public ShaderGenerator
{
  public:
    /// Constructor.
    MdlShaderGenerator(TypeSystemPtr typeSystem);

    /// Creator function.
    /// If a TypeSystem is not provided it will be created internally.
    /// Optionally pass in an externally created TypeSystem here, 
    /// if you want to keep type descriptions alive after the lifetime
    /// of the shader generator. 
    static ShaderGeneratorPtr create(TypeSystemPtr typeSystem = nullptr)
    {
        return std::make_shared<MdlShaderGenerator>(typeSystem ? typeSystem : TypeSystem::create());
    }

    /// Return a unique identifier for the target this generator is for
    const string& getTarget() const override { return TARGET; }

    /// Apply the default GenOptions for MDL shader generation.
    void applyDefaultOptions(GenOptions& options) const override;

    /// Generate a shader starting from the given element, translating
    /// the element and all dependencies upstream into shader code.
    ShaderPtr generate(const string& name, ElementPtr element, GenContext& context) const override;

    /// Create the shader node implementation for a NodeGraph implementation.
    ShaderNodeImplPtr createShaderNodeImplForNodeGraph(const NodeGraph& nodegraph) const override;

    /// Create the shader node implementation for an mplementation implementation.
    ShaderNodeImplPtr createShaderNodeImplForImplementation(const Implementation& implementation) const override;

    /// Return the result of an upstream connection or value for an input.
    string getUpstreamResult(const ShaderInput* input, GenContext& context) const override;

    /// Unique identifier for this generator target
    static const string TARGET;

    /// Map of code snippets for geomprops in MDL.
    static const std::unordered_map<string, string> GEOMPROP_DEFINITIONS;

    /// Get the selected MDL target language version number from the context option.
    /// If not set, the latest version supported by GenMdl is returned.
    GenMdlOptions::MdlVersion getMdlVersion(GenContext& context) const;

    /// Add the MDL file header containing the version number of the generated module.
    void emitMdlVersionNumber(GenContext& context, ShaderStage& stage) const;

    /// Add the version number suffix appended to MDL modules that use versions.
    void emitMdlVersionFilenameSuffix(GenContext& context, ShaderStage& stage) const;

    /// Get the version number suffix appended to MDL modules that use versions.
    const string& getMdlVersionFilenameSuffix(GenContext& context) const;

    /// Emit type definitions for all data types that need them.
    void emitTypeDefinitions(GenContext& context, ShaderStage& stage) const override;

  protected:
    // Create and initialize a new MDL shader for shader generation.
    ShaderPtr createShader(const string& name, ElementPtr element, GenContext& context) const;

    // Emit a block of shader inputs.
    void emitShaderInputs(const VariableBlock& inputs, ShaderStage& stage) const;
};

namespace MDL
{

// Identifiers for MDL variable blocks
extern MX_GENMDL_API const string INPUTS;
extern MX_GENMDL_API const string OUTPUTS;

} // namespace MDL

MATERIALX_NAMESPACE_END

#endif
