// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#pragma once

#include <functional>
#include <memory>
#include <string>
#include <unordered_map>

#include "arrow/status.h"
#include "gandiva/function_holder.h"
#include "gandiva/interval_holder.h"
#include "gandiva/node.h"
#include "gandiva/random_generator_holder.h"
#include "gandiva/regex_functions_holder.h"
#include "gandiva/to_date_holder.h"

namespace gandiva {

#define LAMBDA_MAKER(derived)                               \
  [](const FunctionNode& node, FunctionHolderPtr* holder) { \
    std::shared_ptr<derived> derived_instance;              \
    auto status = derived::Make(node, &derived_instance);   \
    if (status.ok()) {                                      \
      *holder = derived_instance;                           \
    }                                                       \
    return status;                                          \
  }

/// Static registry of function holders.
class FunctionHolderRegistry {
 public:
  using maker_type = std::function<Status(const FunctionNode&, FunctionHolderPtr*)>;
  using map_type = std::unordered_map<std::string, maker_type>;

  static Status Make(const std::string& name, const FunctionNode& node,
                     FunctionHolderPtr* holder) {
    std::string data = name;
    std::transform(data.begin(), data.end(), data.begin(),
                   [](unsigned char c) { return std::tolower(c); });

    auto found = makers().find(data);
    if (found == makers().end()) {
      return Status::Invalid("function holder not registered for function " + name);
    }

    return found->second(node, holder);
  }

 private:
  static map_type& makers() {
    static map_type maker_map = {{"like", LAMBDA_MAKER(LikeHolder)},
                                 {"ilike", LAMBDA_MAKER(LikeHolder)},
                                 {"to_date", LAMBDA_MAKER(ToDateHolder)},
                                 {"random", LAMBDA_MAKER(RandomGeneratorHolder)},
                                 {"rand", LAMBDA_MAKER(RandomGeneratorHolder)},
                                 {"regexp_replace", LAMBDA_MAKER(ReplaceHolder)},
                                 {"regexp_extract", LAMBDA_MAKER(ExtractHolder)},
                                 {"castintervalday", LAMBDA_MAKER(IntervalDaysHolder)},
                                 {"castintervalyear", LAMBDA_MAKER(IntervalYearsHolder)}};
    return maker_map;
  }
};

}  // namespace gandiva
