#ifndef _list_h__
#define _list_h__

/** List **
 * 
 * Simple list using a dynamically allocated array.
 * 
 * This structure is roughly similar to Java's ArrayList, or Python's built-in list type.
 * 
 * This structure distinguishes between capacity, i.e. the length of its underlying array,
 * and size, i.e. the used portion of capacity. It's always true that size <= capacity.
 * 
 * The first element has index 0, and the last element has index size-1.
 * 
 * To use a List on the heap:
 * 
 *   List *list = list_new();
 *   list_grow(list, CAPACITY);  // optional
 *   ...
 *   list_delete(list);
 * 
 * To use a List on the stack:
 * 
 *   List list;
 *   list_init(&list);
 *   list_grow(&list, CAPACITY);  // optional
 *   ...
 *   list_clear(&list);
 * 
 * Iteration is made convenient by macros:
 * 
 *   // List *h;
 *   LIST_FOR(list) {
 *      MyType *element = LIST_CUR(MyType);
 *      if (want_to_remove(element)) {
 *          LIST_CUR_REMOVE(list);
 *      }
 *   }
 * 
 */

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif


/**
 * Default List capacity (maximum size)
 */
#define LIST_DEFAULT_CAPACITY 8

/**
 * Simple List using a dynamically allocated array.
 *
 * Struct members:
 * 
 *   elements: list-allocated array of elements (each element is a pointer)
 *   capacity: length of array
 *   size:     number of elements
 */
typedef struct List {

    void **elements;
    size_t capacity;
    size_t size;

} List;

/**
 * Declare an empty List with given name. This List is already initialized.
 * Make sure to call list_clear() when it is no longer needed.
 */ 
#define LIST(name) \
    List name = { .elements = NULL, .capacity = 0, .size = 0 };

/**
 * Create a new List on the heap. It must be deleted later with list_delete().
 * 
 * The newly created List is empty, i.e. both capacity and size are zero.
 * Use list_grow() to increase capacity.
 * 
 * Return: pointer to the newly allocated List
 */
List *list_new();

/**
 * Delete a List previously created with list_new().
 */
void list_delete(List *list);

/**
 * Initialize a List declared on the stack. It must be cleared later with list_clear().
 * 
 * After initialization, the List is empty, i.e. both capacity and size are zero.
 * Use list_grow() to increase capacity.
 */
void list_init(List *list);

/**
 * Clear a List, i.e. set capacity and size to zero, and free the underlying array.
 * 
 * This function is intended to be called when the List is no longer needed, since it
 * frees the internal array.
 * 
 * The List itself is not freed can be re-used immediately. It is not necessary to call
 * list_init() after list_clear().
 * 
 * To remove all elements from the List, it is more efficient to call list_remove_all()
 * instead, as it will keep the existing array.
 * 
 * It is not necessary to call list_clear() before list_delete().
 */
void list_clear(List *list);

/**
 * Set the capacity of a List to n bytes.
 * The internal array is (re-)allocated accordingly.
 * 
 * If n == 0, LIST_DEFAULT_CAPACITY is used instead.
 */
void list_grow(List *list, size_t n);

/**
 * Return the size of a List.
 */
#define list_size(list)  ((list)->size)

/**
 * Append an element to the end of List. If the List is full, it grows automatically.
 * 
 * Complexity: Amortized O(1)
 */
void list_add(List *list, void *element);

/**
 * Append an element to the end of the List only if it doesn't already contain the element.
 * 
 * If the List is full, it grows automatically.
 * 
 * Equality corresponds to straightforward pointer equality.
 * 
 * Return: 1 if the element was inserted, 0 if the List already contained the element
 * 
 * Complexity: O(size)
 */
int list_add_unique(List *list, void *element);

/**
 * Remove an element from the List.
 * 
 * Return: 1 if the element was found and removed, 0 otherwise
 * 
 * Complexity: O(size)
 */
int list_remove(List *list, void *element);

/**
 * Remove the element at the given index from the List.
 * 
 * To remove the last element, list_remove_last() is more efficient.
 * 
 * Complexity: O(size-index)
 */
void list_remove_at(List *list, size_t index);

/**
 * Remove the last element from the List. Equivalent to list_remove() with index = size - 1,
 * only more efficient.
 * 
 * Complexity: O(1)
 */
#define list_remove_last(list)  { if ((list)->size) (list)->size--; }

/**
 * Remove all elements from the List. Capacity is retained.
 * 
 * Complexity: O(1)
 */
#define list_remove_all(list)  { (list)->size = 0; }

/**
 * Return the element at the given index in the List, or NULL if index is out of bounds.
 * 
 * Complexity: O(1)
 */
#define list_get(list, index)  ( (index) < (list)->size ? (list)->elements[index] : NULL )

/**
 * Return whether the List contains the given element.
 * 
 * Equality corresponds to straightforward pointer equality.
 * 
 * Return: 1 if the List contains the element, 0 otherwise
 * 
 * Complexity: O(size)
 */
int list_contains(List *list, void *element);

/**
 * Macro to call list_get() and cast the result to a pointer of the desired type.
 * 
 * // List *list;
 * MyType *element = LIST_GET(list, 0, MyType);
 */
#define LIST_GET(list, index, type) ((type *)(list_get((list), (index))))

/**
 * Macro for convenient iteration in a for-loop way.
 * 
 *   // List *list;
 *   LIST_FOR(list) {
 *      MyType *element = LIST_CUR(MyType);
 *   }
 */
#define LIST_FOR(list) for (void **__l = (list)->elements; __l < (list)->elements + (list)->size; __l++)

/**
 * Macro for convenient iteration in a for-loop way (reverse iteration).
 * 
 *   // List *list;
 *   LIST_FOR_REVERSE(list) {
 *      MyType *element = LIST_CUR(MyType);
 *   }
 */
#define LIST_FOR_REVERSE(list) for (void **__l = (list)->elements + (list)->size - 1; __l >= (list)->elements; __l--)

/**
 * Macro for convenient iteration in a for-loop way.
 * 
 *   // List *list;
 *   LIST_FOR(list) {
 *      MyType *element = LIST_CUR(MyType);
 *   }
 */
#define LIST_CUR(type) ((type *)(*__l))

/**
 * Macro to remove the current element from the List during iteration.
 * 
 *   // List *list;
 *   LIST_FOR(list) {
 *      MyType *element = LIST_CUR(MyType);
 *      if (want_to_remove(element)) {
 *          LIST_CUR_REMOVE(list);
 *      }
 *   }
 */
#define LIST_CUR_REMOVE(list) { \
    list_remove_at((list), __l - (list)->elements); \
    __l--; \
}

/**
 * Macro to remove the current element from the List during reverse iteration.
 * 
 *   // List *list;
 *   LIST_FOR_REVERSE(list) {
 *      MyType *element = LIST_CUR(MyType);
 *      if (want_to_remove(element)) {
 *          LIST_CUR_REMOVE_REVERSE(list);
 *      }
 *   }
 */
#define LIST_CUR_REMOVE_REVERSE(list) { \
    list_remove_at((list), __l - (list)->elements); \
}


#ifdef __cplusplus
}
#endif

#endif
