#include "tile/base/option/option_provider.h"

#include "tile/base/optional.h"
#include "tile/base/status.h"
#include "tile/base/string.h"

#include "gflags/gflags.h"
#include "tile/init/on_init.h"

namespace tile {
namespace option {
class GFLagsProvider : public OptionPassiveProvider {
public:
    GFLagsProvider() { TILE_LOG_INFO("Initializing GFlagsProvider."); }

    bool GetAll(const std::vector<const option::MultiKey *> &names) override { return true; }

#define DEFINE_GFLAGS_PROVIDER_IMPL_FOR(Type, TypeName, GFlagTypeName)                                                 \
    Status Get##TypeName(const option::MultiKey &name, std::optional<Type> *value) override                            \
    {                                                                                                                  \
        auto &&info  = gflags::GetCommandLineFlagInfoOrDie(name.ToString().c_str());                                   \
        auto val_opt = TryParse<Type>(info.current_value);                                                             \
        TILE_CHECK(val_opt, "Failed to read flag [{}] of type [{}].", name.ToString(), #Type);                         \
        TILE_CHECK_EQ(info.type, GFlagTypeName, "Type mismatch on flag [{}].", name.ToString());                       \
        *value = *val_opt;                                                                                             \
        return Status(0);                                                                                              \
    }

    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(bool, Bool, "bool")
    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(std::int32_t, Int32, "int32");
    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(std::int64_t, Int64, "int64");
    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(std::uint32_t, UInt32, "uint32")
    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(std::uint64_t, UInt64, "uint64")
    DEFINE_GFLAGS_PROVIDER_IMPL_FOR(double, Double, "double");
#undef DEFINE_GFLAGS_PROVIDER_IMPL_FOR

    TILE_OPTION_NOT_SUPPORTED_IMPL_FOR(GFlags, std::int8_t, Int8);
    TILE_OPTION_NOT_SUPPORTED_IMPL_FOR(GFlags, std::uint8_t, UInt8);
    TILE_OPTION_NOT_SUPPORTED_IMPL_FOR(GFlags, std::int16_t, Int16);
    TILE_OPTION_NOT_SUPPORTED_IMPL_FOR(GFlags, std::uint16_t, UInt16);
    TILE_OPTION_NOT_SUPPORTED_IMPL_FOR(GFlags, float, Float);

    Status GetString(const option::MultiKey &name, std::optional<std::string> *value) override
    {
        auto info = gflags::GetCommandLineFlagInfoOrDie(name.ToString().c_str());
        TILE_CHECK_EQ(info.type, "string", "Type mismatch on flag [{}].");
        *value = info.current_value;
        return Status(0);
    }
};
}// namespace option
}// namespace tile

TILE_OPTION_REGISTER_OPTION_PROVIDER("gflags", ::tile::option::GFLagsProvider);
