//
// Copyright 2019 The Abseil Authors.
//
// Licensed 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
//
//      https://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 CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_

#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"

namespace absl {
namespace flags_internal {

// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
class Flag {
 public:
  constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
                 const char* filename,
                 const flags_internal::FlagMarshallingOpFn marshalling_op,
                 const flags_internal::InitialValGenFunc initial_value_gen)
      : internal(name, flags_internal::HelpText::FromFunctionPointer(help_gen),
                 filename, &flags_internal::FlagOps<T>, marshalling_op,
                 initial_value_gen,
                 /*retired_arg=*/false, /*def_arg=*/nullptr,
                 /*cur_arg=*/nullptr) {}

  // Not copyable/assignable.
  Flag(const Flag<T>&) = delete;
  Flag<T>& operator=(const Flag<T>&) = delete;

  absl::string_view Name() const { return internal.Name(); }
  std::string Help() const { return internal.Help(); }
  std::string Filename() const { return internal.Filename(); }

  absl::flags_internal::CommandLineFlag internal;

  void SetCallback(const flags_internal::FlagCallback mutation_callback) {
    internal.SetCallback(mutation_callback);
  }

 private:
  // TODO(rogeeff): add these validations once UnparseFlag invocation is fixed
  // for built-in types and when we cleanup existing code from operating on
  // forward declared types.
  //  auto IsCopyConstructible(const T& v) -> decltype(T(v));
  //  auto HasAbslParseFlag(absl::string_view in, T* dst, std::string* err)
  //      -> decltype(AbslParseFlag(in, dst, GlobalStringADLGuard(err)));
  //  auto HasAbslUnparseFlag(const T& v) -> decltype(AbslUnparseFlag(v));
};

// This class facilitates Flag object registration and tail expression-based
// flag definition, for example:
// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
template <typename T, bool do_register>
class FlagRegistrar {
 public:
  explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
    if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->internal);
  }

  FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
    flag_->SetCallback(cb);
    return *this;
  }

  // Make the registrar "die" gracefully as a bool on a line where registration
  // happens. Registrar objects are intended to live only as temporary.
  operator bool() const { return true; }  // NOLINT

 private:
  Flag<T>* flag_;  // Flag being registered (not owned).
};

// This struct and corresponding overload to MakeDefaultValue are used to
// facilitate usage of {} as default value in ABSL_FLAG macro.
struct EmptyBraces {};

template <typename T>
T* MakeFromDefaultValue(T t) {
  return new T(std::move(t));
}

template <typename T>
T* MakeFromDefaultValue(EmptyBraces) {
  return new T;
}

}  // namespace flags_internal
}  // namespace absl

#endif  // ABSL_FLAGS_INTERNAL_FLAG_H_
