#ifndef AFFECTATION_PROCESSOR_BUILDER_HPP
#define AFFECTATION_PROCESSOR_BUILDER_HPP

#include <algebra/TinyVector.hpp>
#include <language/PEGGrammar.hpp>
#include <language/node_processor/AffectationProcessor.hpp>
#include <language/utils/ASTNodeNaturalConversionChecker.hpp>
#include <language/utils/IAffectationProcessorBuilder.hpp>

#include <type_traits>

template <typename OperatorT, typename ValueT, typename DataT>
class AffectationProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    if constexpr ((std::is_same_v<ValueT, TinyVector<1>> or
                   std::is_same_v<ValueT, TinyMatrix<1>>)and std::is_same_v<DataT, int64_t> and
                  std::is_same_v<OperatorT, language::eq_op>) {
      // Special treatment for the case 0 -> R^1 or 0 -> R^1x1
      if ((rhs_node.is_type<language::integer>()) and (std::stoi(rhs_node.string()) == 0)) {
        return std::make_unique<AffectationFromZeroProcessor<ValueT>>(lhs_node);
      } else {
        return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(lhs_node, rhs_node);
      }
    } else {
      return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(lhs_node, rhs_node);
    }
  }
};

template <typename ValueT>
class AffectationToTupleProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationToTupleProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    return std::make_unique<AffectationToTupleProcessor<ValueT>>(lhs_node, rhs_node);
  }
};

template <typename ValueT>
class AffectationToTupleFromListProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationToTupleFromListProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    ASTNodeNaturalConversionChecker(rhs_node, lhs_node.m_data_type);
    return std::make_unique<AffectationToTupleFromListProcessor<ValueT>>(lhs_node, rhs_node);
  }
};

template <typename OperatorT, typename ValueT>
class AffectationToTinyVectorFromListProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationToTinyVectorFromListProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    return std::make_unique<AffectationToTinyVectorFromListProcessor<OperatorT, ValueT>>(lhs_node, rhs_node);
  }
};

template <typename OperatorT, typename ValueT>
class AffectationToTinyMatrixFromListProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationToTinyMatrixFromListProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    return std::make_unique<AffectationToTinyMatrixFromListProcessor<OperatorT, ValueT>>(lhs_node, rhs_node);
  }
};

template <typename OperatorT, typename ValueT>
class AffectationFromZeroProcessorBuilder final : public IAffectationProcessorBuilder
{
 public:
  AffectationFromZeroProcessorBuilder() = default;
  std::unique_ptr<INodeProcessor>
  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
  {
    if (std::stoi(rhs_node.string()) == 0) {
      return std::make_unique<AffectationFromZeroProcessor<ValueT>>(lhs_node);
    } else {
      throw ParseError("invalid integral value (0 is the solely valid value)", std::vector{lhs_node.begin()});
    }
  }
};

#endif   // AFFECTATION_PROCESSOR_BUILDER_HPP