//
// Copyright 2021 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//
#ifndef PXR_IMAGING_HD_MTLX_HDMTLX_H
#define PXR_IMAGING_HD_MTLX_HDMTLX_H

#include "pxr/pxr.h"
#include "pxr/base/tf/token.h"
#include "pxr/imaging/hdMtlx/api.h"
#include <memory>
#include <set>
#include <unordered_map>

#include <MaterialXCore/Library.h>

MATERIALX_NAMESPACE_BEGIN
    class FileSearchPath;
    using DocumentPtr = std::shared_ptr<class Document>;
    using NodeDefPtr = std::shared_ptr<class NodeDef>;
MATERIALX_NAMESPACE_END

PXR_NAMESPACE_OPEN_SCOPE

class SdfPath;
class VtValue;
struct HdMaterialNetwork2;
struct HdMaterialNode2;
class HdMaterialNetworkInterface;

/// Return the MaterialX search paths. In order, this includes:
/// - Paths set in the environment variable 'PXR_MTLX_PLUGIN_SEARCH_PATHS'
/// - Paths set in the environment variable 'PXR_MTLX_STDLIB_SEARCH_PATHS'
/// - Path to the MaterialX standard library discovered at build time.
HDMTLX_API
const MaterialX::FileSearchPath&
HdMtlxSearchPaths();

/// Return a MaterialX document with the stdlibraries loaded using the above 
/// search paths.
HDMTLX_API
const MaterialX::DocumentPtr&
HdMtlxStdLibraries();

/// Converts the HdParameterValue to a string MaterialX can understand
HDMTLX_API
std::string
HdMtlxConvertToString(VtValue const& hdParameterValue);

// Storing MaterialX-Hydra texture and primvar information
struct HdMtlxTexturePrimvarData {
    HdMtlxTexturePrimvarData() = default;
    using TextureMap = std::map<std::string, std::set<std::string>>;
    TextureMap mxHdTextureMap; // Mx-Hd texture name mapping
    std::set<SdfPath> hdTextureNodes; // Paths to HdTexture Nodes
    std::set<SdfPath> hdPrimvarNodes; // Paths to HdPrimvar nodes
};

HDMTLX_API
std::string
HdMtlxCreateNameFromPath(SdfPath const& path);

/// Custom nodes may have their nodeDefs in a separate mtlx file, this function
/// additional looks to the ImplementationURI stored on the sdr to find this 
/// mtlx file and nodeDef. 
/// This method is preferred over mxDoc->getNodeDef() to catch this custom 
/// node case.
HDMTLX_API
MaterialX::NodeDefPtr
HdMtlxGetNodeDef(
    TfToken const& hdNodeType,
    MaterialX::DocumentPtr const& mxDoc=nullptr);

/// NodeDef names may change between MaterialX versions, this function returns
/// the nodeDef name appropriate for the version of MaterialX being used.
HDMTLX_API
std::string
HdMtlxGetNodeDefName(std::string const& prevMxNodeDefName);

/// Returns true if the given nodeDef's implementation is a nodegraph that
/// contains a 'geompropvalue' or 'geompropvalueuniform' node, i.e. the node
/// reads a primvar (for example UsdPrimvarReader). Native 'geompropvalue'
/// nodes are not detected here since their implementation is source code
/// rather than a nodegraph.
HDMTLX_API
bool
HdMtlxIsPrimvarReaderNodeDef(MaterialX::NodeDefPtr const& mxNodeDef);

/// Returns the node input that carries the primvar name for a primvar-reading
/// node. For wrapper nodes (such as UsdPrimvarReader) this is discovered from
/// the 'interfacename' on the wrapped geompropvalue's 'geomprop' input (for
/// example "varname"). For native geompropvalue nodes, or any node without such
/// a binding, this returns "geomprop".
HDMTLX_API
TfToken
HdMtlxGetPrimvarNameInputName(MaterialX::NodeDefPtr const& mxNodeDef);

/// Returns the node input that carries the fallback value for a primvar-reading
/// node. For wrapper nodes (such as UsdPrimvarReader) this is discovered from
/// the 'interfacename' on the wrapped geompropvalue's 'default' input (for
/// example "fallback"). For native geompropvalue nodes, or any node without such
/// a binding, this returns "default".
HDMTLX_API
TfToken
HdMtlxGetPrimvarDefaultInputName(MaterialX::NodeDefPtr const& mxNodeDef);

/// Get the terminal name used in the MaterialX document based on the Hydra
/// terminal node.
HDMTLX_API
std::string
HdMtlxGetMxTerminalName(
    HdMaterialNetworkInterface *netInterface,
    TfToken const& hdTerminalNodeName);

/// Get the terminal name used in the MaterialX document based on the MaterialX
/// terminal node type.
HDMTLX_API
std::string
HdMtlxGetMxTerminalName(std::string const& mxTerminalType);

/// Creates and returns a MaterialX Document from the given HdMaterialNetwork2 
/// Collecting the hdTextureNodes and hdPrimvarNodes as the network is 
/// traversed as well as the Texture name mapping between MaterialX and Hydra.
HDMTLX_API
MaterialX::DocumentPtr
HdMtlxCreateMtlxDocumentFromHdNetwork(
    HdMaterialNetwork2 const& hdNetwork,
    HdMaterialNode2 const& hdMaterialXNode,
    SdfPath const& hdMaterialXNodePath,
    SdfPath const& materialPath,
    MaterialX::DocumentPtr const& libraries,
    HdMtlxTexturePrimvarData *mxHdData = nullptr);

/// Implementation that uses the material network interface.
HDMTLX_API
MaterialX::DocumentPtr
HdMtlxCreateMtlxDocumentFromHdMaterialNetworkInterface(
    HdMaterialNetworkInterface *netInterface,
    TfToken const& terminalNodeName,
    TfTokenVector const& terminalNodeConnectionNames,
    MaterialX::DocumentPtr const& libraries,
    HdMtlxTexturePrimvarData *mxHdData = nullptr);

PXR_NAMESPACE_CLOSE_SCOPE

#endif
