/************************************************************/ /**
*
*	@file: strings.c
*	@author: Martin Fouilleul
*	@date: 29/05/2021
*	@revision:
*
*****************************************************************/
#include "strings.h"
#include "platform/platform_debug.h"

//----------------------------------------------------------------------------------
// string slices as values
//----------------------------------------------------------------------------------

oc_str8 oc_str8_from_buffer(u64 len, char* buffer)
{
    return ((oc_str8){ .len = len, .ptr = buffer });
}

oc_str8 oc_str8_slice(oc_str8 s, u64 start, u64 end)
{
    OC_ASSERT(start <= end && start <= s.len && end <= s.len);
    return ((oc_str8){ .len = end - start, .ptr = s.ptr + start });
}

oc_str8 oc_str8_push_buffer(oc_arena* arena, u64 len, char* buffer)
{
    oc_str8 str = { 0 };
    str.len = len;
    str.ptr = oc_arena_push_array(arena, char, len + 1);
    memcpy(str.ptr, buffer, len);
    str.ptr[str.len] = '\0';
    return (str);
}

oc_str8 oc_str8_push_cstring(oc_arena* arena, const char* str)
{
    int len = 0;
    if(str)
    {
        len = strlen(str);
    }
    return (oc_str8_push_buffer(arena, strlen(str), (char*)str));
}

oc_str8 oc_str8_push_copy(oc_arena* arena, oc_str8 s)
{
    return (oc_str8_push_buffer(arena, oc_str8_lp(s)));
}

char* oc_str8_to_cstring(oc_arena* arena, oc_str8 string)
{
    //NOTE: forward to push_copy, which null-terminates the copy
    string = oc_str8_push_copy(arena, string);
    return (string.ptr);
}

oc_str8 oc_str8_push_slice(oc_arena* arena, oc_str8 s, u64 start, u64 end)
{
    oc_str8 slice = oc_str8_slice(s, start, end);
    return (oc_str8_push_copy(arena, slice));
}

oc_str8 oc_str8_pushfv(oc_arena* arena, const char* format, va_list args)
{
    //NOTE(martin):
    //	We first compute the number of characters to write passing a size of 0.
    //  then we allocate len+1 (since vsnprint always terminates with a '\0').

    char dummy;
    oc_str8 str = { 0 };
    va_list argCopy;
    va_copy(argCopy, args);
    str.len = vsnprintf(&dummy, 0, format, argCopy);
    va_end(argCopy);

    str.ptr = oc_arena_push_array(arena, char, str.len + 1);
    vsnprintf((char*)str.ptr, str.len + 1, format, args);
    return (str);
}

oc_str8 oc_str8_pushf(oc_arena* arena, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    oc_str8 str = oc_str8_pushfv(arena, format, args);
    va_end(args);
    return (str);
}

int oc_str8_cmp(oc_str8 s1, oc_str8 s2)
{
    int res = strncmp(s1.ptr, s2.ptr, oc_min(s1.len, s2.len));
    if(!res)
    {
        res = (s1.len < s2.len) ? -1 : ((s1.len == s2.len) ? 0 : 1);
    }
    return (res);
}

//----------------------------------------------------------------------------------
// string lists
//----------------------------------------------------------------------------------

void oc_str8_list_init(oc_str8_list* list)
{
    oc_list_init(&list->list);
    list->eltCount = 0;
    list->len = 0;
}

void oc_str8_list_push(oc_arena* arena, oc_str8_list* list, oc_str8 str)
{
    oc_str8_elt* elt = oc_arena_push_type(arena, oc_str8_elt);
    elt->string = str;
    oc_list_append(&list->list, &elt->listElt);
    list->eltCount++;
    list->len += str.len;
}

void oc_str8_list_pushf(oc_arena* arena, oc_str8_list* list, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    oc_str8 str = oc_str8_pushfv(arena, format, args);
    va_end(args);
    oc_str8_list_push(arena, list, str);
}

oc_str8 oc_str8_list_collate(oc_arena* arena, oc_str8_list list, oc_str8 prefix, oc_str8 separator, oc_str8 postfix)
{
    oc_str8 str = { 0 };
    str.len = prefix.len + list.len + list.eltCount * separator.len + postfix.len;
    str.ptr = oc_arena_push_array(arena, char, str.len + 1);
    char* dst = str.ptr;
    memcpy(dst, prefix.ptr, prefix.len);
    dst += prefix.len;

    oc_str8_elt* elt = oc_list_first_entry(&list.list, oc_str8_elt, listElt);
    if(elt)
    {
        memcpy(dst, elt->string.ptr, elt->string.len);
        dst += elt->string.len;
        elt = oc_list_next_entry(&list.list, elt, oc_str8_elt, listElt);
    }

    for(; elt != 0; elt = oc_list_next_entry(&list.list, elt, oc_str8_elt, listElt))
    {
        memcpy(dst, separator.ptr, separator.len);
        dst += separator.len;
        memcpy(dst, elt->string.ptr, elt->string.len);
        dst += elt->string.len;
    }
    memcpy(dst, postfix.ptr, postfix.len);
    str.ptr[str.len] = '\0';
    return (str);
}

oc_str8 oc_str8_list_join(oc_arena* arena, oc_str8_list list)
{
    oc_str8 empty = { .len = 0, .ptr = 0 };
    return (oc_str8_list_collate(arena, list, empty, empty, empty));
}

oc_str8_list oc_str8_split(oc_arena* arena, oc_str8 str, oc_str8_list separators)
{
    oc_str8_list list = { 0 };
    oc_list_init(&list.list);

    char* ptr = str.ptr;
    char* end = str.ptr + str.len;
    char* subStart = ptr;
    for(; ptr < end; ptr++)
    {
        //NOTE(martin): search all separators and try to match them to the current ptr
        oc_str8* foundSep = 0;
        oc_list_for(&separators.list, elt, oc_str8_elt, listElt)
        {
            oc_str8* separator = &elt->string;
            bool equal = true;
            for(u64 offset = 0;
                (offset < separator->len) && (ptr + offset < end);
                offset++)
            {
                if(separator->ptr[offset] != ptr[offset])
                {
                    equal = false;
                    break;
                }
            }
            if(equal)
            {
                foundSep = separator;
                break;
            }
        }
        if(foundSep)
        {
            //NOTE(martin): we found a separator. If the start of the current substring is != ptr,
            //              the current substring is not empty and we emit the substring
            if(ptr != subStart)
            {
                oc_str8 sub = oc_str8_from_buffer(ptr - subStart, subStart);
                oc_str8_list_push(arena, &list, sub);
            }
            ptr += foundSep->len - 1; //NOTE(martin): ptr is incremented at the end of the loop
            subStart = ptr + 1;
        }
    }
    //NOTE(martin): emit the last substring
    if(ptr != subStart)
    {
        oc_str8 sub = oc_str8_from_buffer(ptr - subStart, subStart);
        oc_str8_list_push(arena, &list, sub);
    }
    return (list);
}

//----------------------------------------------------------------------------------
// u16 strings
//----------------------------------------------------------------------------------
oc_str16 oc_str16_from_buffer(u64 len, u16* buffer)
{
    return ((oc_str16){ .len = len, .ptr = buffer });
}

oc_str16 oc_str16_slice(oc_str16 s, u64 start, u64 end)
{
    OC_ASSERT(start <= end && start <= s.len && end <= s.len);
    return ((oc_str16){ .len = end - start, .ptr = s.ptr + start });
}

oc_str16 oc_str16_push_buffer(oc_arena* arena, u64 len, u16* buffer)
{
    oc_str16 str = { 0 };
    str.len = len;
    str.ptr = oc_arena_push_array(arena, u16, len + 1);
    memcpy(str.ptr, buffer, len * sizeof(u16));
    str.ptr[str.len] = (u16)0;
    return (str);
}

oc_str16 oc_str16_push_copy(oc_arena* arena, oc_str16 s)
{
    return (oc_str16_push_buffer(arena, s.len, s.ptr));
}

oc_str16 oc_str16_push_slice(oc_arena* arena, oc_str16 s, u64 start, u64 end)
{
    oc_str16 slice = oc_str16_slice(s, start, end);
    return (oc_str16_push_copy(arena, slice));
}

void oc_str16_list_init(oc_str16_list* list)
{
    oc_list_init(&list->list);
    list->eltCount = 0;
    list->len = 0;
}

void oc_str16_list_push(oc_arena* arena, oc_str16_list* list, oc_str16 str)
{
    oc_str16_elt* elt = oc_arena_push_type(arena, oc_str16_elt);
    elt->string = str;
    oc_list_append(&list->list, &elt->listElt);
    list->eltCount++;
    list->len += str.len;
}

oc_str16 oc_str16_list_collate(oc_arena* arena, oc_str16_list list, oc_str16 prefix, oc_str16 separator, oc_str16 postfix)
{
    oc_str16 str = { 0 };
    str.len = prefix.len + list.len + list.eltCount * separator.len + postfix.len;
    str.ptr = oc_arena_push_array(arena, u16, str.len + 1);
    char* dst = (char*)str.ptr;
    memcpy(dst, prefix.ptr, prefix.len * sizeof(u16));
    dst += prefix.len * sizeof(u16);

    oc_str16_elt* elt = oc_list_first_entry(&list.list, oc_str16_elt, listElt);
    if(elt)
    {
        memcpy(dst, elt->string.ptr, elt->string.len * sizeof(u16));
        dst += elt->string.len * sizeof(u16);
        elt = oc_list_next_entry(&list.list, elt, oc_str16_elt, listElt);
    }

    for(; elt != 0; elt = oc_list_next_entry(&list.list, elt, oc_str16_elt, listElt))
    {
        memcpy(dst, separator.ptr, separator.len * sizeof(u16));
        dst += separator.len * sizeof(u16);
        memcpy(dst, elt->string.ptr, elt->string.len * sizeof(u16));
        dst += elt->string.len * sizeof(u16);
    }
    memcpy(dst, postfix.ptr, postfix.len * sizeof(u16));
    str.ptr[str.len] = (u16)0;
    return (str);
}

oc_str16 oc_str16_list_join(oc_arena* arena, oc_str16_list list)
{
    oc_str16 empty = { .len = 0, .ptr = 0 };
    return (oc_str16_list_collate(arena, list, empty, empty, empty));
}

//----------------------------------------------------------------------------------
// u32 strings
//----------------------------------------------------------------------------------
oc_str32 oc_str32_from_buffer(u64 len, u32* buffer)
{
    return ((oc_str32){ .len = len, .ptr = buffer });
}

oc_str32 oc_str32_slice(oc_str32 s, u64 start, u64 end)
{
    OC_ASSERT(start <= end && start <= s.len && end <= s.len);
    return ((oc_str32){ .len = end - start, .ptr = s.ptr + start });
}

oc_str32 oc_str32_push_buffer(oc_arena* arena, u64 len, u32* buffer)
{
    oc_str32 str = { 0 };
    str.len = len;
    str.ptr = oc_arena_push_array(arena, u32, len + 1);
    memcpy(str.ptr, buffer, len * sizeof(u32));
    str.ptr[str.len] = 0;
    return (str);
}

oc_str32 oc_str32_push_copy(oc_arena* arena, oc_str32 s)
{
    return (oc_str32_push_buffer(arena, s.len, s.ptr));
}

oc_str32 oc_str32_push_slice(oc_arena* arena, oc_str32 s, u64 start, u64 end)
{
    oc_str32 slice = oc_str32_slice(s, start, end);
    return (oc_str32_push_copy(arena, slice));
}

void oc_str32_list_init(oc_str32_list* list)
{
    oc_list_init(&list->list);
    list->eltCount = 0;
    list->len = 0;
}

void oc_str32_list_push(oc_arena* arena, oc_str32_list* list, oc_str32 str)
{
    oc_str32_elt* elt = oc_arena_push_type(arena, oc_str32_elt);
    elt->string = str;
    oc_list_append(&list->list, &elt->listElt);
    list->eltCount++;
    list->len += str.len;
}

oc_str32 oc_str32_list_collate(oc_arena* arena, oc_str32_list list, oc_str32 prefix, oc_str32 separator, oc_str32 postfix)
{
    oc_str32 str = { 0 };
    str.len = prefix.len + list.len + list.eltCount * separator.len + postfix.len;
    str.ptr = oc_arena_push_array(arena, u32, str.len + 1);
    char* dst = (char*)str.ptr;
    memcpy(dst, prefix.ptr, prefix.len * sizeof(u32));
    dst += prefix.len * sizeof(u32);

    oc_str32_elt* elt = oc_list_first_entry(&list.list, oc_str32_elt, listElt);
    if(elt)
    {
        memcpy(dst, elt->string.ptr, elt->string.len * sizeof(u32));
        dst += elt->string.len * sizeof(u32);
        elt = oc_list_next_entry(&list.list, elt, oc_str32_elt, listElt);
    }

    for(; elt != 0; elt = oc_list_next_entry(&list.list, elt, oc_str32_elt, listElt))
    {
        memcpy(dst, separator.ptr, separator.len * sizeof(u32));
        dst += separator.len * sizeof(u32);
        memcpy(dst, elt->string.ptr, elt->string.len * sizeof(u32));
        dst += elt->string.len * sizeof(u32);
    }
    memcpy(dst, postfix.ptr, postfix.len * sizeof(u32));
    str.ptr[str.len] = 0;
    return (str);
}

oc_str32 oc_str32_list_join(oc_arena* arena, oc_str32_list list)
{
    oc_str32 empty = { .len = 0, .ptr = 0 };
    return (oc_str32_list_collate(arena, list, empty, empty, empty));
}
