/*
 * DWN - Desktop Window Manager
 * Abstract List Implementation (Dynamic Array)
 */

#include "core/wm_list.h"
#include "core/wm_types.h"

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

/*==============================================================================
 * List Structure
 *============================================================================*/

struct WmList {
    void **data;
    size_t size;
    size_t capacity;
    WmFreeFunc free_func;
};

/*==============================================================================
 * Lifecycle
 *============================================================================*/

WmList* wm_list_new(void) {
    WmList *list = calloc(1, sizeof(WmList));
    if (!list) return NULL;
    
    list->capacity = 8;
    list->data = malloc(list->capacity * sizeof(void*));
    if (!list->data) {
        free(list);
        return NULL;
    }
    return list;
}

WmList* wm_list_new_sized(size_t initial_capacity) {
    WmList *list = calloc(1, sizeof(WmList));
    if (!list) return NULL;
    
    list->capacity = initial_capacity > 0 ? initial_capacity : 8;
    list->data = malloc(list->capacity * sizeof(void*));
    if (!list->data) {
        free(list);
        return NULL;
    }
    return list;
}

void wm_list_destroy(WmList *list) {
    if (!list) return;
    
    if (list->free_func) {
        for (size_t i = 0; i < list->size; i++) {
            list->free_func(list->data[i]);
        }
    }
    
    free(list->data);
    free(list);
}

void wm_list_destroy_no_free(WmList *list) {
    if (!list) return;
    free(list->data);
    free(list);
}

WmList* wm_list_clone(const WmList *list) {
    if (!list) return NULL;
    
    WmList *clone = wm_list_new_sized(list->capacity);
    if (!clone) return NULL;
    
    memcpy(clone->data, list->data, list->size * sizeof(void*));
    clone->size = list->size;
    clone->free_func = list->free_func;
    return clone;
}

void wm_list_set_free_func(WmList *list, WmFreeFunc func) {
    if (list) list->free_func = func;
}

/*==============================================================================
 * Capacity
 *============================================================================*/

static bool wm_list_grow(WmList *list, size_t min_capacity) {
    if (list->capacity >= min_capacity) return true;
    
    size_t new_capacity = list->capacity * 2;
    if (new_capacity < min_capacity) new_capacity = min_capacity;
    
    void **new_data = realloc(list->data, new_capacity * sizeof(void*));
    if (!new_data) return false;
    
    list->data = new_data;
    list->capacity = new_capacity;
    return true;
}

size_t wm_list_size(const WmList *list) {
    return list ? list->size : 0;
}

size_t wm_list_capacity(const WmList *list) {
    return list ? list->capacity : 0;
}

bool wm_list_is_empty(const WmList *list) {
    return !list || list->size == 0;
}

void wm_list_reserve(WmList *list, size_t capacity) {
    if (!list) return;
    wm_list_grow(list, capacity);
}

void wm_list_compact(WmList *list) {
    if (!list || list->capacity <= list->size) return;
    
    size_t new_capacity = list->size > 0 ? list->size : 1;
    void **new_data = realloc(list->data, new_capacity * sizeof(void*));
    if (new_data) {
        list->data = new_data;
        list->capacity = new_capacity;
    }
}

void wm_list_clear(WmList *list) {
    if (!list) return;
    
    if (list->free_func) {
        for (size_t i = 0; i < list->size; i++) {
            list->free_func(list->data[i]);
        }
    }
    list->size = 0;
}

/*==============================================================================
 * Modifiers
 *============================================================================*/

void wm_list_append(WmList *list, void *item) {
    if (!list) return;
    
    if (!wm_list_grow(list, list->size + 1)) return;
    list->data[list->size++] = item;
}

void wm_list_prepend(WmList *list, void *item) {
    wm_list_insert(list, 0, item);
}

void wm_list_insert(WmList *list, size_t index, void *item) {
    if (!list || index > list->size) return;
    
    if (!wm_list_grow(list, list->size + 1)) return;
    
    memmove(&list->data[index + 1], &list->data[index], 
            (list->size - index) * sizeof(void*));
    list->data[index] = item;
    list->size++;
}

void wm_list_insert_sorted(WmList *list, void *item, WmCompareFunc compare) {
    if (!list) return;
    
    size_t i = 0;
    while (i < list->size && compare(list->data[i], item) < 0) {
        i++;
    }
    wm_list_insert(list, i, item);
}

void wm_list_remove(WmList *list, void *item) {
    if (!list) return;
    
    for (size_t i = 0; i < list->size; i++) {
        if (list->data[i] == item) {
            wm_list_remove_at(list, i);
            return;
        }
    }
}

void wm_list_remove_at(WmList *list, size_t index) {
    if (!list || index >= list->size) return;
    
    if (list->free_func) {
        list->free_func(list->data[index]);
    }
    
    memmove(&list->data[index], &list->data[index + 1],
            (list->size - index - 1) * sizeof(void*));
    list->size--;
}

void* wm_list_take_at(WmList *list, size_t index) {
    if (!list || index >= list->size) return NULL;
    
    void *item = list->data[index];
    memmove(&list->data[index], &list->data[index + 1],
            (list->size - index - 1) * sizeof(void*));
    list->size--;
    return item;
}

bool wm_list_remove_one(WmList *list, void *item) {
    if (!list) return false;
    
    for (size_t i = 0; i < list->size; i++) {
        if (list->data[i] == item) {
            wm_list_remove_at(list, i);
            return true;
        }
    }
    return false;
}

bool wm_list_remove_all(WmList *list, void *item) {
    if (!list) return false;
    
    bool removed = false;
    for (size_t i = list->size; i-- > 0; ) {
        if (list->data[i] == item) {
            wm_list_remove_at(list, i);
            removed = true;
        }
    }
    return removed;
}

void* wm_list_pop_back(WmList *list) {
    if (!list || list->size == 0) return NULL;
    return list->data[--list->size];
}

void* wm_list_pop_front(WmList *list) {
    return wm_list_take_at(list, 0);
}

/*==============================================================================
 * Accessors
 *============================================================================*/

void* wm_list_get(const WmList *list, size_t index) {
    if (!list || index >= list->size) return NULL;
    return list->data[index];
}

void* wm_list_first(const WmList *list) {
    if (!list || list->size == 0) return NULL;
    return list->data[0];
}

void* wm_list_last(const WmList *list) {
    if (!list || list->size == 0) return NULL;
    return list->data[list->size - 1];
}

void* wm_list_front(const WmList *list) {
    return wm_list_first(list);
}

void* wm_list_back(const WmList *list) {
    return wm_list_last(list);
}

void wm_list_set(WmList *list, size_t index, void *item) {
    if (!list || index >= list->size) return;
    
    if (list->free_func) {
        list->free_func(list->data[index]);
    }
    list->data[index] = item;
}

/*==============================================================================
 * Iteration
 *============================================================================*/

void wm_list_foreach(const WmList *list, WmForeachFunc func, void *user_data) {
    if (!list || !func) return;
    
    for (size_t i = 0; i < list->size; i++) {
        if (!func(list->data[i], i, user_data)) break;
    }
}

void wm_list_foreach_reverse(const WmList *list, WmForeachFunc func, void *user_data) {
    if (!list || !func) return;
    
    for (size_t i = list->size; i-- > 0; ) {
        if (!func(list->data[i], i, user_data)) break;
    }
}

/*==============================================================================
 * Search
 *============================================================================*/

ssize_t wm_list_index_of(const WmList *list, void *item) {
    if (!list) return -1;
    
    for (size_t i = 0; i < list->size; i++) {
        if (list->data[i] == item) return (ssize_t)i;
    }
    return -1;
}

bool wm_list_contains(const WmList *list, void *item) {
    return wm_list_index_of(list, item) >= 0;
}

void* wm_list_find(const WmList *list, WmCompareFunc compare, const void *key) {
    if (!list || !compare) return NULL;
    
    for (size_t i = 0; i < list->size; i++) {
        if (compare(list->data[i], key) == 0) return list->data[i];
    }
    return NULL;
}

ssize_t wm_list_find_index(const WmList *list, WmCompareFunc compare, const void *key) {
    if (!list || !compare) return -1;
    
    for (size_t i = 0; i < list->size; i++) {
        if (compare(list->data[i], key) == 0) return (ssize_t)i;
    }
    return -1;
}

/*==============================================================================
 * Sorting
 *============================================================================*/

static void wm_list_qsort_helper(void **data, size_t left, size_t right, 
                                  WmCompareFunc compare) {
    if (left >= right) return;
    
    void *pivot = data[(left + right) / 2];
    size_t i = left, j = right;
    
    while (i <= j) {
        while (compare(data[i], pivot) < 0) i++;
        while (compare(data[j], pivot) > 0) j--;
        if (i <= j) {
            void *tmp = data[i];
            data[i] = data[j];
            data[j] = tmp;
            i++;
            if (j > 0) j--;
        }
    }
    
    if (j > left) wm_list_qsort_helper(data, left, j, compare);
    if (i < right) wm_list_qsort_helper(data, i, right, compare);
}

void wm_list_sort(WmList *list, WmCompareFunc compare) {
    if (!list || list->size < 2) return;
    wm_list_qsort_helper(list->data, 0, list->size - 1, compare);
}

/*==============================================================================
 * Data Operations
 *============================================================================*/

void** wm_list_data(WmList *list) {
    return list ? list->data : NULL;
}

void* wm_list_steal(WmList *list, size_t index) {
    return wm_list_take_at(list, index);
}

WmList* wm_list_slice(const WmList *list, size_t start, size_t end) {
    if (!list || start >= list->size || start >= end) return wm_list_new();
    if (end > list->size) end = list->size;
    
    size_t count = end - start;
    WmList *slice = wm_list_new_sized(count);
    if (!slice) return NULL;
    
    memcpy(slice->data, &list->data[start], count * sizeof(void*));
    slice->size = count;
    return slice;
}

void wm_list_move(WmList *list, size_t from, size_t to) {
    if (!list || from >= list->size || to >= list->size || from == to) return;
    
    void *item = list->data[from];
    if (from < to) {
        memmove(&list->data[from], &list->data[from + 1],
                (to - from) * sizeof(void*));
    } else {
        memmove(&list->data[to + 1], &list->data[to],
                (from - to) * sizeof(void*));
    }
    list->data[to] = item;
}

void wm_list_swap(WmList *list, size_t i, size_t j) {
    if (!list || i >= list->size || j >= list->size) return;
    
    void *tmp = list->data[i];
    list->data[i] = list->data[j];
    list->data[j] = tmp;
}

/*==============================================================================
 * Comparison
 *============================================================================*/

bool wm_list_equals(const WmList *list1, const WmList *list2) {
    if (list1 == list2) return true;
    if (!list1 || !list2) return false;
    if (list1->size != list2->size) return false;
    
    for (size_t i = 0; i < list1->size; i++) {
        if (list1->data[i] != list2->data[i]) return false;
    }
    return true;
}
