/*
Copyright 2024 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/
#pragma once

#include "api.h"
#include <pxr/base/vt/dictionary.h>
#include <pxr/pxr.h>
#include <pxr/usd/pcp/dynamicFileFormatInterface.h>
#include <pxr/usd/sdf/abstractData.h>
#include <pxr/usd/sdf/fileFormat.h>

namespace adobe::usd::sbsar {
struct SBSAROptions
{
    PXR_NS::VtDictionary sbsarParameters;
    std::uint32_t depth = 0;
    bool writeMaterialX = false;
    bool writeASM = true;
    bool writeUsdPreviewSurface = true;
};
}

// To avoid trouble when registering class we use pixar name space.
PXR_NAMESPACE_OPEN_SCOPE
#define SBSAR_FILE_FORMAT_TOKENS                                                                   \
    ((Id, "sbsar"))((Version, "1.0"))((Target, "usd"))((Extension, "sbsar"))
TF_DECLARE_PUBLIC_TOKENS(SBSARFileFormatTokens, SBSAR_FILE_FORMAT_TOKENS);

TF_DECLARE_WEAK_AND_REF_PTRS(SBSARFileFormat);

//! \ingroup usdsbsar
//! \brief Main class of the plugin usd sbsar.
//! This class allow to usd to read sbsar , create layer and generate material
//! or domelight with custom assets paths that should be solved by
//! SBSARPackageResolver.
//!
//! In the USD file, a sbsar should be referenced as a sublayer
//! From this reference, the plugin will generat a first layer like this (in
//! Read()):
//!
//! def Material "{Graph_Name}" ( payload=xxxx.sbsar)
//!{
//!   ...
//!}
//! The payload will be resolved and the function read recalled by usd.
//! That will create another layer with another data that contains the assets
//! paths.
class SBSARFileFormat
  : public SdfFileFormat
  , public PcpDynamicFileFormatInterface
{
  public:
    // SdfFileFormat API.
    USDSBSAR_API virtual bool IsPackage() const override;
    USDSBSAR_API virtual std::string GetPackageRootLayerPath(
      const std::string& resolvedPath) const override;

    USDSBSAR_API bool CanRead(const std::string& file) const override;

    //! \brief Read an sbsar path and generate the layer.
    //! The layer can containt 2 parameters as file argument:
    //!     - depth parameter to control witch layer the Read() method should
    //!     create.
    //!         This parameters is generated by some sub fonction of Read()
    //!     - SBSAR parameter generate by ComposeFieldsForFileFormatArguments,
    //!     used to generate asset path.
    //! This method can be called several time to generat all layer.
    //! \param layer            Layer to generate.
    //! \param resolvedPath     SBSAR path without file argument.
    //! \param metadataOnly     Not used.
    //! \return True if the layer was correctly create.
    USDSBSAR_API bool Read(SdfLayer* layer,
                           const std::string& resolvedPath,
                           bool metadataOnly) const override;

    // PcpDynamicFileFormatInterface API
    //! \brief Read and link all input of the .sbsar stored in the currect
    //! context as attribute and create a dictionary of sbsar parameters. This
    //! method allow usd to know which attributes to track. And after each
    //! update of one of this attribut, the Read() method will called to
    //! regenerate all layer, and all asset path
    //! \param assetPath    SBSAR Asset path, this path is raw and can contains
    //! file argument at the end of the path.
    //! \param context      Current context
    //! \param args         Output of the method, dictionary of sbsar
    //! parameters.
    //! \param dependencyContextData Not used.
    USDSBSAR_API void ComposeFieldsForFileFormatArguments(
      const std::string& assetPath,
      const PXR_NS::PcpDynamicFileFormatContext& context,
      FileFormatArguments* args,
      VtValue* dependencyContextData) const override;

    USDSBSAR_API bool CanAttributeDefaultValueChangeAffectFileFormatArguments(
      const TfToken& attributeName,
      const VtValue& oldValue,
      const VtValue& newValue,
      const VtValue& dependencyContextData) const override;

    USDSBSAR_API bool CanFieldChangeAffectFileFormatArguments(
      const TfToken& field,
      const VtValue& oldValue,
      const VtValue& newValue,
      const VtValue& dependencyContextData) const override;

    //! \brief Create the USD layer and store it in the given layer.
    //! \param sdfDataPtr       Sdf data to store the layer in.
    //! \param resolvedPath     SBSAR path without file argument.
    //! \param depth            Current depth
    //! \param sbsarParameters  Sbsar parameters used to generate texture asset path.
    //! \return True if the layer has been created successfully
    static USDSBSAR_API bool CreateLayerData(const SdfAbstractDataRefPtr& sdfDataPtr,
                                             const std::string& resolvedPath,
                                             const adobe::usd::sbsar::SBSAROptions& sbsarData);
    USDSBSAR_API bool WriteToFile(const SdfLayer& layer,
                                  const std::string& filePath,
                                  const std::string& comment,
                                  const FileFormatArguments& args) const override;
    USDSBSAR_API bool WriteToString(const SdfLayer& layer,
                                    std::string* str,
                                    const std::string& comment) const override;

    USDSBSAR_API bool WriteToStream(const SdfSpecHandle& spec,
                                    std::ostream& out,
                                    size_t indent) const override;

  protected:
    SDF_FILE_FORMAT_FACTORY_ACCESS;
    virtual ~SBSARFileFormat();
    SBSARFileFormat();
};
PXR_NAMESPACE_CLOSE_SCOPE
