// ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// array.c
//
// @note    A simple demo of Object Oriented C, using struct array as an example
// @author  Jorma Rebane
// @date    16.09.2014
// @warning Uses C99 dialect, so compile with -std=gnu99 / -std=c99
//          or via IDE-s Visual Studio 2013 (and higher) / DevC++5.7 (and higher)
//
// ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#include <stdio.h>  // printf
#include <stdlib.h> // malloc
#include <string.h> // memmove



// array structure type definition
typedef struct _array
{
	int  size;     // number of elements in array
	int  capacity; // number of elements in reserve
	int* data;     // elements buffer
} array;



// @return A new empty array object.
array* arr_new()
{
	array* a = (array*)malloc(sizeof(array));
	a->size     = 0;
	a->capacity = 0;
	a->data     = NULL;
	return a;
}


// Frees an allocated array object and its buffer
void arr_free(array* a)
{
	if (a) {
		if (a->data) free(a->data); // free allocated data if needed
		free(a);
	}
}


// Adds an element to the end of the array
void arr_add(array* a, int item)
{
	if (a->size == a->capacity)
	{
		a->capacity += 4 + (a->capacity / 2);
		a->data = (int*)realloc(a->data, a->capacity * sizeof(int));
	}
	a->data[a->size++] = item;
}


// Removes an element from the specified index (no range checks)
// @note Valid ranges [0 .. SIZE-1]
void arr_erase(array* a, int index)
{
	int* end = a->data + a->size;
	int* dst = a->data + index;
	int* src = dst + 1;

	// | 0 | 1 | 2 | 3 | 4 | ... |
	//  dst^   ^src              ^end
	//      <--   move from src to dst, all the bytes between [src .. end]
	// | 0 | 2 | 3 | 4 | ... |
	memmove(dst, src, (char*)end - (char*)src);
	--a->size;
}


// Inserts an element to the specified index in the array (no range checks)
// @note Valid ranges [0 .. SIZE]
void arr_insert(array* a, int index, int item)
{
	if (a->size == a->capacity)
	{
		a->capacity += 4 + (a->capacity / 2);
		a->data = (int*)realloc(a->data, a->capacity * sizeof(int));
	}

	int* end = a->data + a->size;
	int* src = a->data + index;
	int* dst = src + 1;

	// | 0 | 1 | 2 | 3 | ... |
	//  src^   ^dst          ^end
	//      -->   move from src to dst, all the bytes between [src .. end]
	// leaving an empty array slot in the middle
	// | 0 | X | 1 | 2 | 3 | ... |
	memmove(dst, src, (char*)end - (char*)src);
	a->data[index] = item;
	++a->size;
}


// Prints the contents of the array as [size] { %d %d %d ... }\n
void arr_print(array* a)
{
	printf("[%d] { ", a->size);
	for (int i = 0; i < a->size; ++i)
		printf("%d ", a->data[i]);
	printf("}\n");
}


int main(int argc, char** argv)
{
	array* a = arr_new();

	// create [3] { 10 20 30 }
	arr_add(a, 10);
	arr_add(a, 20);
	arr_add(a, 30);
	arr_print(a);

	// erase and insert items
	arr_erase(a, 1);
	arr_insert(a, 1, 40);
	arr_print(a);

	arr_free(a);


	system("pause");
	return 0;
}