//----------------------------------------------------------------------------//
//                           GNU GPL OS/K                                     //
//                                                                            //
//  Desc:       String manipulation utilities                                 //
//                                                                            //
//                                                                            //
//  Copyright © 2018-2019 The OS/K Team                                       //
//                                                                            //
//  This file is part of OS/K.                                                //
//                                                                            //
//  OS/K is free software: you can redistribute it and/or modify              //
//  it under the terms of the GNU General Public License as published by      //
//  the Free Software Foundation, either version 3 of the License, or         //
//  any later version.                                                        //
//                                                                            //
//  OS/K is distributed in the hope that it will be useful,                   //
//  but WITHOUT ANY WARRANTY//without even the implied warranty of            //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             //
//  GNU General Public License for more details.                              //
//                                                                            //
//  You should have received a copy of the GNU General Public License         //
//  along with OS/K.  If not, see <https://www.gnu.org/licenses/>.            //
//----------------------------------------------------------------------------//

#include <kalbase.h>

//
// Compare two strings
//
int strcmp(const char *str1, const char *str2)
{
    while (*str1 == *str2 && *str2) str1++, str2++;

    return *(uchar *)str1 - *(uchar *)str2;
}

//
// Compare at most n bytes of two strings
//
int strncmp(const char *str1, const char *str2, size_t n)
{
    size_t it = 0;

    while (*str1 == *str2 && *str2 && it < n) str1++, str2++, it++;

    return *(uchar *)str1 - *(uchar *)str2;
}

//
// Return str's length
//
size_t strlen(const char *str)
{
    const char *base = str;

    while (*str) str++;

    return str - base;
}

//
// Return a pointer to the first occurence of ch in str,
// or str's null-terminator if none is found
//
char *strchrnul(const char *str, int ch)
{
    while ((*str && *str != (char)ch)) str++;

    return (char *)str;
}

//
// Return a pointer to the first occurence of ch in str,
// NULL if none is found
//
char *strchr(const char *str, int ch)
{
    while ((*str && *str != (char)ch)) str++;

    return *str ? (char *)str : NULL;
}

//
// Return a point to the last occurence of ch in str,
// NULL if none is found
//
char *strrchr(const char *str, int ch)
{
    char *ptr = NULL;

    while (*str) {
        if (*str == ch) {
            ptr = (char *)str;
        }
        str++;
    }

    return ptr;
}

//
// Return the length of the longest inital segment of str
// that only contains characters in acc
//
size_t strspn(const char *str, const char *acc)
{
    const char *ptr = str;

    while (*ptr && strchr(acc, *ptr) != NULL) ptr++;

    return ptr - str;
}

//
// Return the length of the longest initial segment of str
// that does not contain any character in rej
//
size_t strcspn(const char *str, const char *rej)
{
    const char *ptr = str;

    while (*ptr && strchr(rej, *ptr) == NULL) ptr++;

    return ptr - str;
}

//
// Return the first occurence in str of any byte in acc
//
char *strpbrk(const char *str, const char *acc)
{
    str += strcspn(str, acc);

    return *str ? (char *)str : NULL;
}

//
// Return the first occurence of the substring needle
// in the string haystack, NULL if none is found
// Null-terminators aren't compared
//
char *strstr(const char *haystack, const char *needle)
{
    const size_t needle_size = strlen(needle);

    // Moves haystack to first occurence of the needle's first byte
    while ((haystack = strchr(haystack, *needle)) != NULL) {
        if (strncmp(haystack, needle, needle_size) == 0) {
            return (char *)haystack;
        }
    }

    return NULL;
}

//
// Tokenize a string, using saveptr as a savestate
// We let a segmentation fault happen if *saveptr == NULL
//
char *strtok_r(char *restrict str, const char *restrict delim, char **restrict saveptr)
{
    assert(*saveptr != NULL);

    if (str == NULL) str = *saveptr;

    // Skip initial segments composed only of delimiters
    str += strspn(str, delim);

    // If str is empty, store it in saveptr so that next call
    // still finds an empty strings and returns NULL
    if (*str == 0) {
        *saveptr = str;
        return NULL;
    }

    char *ptr = str, *tok_end = strpbrk(str, delim);

    //
    // If we found the last token, set *saveptr to a str's null-terminator
    // Otherwise, null-terminate token and save next byte
    //

    if (tok_end == NULL) {
        while (*ptr) ptr++;
        *saveptr = ptr;
    }

    else {
        *tok_end = 0;
        *saveptr = tok_end + 1;
    }

    return str;
}

//
// Tokenize a string in a very thread-unsafe way
//
char *strtok(char *restrict str, const char *restrict delim)
{
    static char *saveptr = NULL;

    // Avoid this function if possible
    KalAssert(FALSE);

    if (str) saveptr = str;

    return strtok_r(str, delim, &saveptr);
}

//
// Copy the string src into dest
//
char *strcpy(char *restrict dest, const char *restrict src)
{
    char *base = dest;

    while ((*dest++ = *src++));

    return base;
}

//
// strcpy() but always writes n bytes
// Will not null-terminate for strings longer than n bytes
//
char *strncpy(char *restrict dest, const char *restrict src, size_t n)
{
    char *base = dest;

    while (n-- && (*dest++ = *src++));
    while (n--) *dest++ = 0;

    return base;
}

//
// Copies at most n-1 bytes from src to dest
// Always null-terminates dest, but doesn't fill
// dest's contents past the null-terminator
//
// Returns a pointer to the last character of src copied
// (In particular it points to src's null-terminator if
// and only if every character of src was successfuly copied)
//
char *strnzcpy(char *restrict dest, const char *restrict src, size_t n)
{
    while (n-- > 1 && (*dest = *src)) dest++, src++;
    if (*src) *++dest = 0;

    return (char *)src;
}

//
// Appends a copy of src at the end of dest
//
char *strcat(char *restrict dest, const char *restrict src)
{
    char *base = dest;
    while (*dest) dest++;
    while ((*dest++ = *src++));
    return base;
}

//
// Appends a copy of at most n bytes of src at the end of dest
//
char *strncat(char *restrict dest, const char *restrict src, size_t n)
{
    char *base = dest;

    while (*dest) dest++;
    while (n-- && (*dest++ = *src++));
    while (n--) *dest++ = 0;

    return base;
}

//
// Appends at most n-1 bytes from src to dest
// Always null-terminates dest, but doesn't fill
// dest's contents past the null-terminator
//
// Returns a pointer to the last character of src copied
// (In particular it points to src's null-terminator if
// and only if every character of src was successfuly copied)
//
char *strnzcat(char *restrict dest, const char *restrict src, size_t n)
{
    while (*dest) dest++;

    while (n-- > 1 && (*dest = *src)) dest++, src++;
    if (*src) *++dest = 0;

    return (char *)src;
}

//
// Reverses the string src, putting the result into dest
//
char *strrev(char *restrict dest, const char *restrict src)
{
    char *orig = dest;
    size_t n = strlen(src);

    dest[n--] = '\0';

    while ((*dest++ = src[n--]));

    return orig;
}

//
// Reverses a string, modifying it
//
char *strrev2(char *str)
{
    char ch, *orig = str;
    size_t n = strlen(str);
    char *temp = str + n - 1;

    while (temp > str) {
        ch = *temp;
        *temp-- = *str;
        *str++ = ch;
    }

    return orig;
}