// IWYU pragma: private; include "rlang.h"

#ifndef RLANG_INTERNAL_WALK_H
#define RLANG_INTERNAL_WALK_H

#include "rlang-types.h"
#include "cnd.h"

/**
 * Direction of iteration
 *
 * Each non-leaf node of the sexp tree is visited twice: First before
 * visiting the children, and again after the children have been
 * visited. See
 * <https://stlab.adobe.com/group__asl__tutorials__forest.html> about
 * this iteration process.
 *
 * There are three directions:
 * - Incoming: The first time a non-leaf node is visited.
 * - Leaf: After reaching a leaf node, the direction changes from
 *   incoming to outgoing.
 * - Outgoing: The second time a non-leaf node is visited on the way back.
 */
enum r_sexp_it_direction {
  R_SEXP_IT_DIRECTION_leaf = 0,
  R_SEXP_IT_DIRECTION_incoming,
  R_SEXP_IT_DIRECTION_outgoing
};

enum r_sexp_it_relation {
  R_SEXP_IT_RELATION_none = -1,
  R_SEXP_IT_RELATION_root = 0,

  R_SEXP_IT_RELATION_attrib,

  // Nodes
  R_SEXP_IT_RELATION_node_car,
  R_SEXP_IT_RELATION_node_cdr,
  R_SEXP_IT_RELATION_node_tag,

  R_SEXP_IT_RELATION_symbol_string,
  R_SEXP_IT_RELATION_symbol_value,
  R_SEXP_IT_RELATION_symbol_internal,

  R_SEXP_IT_RELATION_function_fmls,
  R_SEXP_IT_RELATION_function_body,
  R_SEXP_IT_RELATION_function_env,

  R_SEXP_IT_RELATION_environment_frame,
  R_SEXP_IT_RELATION_environment_enclos,
  R_SEXP_IT_RELATION_environment_hashtab,

  R_SEXP_IT_RELATION_promise_value,
  R_SEXP_IT_RELATION_promise_expr,
  R_SEXP_IT_RELATION_promise_env,

  R_SEXP_IT_RELATION_pointer_prot,
  R_SEXP_IT_RELATION_pointer_tag,

  // Vectors
  R_SEXP_IT_RELATION_list_elt,
  R_SEXP_IT_RELATION_character_elt,
  R_SEXP_IT_RELATION_expression_elt
};

enum r_sexp_it_raw_relation {
  R_SEXP_IT_RAW_RELATION_root = 0,
  R_SEXP_IT_RAW_RELATION_attrib,
  R_SEXP_IT_RAW_RELATION_node_tag,
  R_SEXP_IT_RAW_RELATION_node_car,
  R_SEXP_IT_RAW_RELATION_node_cdr,
  R_SEXP_IT_RAW_RELATION_vector_elt
};


struct r_sexp_iterator {
  r_obj* shelter;
  bool skip_incoming;

  r_obj* x;
  enum r_type type;
  int depth;
  r_obj* parent;
  enum r_sexp_it_relation rel;
  r_ssize i;
  enum r_sexp_it_direction dir;

  /* private: */
  struct r_dyn_array* p_stack;
};

struct r_sexp_iterator* r_new_sexp_iterator(r_obj* root);

bool r_sexp_next(struct r_sexp_iterator* p_it);
bool r_sexp_skip(struct r_sexp_iterator* p_it);


static inline
enum r_sexp_it_raw_relation r_sexp_it_raw_relation(enum r_sexp_it_relation rel) {
  switch (rel) {
  case R_SEXP_IT_RELATION_root:
    return R_SEXP_IT_RAW_RELATION_root;

  case R_SEXP_IT_RELATION_attrib:
    return R_SEXP_IT_RAW_RELATION_attrib;

  case R_SEXP_IT_RELATION_node_car:
  case R_SEXP_IT_RELATION_symbol_string:
  case R_SEXP_IT_RELATION_environment_frame:
  case R_SEXP_IT_RELATION_function_fmls:
  case R_SEXP_IT_RELATION_promise_value:
    return R_SEXP_IT_RAW_RELATION_node_car;

  case R_SEXP_IT_RELATION_node_cdr:
  case R_SEXP_IT_RELATION_symbol_value:
  case R_SEXP_IT_RELATION_environment_enclos:
  case R_SEXP_IT_RELATION_function_body:
  case R_SEXP_IT_RELATION_promise_expr:
  case R_SEXP_IT_RELATION_pointer_prot:
    return R_SEXP_IT_RAW_RELATION_node_cdr;

  case R_SEXP_IT_RELATION_node_tag:
  case R_SEXP_IT_RELATION_symbol_internal:
  case R_SEXP_IT_RELATION_environment_hashtab:
  case R_SEXP_IT_RELATION_function_env:
  case R_SEXP_IT_RELATION_promise_env:
  case R_SEXP_IT_RELATION_pointer_tag:
    return R_SEXP_IT_RAW_RELATION_node_tag;

  case R_SEXP_IT_RELATION_list_elt:
  case R_SEXP_IT_RELATION_character_elt:
  case R_SEXP_IT_RELATION_expression_elt:
    return R_SEXP_IT_RAW_RELATION_vector_elt;

  default:
    r_abort("Unimplemented type.");
  }
}

const char* r_sexp_it_direction_as_c_string(enum r_sexp_it_direction dir);
const char* r_sexp_it_relation_as_c_string(enum r_sexp_it_relation rel);
const char* r_sexp_it_raw_relation_as_c_string(enum r_sexp_it_raw_relation rel);


#endif
