// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef _ALIAS_DATA_STRUCTURE_VECTOR_H_
#define _ALIAS_DATA_STRUCTURE_VECTOR_H_

#include <tabula.h>

#include <alias/memory.h>

#define alias_Vector(T) struct { uint32_t capacity; uint32_t length; T * data; }

#define ALIAS_VECTOR_INIT { 0, 0, NULL }

#define alias_Vector_clear(V) \
  do {                        \
    (V)->length = 0;          \
  } while(0)

#define alias_Vector_init(V) \
  do {                       \
    alias_Vector_clear(V);   \
    (V)->capacity = 0;       \
    (V)->data = NULL;        \
  } while(0)

#define alias_Vector_free(V, CB)                                                          \
  do {                                                                                    \
    if((V)->data != NULL) {                                                               \
      alias_free(CB, (V)->data, (V)->capacity * sizeof(*(V)->data), alignof(*(V)->data)); \
    }                                                                                     \
    alias_Vector_init(V);                                                                 \
  } while(0)

#define alias_Vector_pop(V) ((V)->data + (--(V)->length))

#define alias_Vector_push(V) ((V)->data + ((V)->length++))

#define alias_Vector_set_capacity(V, CB, C)                                                                                                  \
  ( ((C) > (V)->capacity) ?                                                                                                                  \
    (                                                                                                                                        \
     ((V)->data = alias_realloc(CB, (V)->data, (V)->capacity * sizeof(*(V)->data), (C) * sizeof(*(V)->data), alignof(*(V)->data))) != NULL ? \
      ((V)->capacity = (C), true) :                                                                                                          \
      false                                                                                                                                  \
    ) : true                                                                                                                                 \
  )

#define alias_Vector_space_for(V, CB, C) alias_Vector_set_capacity(V, CB, (((V)->length + (C)) + (((V)->length + (C)) >> 1)))

#define alias_Vector_qsort(V, F, U) tabula_qsort((V)->data, (V)->length, sizeof(*(V)->data), F, U)

#define alias_Vector_bsearch(V, K, F, U) tabula_bsearch(K, (V)->data, (V)->length, sizeof(*(V)->data), F, U)

#define alias_Vector_remove_at(V, I)                                                             \
  do {                                                                                           \
      uint32_t __i = (I);                                                                        \
      (V)->length--;                                                                             \
      if((V)->length > 0 && (V)->length != __i) {                                                \
        memmove((V)->data + __i, (V)->data + __i + 1, ((V)->length - __i) * sizeof(*(V)->data)); \
      }                                                                                          \
  } while(0)

#define alias_Vector_swap_pop(V, I)                                         \
  do {                                                                      \
    uint32_t __i = (I);                                                     \
    (V)->length--;                                                          \
    if((V)->length != __i) {                                                \
      memcpy((V)->data + __i, (V)->data + (V)->length, sizeof(*(V)->data)); \
    }                                                                       \
  } while(0)

#endif