#ifndef FUNCTION_ARGUMENT_CONVERTER_HPP
#define FUNCTION_ARGUMENT_CONVERTER_HPP

#include <language/node_processor/ExecutionPolicy.hpp>
#include <language/utils/DataVariant.hpp>

class IFunctionArgumentConverter
{
 public:
  virtual DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) = 0;

  IFunctionArgumentConverter() = default;

  IFunctionArgumentConverter(const IFunctionArgumentConverter&) = delete;
  IFunctionArgumentConverter(IFunctionArgumentConverter&&)      = delete;

  virtual ~IFunctionArgumentConverter() = default;
};

template <typename ExpectedValueType, typename ProvidedValueType>
class FunctionArgumentConverter final : public IFunctionArgumentConverter
{
 private:
  size_t m_argument_id;

 public:
  DataVariant
  convert(ExecutionPolicy& exec_policy, DataVariant&& value)
  {
    if constexpr (std::is_same_v<ExpectedValueType, ProvidedValueType>) {
      exec_policy.currentContext()[m_argument_id] = std::move(value);
    } else if constexpr (std::is_same_v<ExpectedValueType, std::string>) {
      exec_policy.currentContext()[m_argument_id] = std::move(std::to_string(std::get<ProvidedValueType>(value)));
    } else if constexpr (std::is_same_v<ProvidedValueType, ZeroType>) {
      exec_policy.currentContext()[m_argument_id] = ExpectedValueType{ZeroType::zero};
    } else {
      exec_policy.currentContext()[m_argument_id] =
        std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
    }
    return {};
  }

  FunctionArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {}
};

class FunctionArgumentToFunctionSymbolIdConverter final : public IFunctionArgumentConverter
{
 private:
  size_t m_argument_id;
  std::shared_ptr<SymbolTable> m_symbol_table;

 public:
  DataVariant
  convert(ExecutionPolicy& exec_policy, DataVariant&& value)
  {
    exec_policy.currentContext()[m_argument_id] = FunctionSymbolId{std::get<uint64_t>(value), m_symbol_table};

    return {};
  }

  FunctionArgumentToFunctionSymbolIdConverter(size_t argument_id, const std::shared_ptr<SymbolTable>& symbol_table)
    : m_argument_id{argument_id}, m_symbol_table{symbol_table}
  {}
};

#endif   // FUNCTION_ARGUMENT_CONVERTER_HPP