/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
*/
#pragma once
#include "ast.h"
#include "platform-utils.h"
namespace tolk {
class ASTReplicator {
protected:
  virtual AnyV clone(AnyV v) = 0;
  virtual AnyExprV clone(AnyExprV v) = 0;
  virtual TypePtr clone(TypePtr) = 0;
public:
  virtual ~ASTReplicator() = default;
};
class ASTReplicatorFunction : public ASTReplicator {
protected:
  using parent = ASTReplicatorFunction;
  std::vector clone(const std::vector& items) {
    std::vector result;
    result.reserve(items.size());
    for (AnyV item : items) {
      result.push_back(clone(item));
    }
    return result;
  }
  std::vector clone(const std::vector& items) {
    std::vector result;
    result.reserve(items.size());
    for (AnyExprV item : items) {
      result.push_back(clone(item));
    }
    return result;
  }
  // expressions
  virtual V clone(V v) {
    return createV(v->loc);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_expr()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_items()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_items()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_identifier()), v->has_instantiationTs() ? clone(v->get_instantiationTs()) : nullptr);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_identifier()), clone(v->declared_type), v->is_immutable, v->marked_as_redef);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_expr()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->intval, v->orig_str);
  }
  virtual V clone(V v) {
    return createV(v->loc, v->str_val, v->modifier);
  }
  virtual V clone(V v) {
    return createV(v->loc, v->bool_val);
  }
  virtual V clone(V v) {
    return createV(v->loc);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_expr()), v->passed_as_mutate);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_arguments()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_obj()), clone(v->get_identifier()), v->has_instantiationTs() ? clone(v->get_instantiationTs()) : nullptr);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_callee()), clone(v->get_arg_list()));
  }
  virtual V clone(V v) {
    return createV(v->loc);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_lhs()), clone(v->get_rhs()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->operator_name, v->tok, clone(v->get_lhs()), clone(v->get_rhs()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->operator_name, v->tok, clone(v->get_rhs()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->operator_name, v->tok, clone(v->get_lhs()), clone(v->get_rhs()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_cond()), clone(v->get_when_true()), clone(v->get_when_false()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_expr()), clone(v->cast_to_type));
  }
  // statements
  virtual V clone(V v) {
    return createV(v->loc);
  }
  virtual V clone(V v) {
    return createV(v->loc, v->loc_end, clone(v->get_items()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_return_value()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->is_ifnot, clone(v->get_cond()), clone(v->get_if_body()), clone(v->get_else_body()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_cond()), clone(v->get_body()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_cond()), clone(v->get_body()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_body()), clone(v->get_cond()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_thrown_code()), clone(v->get_thrown_arg()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_cond()), clone(v->get_thrown_code()));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_try_body()), clone(v->get_catch_expr()), clone(v->get_catch_body()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->arg_order, v->ret_order, clone(v->get_asm_commands()));
  }
  // other
  virtual V clone(V v) {
    return createV(v->loc, v->name);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->substituted_type));
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_items()));
  }
  virtual V clone(V v) {
    return createV(v->loc, v->param_name, clone(v->declared_type), v->declared_as_mutate);
  }
  virtual V clone(V v) {
    return createV(v->loc, clone(v->get_params()));
  }
  AnyExprV clone(AnyExprV v) final {
    switch (v->type) {
      case ast_empty_expression:                return clone(v->as());
      case ast_parenthesized_expression:        return clone(v->as());
      case ast_tensor:                          return clone(v->as());
      case ast_typed_tuple:                     return clone(v->as());
      case ast_reference:                       return clone(v->as());
      case ast_local_var_lhs:                   return clone(v->as());
      case ast_local_vars_declaration:          return clone(v->as());
      case ast_int_const:                       return clone(v->as());
      case ast_string_const:                    return clone(v->as());
      case ast_bool_const:                      return clone(v->as());
      case ast_null_keyword:                    return clone(v->as());
      case ast_argument:                        return clone(v->as());
      case ast_argument_list:                   return clone(v->as());
      case ast_dot_access:                      return clone(v->as());
      case ast_function_call:                   return clone(v->as());
      case ast_underscore:                      return clone(v->as());
      case ast_assign:                          return clone(v->as());
      case ast_set_assign:                      return clone(v->as());
      case ast_unary_operator:                  return clone(v->as());
      case ast_binary_operator:                 return clone(v->as());
      case ast_ternary_operator:                return clone(v->as());
      case ast_cast_as_operator:                return clone(v->as());
      default:
        throw UnexpectedASTNodeType(v, "ASTReplicatorFunction::clone");
    }
  }
  AnyV clone(AnyV v) final {
    switch (v->type) {
      case ast_empty_statement:                 return clone(v->as());
      case ast_sequence:                        return clone(v->as());
      case ast_return_statement:                return clone(v->as());
      case ast_if_statement:                    return clone(v->as());
      case ast_repeat_statement:                return clone(v->as());
      case ast_while_statement:                 return clone(v->as());
      case ast_do_while_statement:              return clone(v->as());
      case ast_throw_statement:                 return clone(v->as());
      case ast_assert_statement:                return clone(v->as());
      case ast_try_catch_statement:             return clone(v->as());
      case ast_asm_body:                        return clone(v->as());
      // other AST nodes that can be children of ast nodes of function body
      case ast_identifier:                      return clone(v->as());
      case ast_instantiationT_item:             return clone(v->as());
      case ast_instantiationT_list:             return clone(v->as());
      case ast_parameter:                       return clone(v->as());
      case ast_parameter_list:                  return clone(v->as());
      default: {
        // be very careful, don't forget to handle all statements/other (not expressions) above!
        AnyExprV as_expr = reinterpret_cast(v);
        return clone(as_expr);
      }
    }
  }
  TypePtr clone(TypePtr t) override {
    return t;
  }
 public:
  virtual V clone_function_body(V v_function) {
    return createV(
      v_function->loc,
      clone(v_function->get_identifier()),
      clone(v_function->get_param_list()),
      clone(v_function->get_body()->as()),
      clone(v_function->declared_return_type),
      v_function->genericsT_list,
      v_function->method_id,
      v_function->flags
    );
  }
};
} // namespace tolk