//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

#ifndef smtk_session_aeva_detail_OperationManagersAdaptor_h
#define smtk_session_aeva_detail_OperationManagersAdaptor_h

#include "smtk/resource/Metadata.h"

#include "smtk/common/Managers.h"

namespace smtk
{
namespace session
{
namespace aeva
{
namespace detail
{

template<typename Class>
struct OperationManagersAdaptor
{
  // If no setManagers() method exists, do nothing.
  template<typename Test>
  static void test(Test&, const smtk::common::Managers::Ptr&, ...) // NOLINT
  {
  }

  // Operation::setManagers exists, so invoke it.
  template<typename Test>
  static void test(Test& obj, // NOLINT(readability-named-parameter)
    const smtk::common::Managers::Ptr& managers,
    decltype(&Test::setManagers) /* unused */) // NOLINT(readability-named-parameter)
  {
    obj.setManagers(managers);
  }

  // Make construction of the adaptor invoke one of the methods above.
  OperationManagersAdaptor(Class& obj, const smtk::common::Managers::Ptr& managers)
  {
    OperationManagersAdaptor::test<Class>(obj, managers, nullptr);
  }
};

template<typename MetadataType, typename ResourceType>
struct RegisterResourceMetadata
{
  using ReadFunctor = std::function<smtk::resource::Resource::Ptr(const std::string&,
    const std::shared_ptr<smtk::common::Managers>&)>;
  using WriteFunctor = std::function<bool(const smtk::resource::Resource::Ptr&,
    const std::shared_ptr<smtk::common::Managers>&)>;

  template<typename Test>
  typename std::enable_if<!std::is_same<decltype(Test::read), ReadFunctor>::value, void>::type
    registerResource /* NOLINT */ (const smtk::resource::Manager::Ptr& resourceManager,
      ReadFunctor read,   // NOLINT(performance-unnecessary-value-param)
      WriteFunctor write) // NOLINT(performance-unnecessary-value-param)
  {
    auto adaptedRead = [read](const std::string& filename) -> smtk::resource::Resource::Ptr {
      std::shared_ptr<smtk::common::Managers> emptyManagers;
      return read(filename, emptyManagers);
    };
    auto adaptedWrite = [write](const smtk::resource::Resource::Ptr& resource) -> bool {
      std::shared_ptr<smtk::common::Managers> emptyManagers;
      return write(resource, emptyManagers);
    };
    resourceManager->registerResource<ResourceType>(adaptedRead, adaptedWrite);
  }

  template<typename Test>
  typename std::enable_if<std::is_same<decltype(Test::read), ReadFunctor>::value, void>::type
    registerResource /* NOLINT */ (const smtk::resource::Manager::Ptr& resourceManager,
      ReadFunctor read,
      WriteFunctor write)
  {
    resourceManager->registerResource<ResourceType>(read, write);
  }

  // If read member has the old signature, use the old read/write methods
  RegisterResourceMetadata(const smtk::resource::Manager::Ptr& resourceManager,
    ReadFunctor read,
    WriteFunctor write)
  {
    // Only one of the templates above will be enabled:
    this->registerResource<MetadataType>(resourceManager, read, write);
  }
};

} // namespace detail
} // namespace aeva
} // namespace session
} // namespace smtk

#endif
