////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 by EMC Corporation, All Rights Reserved
///
/// 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
///
///     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 CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is EMC Corporation
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "search/scorer.hpp"
#include "utils/text_format.hpp"

namespace irs {

class scorer_registrar {
 public:
  scorer_registrar(const type_info& type, const type_info& args_format,
                   Scorer::ptr (*factory)(std::string_view args),
                   const char* source = nullptr);
  operator bool() const noexcept;

 private:
  bool registered_;
};

#define REGISTER_SCORER__(scorer_name, args_format, factory, line, source) \
  static ::irs::scorer_registrar scorer_registrar##_##line(                \
    ::irs::type<scorer_name>::get(), ::irs::type<args_format>::get(),      \
    &factory, source)
#define REGISTER_SCORER_EXPANDER__(scorer_name, args_format, factory, file, \
                                   line)                                    \
  REGISTER_SCORER__(scorer_name, args_format, factory, line,                \
                    file ":" IRS_TO_STRING(line))
#define REGISTER_SCORER(scorer_name, args_format, factory)                \
  REGISTER_SCORER_EXPANDER__(scorer_name, args_format, factory, __FILE__, \
                             __LINE__)
#define REGISTER_SCORER_CSV(scorer_name, factory) \
  REGISTER_SCORER(scorer_name, ::irs::text_format::csv, factory)
#define REGISTER_SCORER_JSON(scorer_name, factory) \
  REGISTER_SCORER(scorer_name, ::irs::text_format::json, factory)
#define REGISTER_SCORER_VPACK(scorer_name, factory) \
  REGISTER_SCORER(scorer_name, ::irs::text_format::vpack, factory)
#define REGISTER_SCORER_TEXT(scorer_name, factory) \
  REGISTER_SCORER(scorer_name, ::irs::text_format::text, factory)
#define REGISTER_SCORER_XML(scorer_name, factory) \
  REGISTER_SCORER(scorer_name, ::irs::text_format::xml, factory)
#define REGISTER_SCORER_TYPED(scorer_name, args_format) \
  REGISTER_SCORER(scorer_name, args_format, scorer_name::make)

namespace scorers {

// Check whether scorer with a specified name is registered
bool exists(std::string_view name, const type_info& args_format,
            bool load_library = true);

// Find a scorer by name, or nullptr if not found
// indirect call to <class>::make(...)
// NOTE: make(...) MUST be defined in CPP to ensire proper code scope
Scorer::ptr get(std::string_view name, const type_info& args_format,
                std::string_view args, bool load_library = true) noexcept;

// For static lib reference all known scorers in lib
// for shared lib NOOP
// no explicit call of fn is required, existence of fn is sufficient
void init();

// Load all scorers from plugins directory
void load_all(std::string_view path);

// Visit all loaded scorers, terminate early if visitor returns false
bool visit(
  const std::function<bool(std::string_view, const type_info&)>& visitor);

}  // namespace scorers
}  // namespace irs
