// SPDX-FileCopyrightText: 2017 Google LLC
// SPDX-License-Identifier: Apache-2.0

#include "internal/string_view.h"
#include <assert.h>
#include <ctype.h>
#include <string.h>

int CpuFeatures_StringView_IndexOfChar(const StringView view, char c)
{
    if (view.ptr && view.size)
        {
            const char* const found = (const char*)memchr(view.ptr, c, view.size);
            if (found)
                {
                    return (int)(found - view.ptr);
                }
        }
    return -1;
}

int CpuFeatures_StringView_IndexOf(const StringView view,
    const StringView sub_view)
{
    if (sub_view.size)
        {
            StringView remainder = view;
            while (remainder.size >= sub_view.size)
                {
                    const int found_index =
                        CpuFeatures_StringView_IndexOfChar(remainder, sub_view.ptr[0]);
                    if (found_index < 0) break;
                    remainder = CpuFeatures_StringView_PopFront(remainder, found_index);
                    if (CpuFeatures_StringView_StartsWith(remainder, sub_view))
                        {
                            return (int)(remainder.ptr - view.ptr);
                        }
                    remainder = CpuFeatures_StringView_PopFront(remainder, 1);
                }
        }
    return -1;
}

bool CpuFeatures_StringView_IsEquals(const StringView a, const StringView b)
{
    if (a.size == b.size)
        {
            return a.ptr == b.ptr || memcmp(a.ptr, b.ptr, b.size) == 0;
        }
    return false;
}

bool CpuFeatures_StringView_StartsWith(const StringView a, const StringView b)
{
    return a.ptr && b.ptr && b.size && a.size >= b.size
               ? memcmp(a.ptr, b.ptr, b.size) == 0
               : false;
}

StringView CpuFeatures_StringView_PopFront(const StringView str_view,
    size_t count)
{
    if (count > str_view.size)
        {
            return kEmptyStringView;
        }
    return view(str_view.ptr + count, str_view.size - count);
}

StringView CpuFeatures_StringView_PopBack(const StringView str_view,
    size_t count)
{
    if (count > str_view.size)
        {
            return kEmptyStringView;
        }
    return view(str_view.ptr, str_view.size - count);
}

StringView CpuFeatures_StringView_KeepFront(const StringView str_view,
    size_t count)
{
    return count <= str_view.size ? view(str_view.ptr, count) : str_view;
}

char CpuFeatures_StringView_Front(const StringView view)
{
    assert(view.size);
    assert(view.ptr);
    return view.ptr[0];
}

char CpuFeatures_StringView_Back(const StringView view)
{
    assert(view.size);
    return view.ptr[view.size - 1];
}

StringView CpuFeatures_StringView_TrimWhitespace(StringView view)
{
    while (view.size && isspace(CpuFeatures_StringView_Front(view)))
        view = CpuFeatures_StringView_PopFront(view, 1);
    while (view.size && isspace(CpuFeatures_StringView_Back(view)))
        view = CpuFeatures_StringView_PopBack(view, 1);
    return view;
}

static int HexValue(const char c)
{
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
    if (c >= 'A' && c <= 'F') return c - 'A' + 10;
    return -1;
}

// Returns -1 if view contains non digits.
static int ParsePositiveNumberWithBase(const StringView view, int base)
{
    int result = 0;
    StringView remainder = view;
    for (; remainder.size;
         remainder = CpuFeatures_StringView_PopFront(remainder, 1))
        {
            const int value = HexValue(CpuFeatures_StringView_Front(remainder));
            if (value < 0 || value >= base) return -1;
            result = (result * base) + value;
        }
    return result;
}

int CpuFeatures_StringView_ParsePositiveNumber(const StringView view)
{
    if (view.size)
        {
            const StringView hex_prefix = str("0x");
            if (CpuFeatures_StringView_StartsWith(view, hex_prefix))
                {
                    const StringView span_no_prefix =
                        CpuFeatures_StringView_PopFront(view, hex_prefix.size);
                    return ParsePositiveNumberWithBase(span_no_prefix, 16);
                }
            return ParsePositiveNumberWithBase(view, 10);
        }
    return -1;
}

void CpuFeatures_StringView_CopyString(const StringView src, char* dst,
    size_t dst_size)
{
    if (dst_size > 0)
        {
            const size_t max_copy_size = dst_size - 1;
            const size_t copy_size =
                src.size > max_copy_size ? max_copy_size : src.size;
            memcpy(dst, src.ptr, copy_size);
            dst[copy_size] = '\0';
        }
}

bool CpuFeatures_StringView_HasWord(const StringView line,
    const char* const word_str,
    const char separator)
{
    const StringView word = str(word_str);
    StringView remainder = line;
    for (;;)
        {
            const int index_of_word = CpuFeatures_StringView_IndexOf(remainder, word);
            if (index_of_word < 0)
                {
                    return false;
                }
            else
                {
                    const StringView before =
                        CpuFeatures_StringView_KeepFront(line, index_of_word);
                    const StringView after =
                        CpuFeatures_StringView_PopFront(line, index_of_word + word.size);
                    const bool valid_before =
                        before.size == 0 || CpuFeatures_StringView_Back(before) == separator;
                    const bool valid_after =
                        after.size == 0 || CpuFeatures_StringView_Front(after) == separator;
                    if (valid_before && valid_after) return true;
                    remainder =
                        CpuFeatures_StringView_PopFront(remainder, index_of_word + word.size);
                }
        }
    return false;
}

bool CpuFeatures_StringView_GetAttributeKeyValue(const StringView line,
    StringView* key,
    StringView* value)
{
    const StringView sep = str(": ");
    const int index_of_separator = CpuFeatures_StringView_IndexOf(line, sep);
    if (index_of_separator < 0) return false;
    *value = CpuFeatures_StringView_TrimWhitespace(
        CpuFeatures_StringView_PopFront(line, index_of_separator + sep.size));
    *key = CpuFeatures_StringView_TrimWhitespace(
        CpuFeatures_StringView_KeepFront(line, index_of_separator));
    return true;
}
