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

#ifndef RLANG_DYN_ARRAY_H
#define RLANG_DYN_ARRAY_H

#include "rlang-types.h"
#include "c-utils.h"
#include "cnd.h"
#include "vec.h"


struct r_dyn_array {
  r_obj* shelter;
  r_ssize count;
  r_ssize capacity;
  int growth_factor;

  r_obj* data;
  void* v_data;
  const void* v_data_const;

  // private:
  enum r_type type;
  r_ssize elt_byte_size;
  void (*barrier_set)(r_obj* x, r_ssize i, r_obj* value);
};

struct r_dyn_array* r_new_dyn_vector(enum r_type type,
                                     r_ssize capacity);

struct r_dyn_array* r_new_dyn_array(r_ssize elt_byte_size,
                                    r_ssize capacity);

void r_dyn_resize(struct r_dyn_array* p_arr,
                  r_ssize capacity);

void r_dyn_push_back(struct r_dyn_array* p_arr,
                     const void* p_elt);

r_obj* r_dyn_unwrap(struct r_dyn_array* p_arr);

static inline
void* r_dyn_pointer(struct r_dyn_array* p_arr, r_ssize i) {
  if (p_arr->barrier_set) {
    r_abort("Can't take mutable pointer of barrier vector.");
  }
  r_ssize offset = i * p_arr->elt_byte_size;
  return ((unsigned char*) p_arr->v_data) + offset;
}
static inline
void* r_dyn_begin(struct r_dyn_array* p_arr) {
  return r_dyn_pointer(p_arr, 0);
}
static inline
void* r_dyn_last(struct r_dyn_array* p_arr) {
  return r_dyn_pointer(p_arr, p_arr->count - 1);
}
static inline
void* r_dyn_end(struct r_dyn_array* p_arr) {
  return r_dyn_pointer(p_arr, p_arr->count);
}

static inline
const void* r_dyn_cpointer(struct r_dyn_array* p_arr, r_ssize i) {
  r_ssize offset = i * p_arr->elt_byte_size;
  return ((const unsigned char*) p_arr->v_data_const) + offset;
}
static inline
const void* r_dyn_cbegin(struct r_dyn_array* p_arr) {
  return r_dyn_cpointer(p_arr, 0);
}
static inline
const void* r_dyn_clast(struct r_dyn_array* p_arr) {
  return r_dyn_cpointer(p_arr, p_arr->count - 1);
}
static inline
const void* r_dyn_cend(struct r_dyn_array* p_arr) {
  return r_dyn_cpointer(p_arr, p_arr->count);
}

#define R_DYN_GET(TYPE, X, I) (*((TYPE*) r_dyn_pointer((X), (I))))
#define R_DYN_POKE(TYPE, X, I, VAL) (*((TYPE*) r_dyn_pointer((X), (I))) = (VAL))

static inline
int r_dyn_lgl_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((const int*) p_vec->v_data_const)[i];
}
static inline
int r_dyn_int_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((const int*) p_vec->v_data_const)[i];
}
static inline
double r_dyn_dbl_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((const double*) p_vec->v_data_const)[i];
}
static inline
r_complex r_dyn_cpl_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((const r_complex*) p_vec->v_data_const)[i];
}
static inline
char r_dyn_raw_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((const char*) p_vec->v_data_const)[i];
}
static inline
r_obj* r_dyn_chr_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((r_obj* const *) p_vec->v_data_const)[i];
}
static inline
r_obj* r_dyn_list_get(struct r_dyn_array* p_vec, r_ssize i) {
  return ((r_obj* const *) p_vec->v_data_const)[i];
}

static inline
void r_dyn_lgl_poke(struct r_dyn_array* p_vec, r_ssize i, int value) {
  ((int*) p_vec->v_data)[i] = value;
}
static inline
void r_dyn_int_poke(struct r_dyn_array* p_vec, r_ssize i, int value) {
  ((int*) p_vec->v_data)[i] = value;
}
static inline
void r_dyn_dbl_poke(struct r_dyn_array* p_vec, r_ssize i, double value) {
  ((double*) p_vec->v_data)[i] = value;
}
static inline
void r_dyn_cpl_poke(struct r_dyn_array* p_vec, r_ssize i, r_complex value) {
  ((r_complex*) p_vec->v_data)[i] = value;
}
static inline
void r_dyn_raw_poke(struct r_dyn_array* p_vec, r_ssize i, char value) {
  ((char*) p_vec->v_data)[i] = value;
}
static inline
void r_dyn_chr_poke(struct r_dyn_array* p_vec, r_ssize i, r_obj* value) {
  r_chr_poke(p_vec->data, i, value);
}
static inline
void r_dyn_list_poke(struct r_dyn_array* p_vec, r_ssize i, r_obj* value) {
  r_list_poke(p_vec->data, i, value);
}

static inline
void* const * r_dyn_pop_back(struct r_dyn_array* p_arr) {
  void* const * out = (void* const *) r_dyn_clast(p_arr);
  --p_arr->count;
  return out;
}

static inline
r_ssize r__dyn_increment(struct r_dyn_array* p_arr) {
  r_ssize loc = p_arr->count++;

  if (p_arr->count > p_arr->capacity) {
    r_ssize new_capacity = r_ssize_mult(p_arr->capacity, p_arr->growth_factor);
    r_dyn_resize(p_arr, new_capacity);
  }

  return loc;
}

static inline
void r_dyn_lgl_push_back(struct r_dyn_array* p_vec, int elt) {
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_lgl_poke(p_vec, loc, elt);
}
static inline
void r_dyn_int_push_back(struct r_dyn_array* p_vec, int elt) {
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_int_poke(p_vec, loc, elt);
}
static inline
void r_dyn_dbl_push_back(struct r_dyn_array* p_vec, double elt) {
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_dbl_poke(p_vec, loc, elt);
}
static inline
void r_dyn_cpl_push_back(struct r_dyn_array* p_vec, r_complex elt) {
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_cpl_poke(p_vec, loc, elt);
}
static inline
void r_dyn_raw_push_back(struct r_dyn_array* p_vec, char elt) {
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_raw_poke(p_vec, loc, elt);
}
static inline
void r_dyn_chr_push_back(struct r_dyn_array* p_vec, r_obj* elt) {
  KEEP(elt);
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_chr_poke(p_vec, loc, elt);
  FREE(1);
}
static inline
void r_dyn_list_push_back(struct r_dyn_array* p_vec, r_obj* elt) {
  KEEP(elt);
  r_ssize loc = r__dyn_increment(p_vec);
  r_dyn_list_poke(p_vec, loc, elt);
  FREE(1);
}

#endif
