//============================================================================
//  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.
//============================================================================

#include <fides/Deprecated.h>

#include <iostream>

namespace
{

struct NewClass
{
  void ImportantMethod(double x, double tolerance)
  {
    std::cout << "Using " << x << " with tolerance " << tolerance << std::endl;
  }

  FIDES_DEPRECATED(1.7, "You must now specify a tolerance.") void ImportantMethod(double x)
  {
    this->ImportantMethod(x, 1e-6);
  }

  FIDES_DEPRECATED(1.6, "You must now specify both a value and tolerance.")
  void ImportantMethod()
  {
    // It can be the case that to implement a deprecated method you need to use other
    // deprecated features. To do that, just temporarily suppress those warnings.
    FIDES_DEPRECATED_SUPPRESS_BEGIN
    this->ImportantMethod(0.0);
    FIDES_DEPRECATED_SUPPRESS_END
  }
};

struct FIDES_DEPRECATED(1.6, "OldClass replaced with NewClass.") OldClass
{
};

using OldAlias FIDES_DEPRECATED(1.6, "Use NewClass instead.") = NewClass;

// Should be OK for one deprecated alias to use another deprecated thing, but most compilers
// do not think so. So, when implementing deprecated things, you might need to suppress
// warnings for that part of the code.
FIDES_DEPRECATED_SUPPRESS_BEGIN
using OlderAlias FIDES_DEPRECATED(1.6, "Update your code to NewClass.") = OldAlias;
FIDES_DEPRECATED_SUPPRESS_END

enum struct FIDES_DEPRECATED(1.7, "Use NewEnum instead.") OldEnum
{
  OLD_VALUE
};

enum struct NewEnum
{
  OLD_VALUE1 FIDES_DEPRECATED(1.7, "Use NEW_VALUE instead."),
  NEW_VALUE,
  OLD_VALUE2 FIDES_DEPRECATED(1.7) = 42
};

template <typename T>
void DoSomethingWithObject(T)
{
  std::cout << "Looking at " << typeid(T).name() << std::endl;
}

} // anonymous namespace

int main()
{
  std::cout << "C++14 [[deprecated]] supported: "
#ifdef FIDES_DEPRECATED_ATTRIBUTE_SUPPORTED
            << "yes"
#else
            << "no"
#endif
            << std::endl;
  std::cout << "Deprecated warnings can be suppressed: "
#ifdef FIDES_DEPRECATED_SUPPRESS_SUPPORTED
            << "yes"
#else
            << "no"
#endif
            << std::endl;

  std::string msg = FIDES_DEPRECATED_MAKE_MESSAGE(X.Y);
  if (msg != " Deprecated in version X.Y.")
  {
    std::runtime_error("error in FIDES_DEPRECATED_MAKE_MESSAGE");
  }
  msg = FIDES_DEPRECATED_MAKE_MESSAGE(X.Y.Z, "Use feature foo instead.");
  if (msg != "Use feature foo instead. Deprecated in version X.Y.Z.")
  {
    std::runtime_error("error in FIDES_DEPRECATED_MAKE_MESSAGE");
  }

  // Using valid classes with unused deprecated parts should be fine.
  NewClass useIt;
  DoSomethingWithObject(useIt);
  useIt.ImportantMethod(1.1, 1e-8);
  DoSomethingWithObject(NewEnum::NEW_VALUE);

  return EXIT_SUCCESS;
}
