/*
 * DWN - Desktop Window Manager
 * Abstract String Implementation
 */

#define _GNU_SOURCE

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>

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

/*==============================================================================
 * String Structure
 *============================================================================*/

struct WmString {
    char *data;
    size_t length;
    size_t capacity;
};

/*==============================================================================
 * Internal Helpers
 *============================================================================*/

static bool wm_string_grow(WmString *str, size_t min_capacity) {
    if (str->capacity >= min_capacity) return true;
    
    size_t new_capacity = str->capacity * 2;
    if (new_capacity < 16) new_capacity = 16;
    if (new_capacity < min_capacity) new_capacity = min_capacity;
    
    char *new_data = realloc(str->data, new_capacity);
    if (!new_data) return false;
    
    str->data = new_data;
    str->capacity = new_capacity;
    return true;
}

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

WmString* wm_string_new(const char *str) {
    if (!str) return wm_string_new_empty();
    return wm_string_new_n(str, strlen(str));
}

WmString* wm_string_new_empty(void) {
    WmString *s = calloc(1, sizeof(WmString));
    if (!s) return NULL;
    
    s->data = malloc(1);
    if (!s->data) {
        free(s);
        return NULL;
    }
    s->data[0] = '\0';
    s->length = 0;
    s->capacity = 1;
    return s;
}

WmString* wm_string_new_sized(size_t capacity) {
    WmString *s = calloc(1, sizeof(WmString));
    if (!s) return NULL;
    
    s->data = malloc(capacity + 1);
    if (!s->data) {
        free(s);
        return NULL;
    }
    s->data[0] = '\0';
    s->length = 0;
    s->capacity = capacity + 1;
    return s;
}

WmString* wm_string_new_printf(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    WmString *s = wm_string_new_vprintf(fmt, args);
    va_end(args);
    return s;
}

WmString* wm_string_new_vprintf(const char *fmt, va_list args) {
    va_list args_copy;
    va_copy(args_copy, args);
    int len = vsnprintf(NULL, 0, fmt, args_copy);
    va_end(args_copy);
    
    if (len < 0) return NULL;
    
    WmString *s = wm_string_new_sized(len + 1);
    if (!s) return NULL;
    
    vsnprintf(s->data, s->capacity, fmt, args);
    s->length = len;
    return s;
}

WmString* wm_string_new_n(const char *str, size_t n) {
    if (!str) return wm_string_new_empty();
    
    WmString *s = calloc(1, sizeof(WmString));
    if (!s) return NULL;
    
    s->data = malloc(n + 1);
    if (!s->data) {
        free(s);
        return NULL;
    }
    
    memcpy(s->data, str, n);
    s->data[n] = '\0';
    s->length = n;
    s->capacity = n + 1;
    return s;
}

void wm_string_destroy(WmString *str) {
    if (!str) return;
    free(str->data);
    free(str);
}

WmString* wm_string_clone(const WmString *str) {
    if (!str) return NULL;
    return wm_string_new_n(str->data, str->length);
}

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

void wm_string_append(WmString *str, const char *suffix) {
    if (!str || !suffix) return;
    wm_string_append_n(str, suffix, strlen(suffix));
}

void wm_string_append_char(WmString *str, char c) {
    if (!str) return;
    if (!wm_string_grow(str, str->length + 2)) return;
    
    str->data[str->length++] = c;
    str->data[str->length] = '\0';
}

void wm_string_append_n(WmString *str, const char *suffix, size_t n) {
    if (!str || !suffix || n == 0) return;
    
    if (!wm_string_grow(str, str->length + n + 1)) return;
    
    memcpy(str->data + str->length, suffix, n);
    str->length += n;
    str->data[str->length] = '\0';
}

void wm_string_append_printf(WmString *str, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    wm_string_append_vprintf(str, fmt, args);
    va_end(args);
}

void wm_string_append_vprintf(WmString *str, const char *fmt, va_list args) {
    if (!str || !fmt) return;
    
    va_list args_copy;
    va_copy(args_copy, args);
    int len = vsnprintf(NULL, 0, fmt, args_copy);
    va_end(args_copy);
    
    if (len < 0) return;
    
    if (!wm_string_grow(str, str->length + len + 1)) return;
    
    vsnprintf(str->data + str->length, str->capacity - str->length, fmt, args);
    str->length += len;
}

void wm_string_append_string(WmString *str, const WmString *suffix) {
    if (!suffix) return;
    wm_string_append_n(str, suffix->data, suffix->length);
}

void wm_string_prepend(WmString *str, const char *prefix) {
    if (!str || !prefix) return;
    
    size_t prefix_len = strlen(prefix);
    if (prefix_len == 0) return;
    
    if (!wm_string_grow(str, str->length + prefix_len + 1)) return;
    
    memmove(str->data + prefix_len, str->data, str->length + 1);
    memcpy(str->data, prefix, prefix_len);
    str->length += prefix_len;
}

void wm_string_prepend_char(WmString *str, char c) {
    if (!str) return;
    
    if (!wm_string_grow(str, str->length + 2)) return;
    
    memmove(str->data + 1, str->data, str->length + 1);
    str->data[0] = c;
    str->length++;
}

void wm_string_insert(WmString *str, size_t pos, const char *insert) {
    if (!str || !insert || pos > str->length) return;
    
    size_t insert_len = strlen(insert);
    if (insert_len == 0) return;
    
    if (!wm_string_grow(str, str->length + insert_len + 1)) return;
    
    memmove(str->data + pos + insert_len, str->data + pos, str->length - pos + 1);
    memcpy(str->data + pos, insert, insert_len);
    str->length += insert_len;
}

void wm_string_insert_char(WmString *str, size_t pos, char c) {
    if (!str || pos > str->length) return;
    
    if (!wm_string_grow(str, str->length + 2)) return;
    
    memmove(str->data + pos + 1, str->data + pos, str->length - pos + 1);
    str->data[pos] = c;
    str->length++;
}

void wm_string_insert_string(WmString *str, size_t pos, const WmString *insert) {
    if (!insert) return;
    wm_string_insert(str, pos, insert->data);
}

void wm_string_erase(WmString *str, size_t pos, size_t len) {
    if (!str || pos >= str->length) return;
    
    if (len > str->length - pos) len = str->length - pos;
    
    memmove(str->data + pos, str->data + pos + len, str->length - pos - len + 1);
    str->length -= len;
}

void wm_string_clear(WmString *str) {
    if (!str) return;
    str->length = 0;
    str->data[0] = '\0';
}

/*==============================================================================
 * Queries
 *============================================================================*/

const char* wm_string_cstr(const WmString *str) {
    return str ? str->data : "";
}

char* wm_string_detach(WmString *str) {
    if (!str) return NULL;
    char *data = str->data;
    free(str);
    return data;
}

size_t wm_string_length(const WmString *str) {
    return str ? str->length : 0;
}

size_t wm_string_capacity(const WmString *str) {
    return str ? str->capacity : 0;
}

size_t wm_string_bytes(const WmString *str) {
    return str ? str->length + 1 : 0;
}

bool wm_string_is_empty(const WmString *str) {
    return !str || str->length == 0;
}

bool wm_string_is_null(const WmString *str) {
    return str == NULL;
}

char wm_string_char_at(const WmString *str, size_t pos) {
    if (!str || pos >= str->length) return '\0';
    return str->data[pos];
}

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

bool wm_string_equals(const WmString *str1, const WmString *str2) {
    if (str1 == str2) return true;
    if (!str1 || !str2) return false;
    if (str1->length != str2->length) return false;
    return memcmp(str1->data, str2->data, str1->length) == 0;
}

bool wm_string_equals_cstr(const WmString *str, const char *cstr) {
    if (!str) return !cstr || cstr[0] == '\0';
    if (!cstr) return str->length == 0;
    return strcmp(str->data, cstr) == 0;
}

int wm_string_compare(const WmString *str1, const WmString *str2) {
    if (str1 == str2) return 0;
    if (!str1) return -1;
    if (!str2) return 1;
    return strcmp(str1->data, str2->data);
}

int wm_string_compare_cstr(const WmString *str, const char *cstr) {
    if (!str) return cstr ? -1 : 0;
    if (!cstr) return 1;
    return strcmp(str->data, cstr);
}

int wm_string_case_compare(const WmString *str1, const WmString *str2) {
    if (str1 == str2) return 0;
    if (!str1) return -1;
    if (!str2) return 1;
    return strcasecmp(str1->data, str2->data);
}

int wm_string_case_compare_cstr(const WmString *str, const char *cstr) {
    if (!str) return cstr ? -1 : 0;
    if (!cstr) return 1;
    return strcasecmp(str->data, cstr);
}

/*==============================================================================
 * Searching
 *============================================================================*/

bool wm_string_contains(const WmString *str, const char *needle) {
    if (!str || !needle) return false;
    return strstr(str->data, needle) != NULL;
}

bool wm_string_contains_char(const WmString *str, char c) {
    if (!str) return false;
    return strchr(str->data, c) != NULL;
}

size_t wm_string_find(const WmString *str, const char *needle, size_t start) {
    if (!str || !needle || start > str->length) return (size_t)-1;
    const char *found = strstr(str->data + start, needle);
    return found ? (size_t)(found - str->data) : (size_t)-1;
}

size_t wm_string_find_char(const WmString *str, char c, size_t start) {
    if (!str || start > str->length) return (size_t)-1;
    const char *found = strchr(str->data + start, c);
    return found ? (size_t)(found - str->data) : (size_t)-1;
}

size_t wm_string_find_last(const WmString *str, const char *needle) {
    if (!str || !needle) return (size_t)-1;
    
    /* Simple implementation since strrstr is not standard */
    size_t needle_len = strlen(needle);
    if (needle_len == 0) return str->length;
    
    for (size_t i = str->length - needle_len + 1; i-- > 0; ) {
        if (memcmp(str->data + i, needle, needle_len) == 0) {
            return i;
        }
    }
    return (size_t)-1;
}

size_t wm_string_find_last_char(const WmString *str, char c) {
    if (!str) return (size_t)-1;
    const char *found = strrchr(str->data, c);
    return found ? (size_t)(found - str->data) : (size_t)-1;
}

bool wm_string_starts_with(const WmString *str, const char *prefix) {
    if (!str || !prefix) return false;
    size_t prefix_len = strlen(prefix);
    if (prefix_len > str->length) return false;
    return strncmp(str->data, prefix, prefix_len) == 0;
}

bool wm_string_starts_with_char(const WmString *str, char c) {
    return str && str->length > 0 && str->data[0] == c;
}

bool wm_string_ends_with(const WmString *str, const char *suffix) {
    if (!str || !suffix) return false;
    size_t suffix_len = strlen(suffix);
    if (suffix_len > str->length) return false;
    return strncmp(str->data + str->length - suffix_len, suffix, suffix_len) == 0;
}

bool wm_string_ends_with_char(const WmString *str, char c) {
    return str && str->length > 0 && str->data[str->length - 1] == c;
}

/*==============================================================================
 * Modification
 *============================================================================*/

void wm_string_trim(WmString *str) {
    wm_string_trim_left(str);
    wm_string_trim_right(str);
}

void wm_string_trim_left(WmString *str) {
    if (!str) return;
    size_t i = 0;
    while (i < str->length && isspace((unsigned char)str->data[i])) i++;
    if (i > 0) {
        memmove(str->data, str->data + i, str->length - i + 1);
        str->length -= i;
    }
}

void wm_string_trim_right(WmString *str) {
    if (!str) return;
    while (str->length > 0 && isspace((unsigned char)str->data[str->length - 1])) {
        str->data[--str->length] = '\0';
    }
}

void wm_string_replace(WmString *str, const char *search, const char *replace) {
    if (!str || !search || !replace) return;
    size_t count = wm_string_replace_all(str, search, replace);
    (void)count;
}

void wm_string_replace_char(WmString *str, char search, char replace) {
    if (!str) return;
    for (size_t i = 0; i < str->length; i++) {
        if (str->data[i] == search) str->data[i] = replace;
    }
}

size_t wm_string_replace_all(WmString *str, const char *search, const char *replace) {
    if (!str || !search || !replace) return 0;
    
    size_t search_len = strlen(search);
    size_t replace_len = strlen(replace);
    size_t count = 0;
    
    char *pos = str->data;
    while ((pos = strstr(pos, search)) != NULL) {
        count++;
        pos += search_len;
    }
    
    if (count == 0) return 0;
    
    size_t new_length = str->length + count * (replace_len - search_len);
    if (!wm_string_grow(str, new_length + 1)) return 0;
    
    WmString *result = wm_string_new_sized(new_length);
    if (!result) return 0;
    
    char *src = str->data;
    char *dst = result->data;
    
    while ((pos = strstr(src, search)) != NULL) {
        size_t prefix_len = pos - src;
        memcpy(dst, src, prefix_len);
        dst += prefix_len;
        memcpy(dst, replace, replace_len);
        dst += replace_len;
        src = pos + search_len;
    }
    
    size_t remaining = new_length - (size_t)(dst - result->data);
    strncpy(dst, src, remaining);
    dst[remaining] = '\0';
    result->length = new_length;
    
    free(str->data);
    str->data = result->data;
    str->length = result->length;
    str->capacity = result->capacity;
    free(result);
    
    return count;
}

void wm_string_to_lower(WmString *str) {
    if (!str) return;
    for (size_t i = 0; i < str->length; i++) {
        str->data[i] = tolower((unsigned char)str->data[i]);
    }
}

void wm_string_to_upper(WmString *str) {
    if (!str) return;
    for (size_t i = 0; i < str->length; i++) {
        str->data[i] = toupper((unsigned char)str->data[i]);
    }
}

void wm_string_reverse(WmString *str) {
    if (!str) return;
    for (size_t i = 0, j = str->length - 1; i < j; i++, j--) {
        char tmp = str->data[i];
        str->data[i] = str->data[j];
        str->data[j] = tmp;
    }
}

/*==============================================================================
 * Substrings
 *============================================================================*/

WmString* wm_string_substring(const WmString *str, size_t start, size_t len) {
    if (!str || start > str->length) return wm_string_new_empty();
    if (start + len > str->length) len = str->length - start;
    return wm_string_new_n(str->data + start, len);
}

WmString* wm_string_left(const WmString *str, size_t n) {
    if (!str) return wm_string_new_empty();
    if (n > str->length) n = str->length;
    return wm_string_new_n(str->data, n);
}

WmString* wm_string_right(const WmString *str, size_t n) {
    if (!str) return wm_string_new_empty();
    if (n > str->length) n = str->length;
    return wm_string_new_n(str->data + str->length - n, n);
}

/*==============================================================================
 * Formatting
 *============================================================================*/

void wm_string_printf(WmString *str, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    wm_string_clear(str);
    wm_string_append_vprintf(str, fmt, args);
    va_end(args);
}

void wm_string_vprintf(WmString *str, const char *fmt, va_list args) {
    wm_string_clear(str);
    wm_string_append_vprintf(str, fmt, args);
}

/*==============================================================================
 * Conversion
 *============================================================================*/

int wm_string_to_int(const WmString *str, int default_val) {
    if (!str || str->length == 0) return default_val;
    char *end;
    long val = strtol(str->data, &end, 10);
    if (end == str->data) return default_val;
    return (int)val;
}

long wm_string_to_long(const WmString *str, long default_val) {
    if (!str || str->length == 0) return default_val;
    char *end;
    long val = strtol(str->data, &end, 10);
    if (end == str->data) return default_val;
    return val;
}

float wm_string_to_float(const WmString *str, float default_val) {
    if (!str || str->length == 0) return default_val;
    char *end;
    float val = strtof(str->data, &end);
    if (end == str->data) return default_val;
    return val;
}

double wm_string_to_double(const WmString *str, double default_val) {
    if (!str || str->length == 0) return default_val;
    char *end;
    double val = strtod(str->data, &end);
    if (end == str->data) return default_val;
    return val;
}

bool wm_string_to_bool(const WmString *str, bool default_val) {
    if (!str || str->length == 0) return default_val;
    if (strcasecmp(str->data, "true") == 0 || 
        strcmp(str->data, "1") == 0 ||
        strcasecmp(str->data, "yes") == 0) {
        return true;
    }
    if (strcasecmp(str->data, "false") == 0 || 
        strcmp(str->data, "0") == 0 ||
        strcasecmp(str->data, "no") == 0) {
        return false;
    }
    return default_val;
}

WmString* wm_string_from_int(int val) {
    return wm_string_new_printf("%d", val);
}

WmString* wm_string_from_long(long val) {
    return wm_string_new_printf("%ld", val);
}

WmString* wm_string_from_float(float val, int precision) {
    return wm_string_new_printf("%.*f", precision, val);
}

WmString* wm_string_from_double(double val, int precision) {
    return wm_string_new_printf("%.*lf", precision, val);
}

WmString* wm_string_from_bool(bool val) {
    return wm_string_new(val ? "true" : "false");
}

/*==============================================================================
 * Hash
 *============================================================================*/

uint32_t wm_string_hash(const WmString *str) {
    if (!str) return 0;
    return wm_string_hash_cstr(str->data);
}

uint32_t wm_string_hash_cstr(const char *str) {
    if (!str) return 0;
    /* FNV-1a hash */
    uint32_t hash = 2166136261u;
    while (*str) {
        hash ^= (uint8_t)*str++;
        hash *= 16777619u;
    }
    return hash;
}
