#include <luna/CString.h>
#include <luna/NumberParsing.h>
#include <luna/ScopeGuard.h>
#include <luna/String.h>
#include <luna/StringView.h>

static const char* empty = "";

StringView::StringView()
{
    m_string = empty;
    m_length = 0;
}

StringView::StringView(const StringView& other)
{
    m_string = other.m_string;
    m_length = other.m_length;
}

StringView::StringView(const char* c_str)
{
    check(c_str);
    m_string = c_str;
    m_length = strlen(m_string);
}

StringView::StringView(const char* c_str, usize length)
{
    check(c_str);
    m_string = c_str;
    m_length = length;
}

StringView& StringView::operator=(const StringView& other)
{
    if (&other == this) return *this;

    m_string = other.m_string;
    m_length = other.m_length;

    return *this;
}

const char& StringView::operator[](usize index) const
{
    expect(index < m_length, "index out of range");
    return m_string[index];
}

bool StringView::operator==(const char* other) const
{
    if (m_length != strlen(other)) return false;
    return !strncmp(m_string, other, m_length);
}

bool StringView::operator==(StringView other) const
{
    if (m_length != other.m_length) return false;
    return !strncmp(m_string, other.m_string, m_length);
}

Result<Vector<String>> StringView::split(StringView delim) const
{
    Vector<String> result;
    String str;

    char* copy = strndup(m_string, m_length);
    auto guard = make_scope_guard([copy] { free_impl(copy); });

    char* segment = strtok(copy, delim.chars());
    if (!segment) return result;

    str = TRY(String::from_cstring(segment));
    TRY(result.try_append(move(str)));

    while (true)
    {
        segment = strtok(nullptr, delim.chars());
        if (!segment) return result;

        str = TRY(String::from_cstring(segment));
        TRY(result.try_append(move(str)));
    }

    return result;
}

Result<Vector<String>> StringView::split_once(char delim) const
{
    Vector<String> result;

    char* copy = strndup(m_string, m_length);
    auto guard = make_scope_guard([copy] { free_impl(copy); });

    char* middle = strchr(copy, delim);
    if (!middle)
    {
        auto str = TRY(String::from_cstring(copy));
        TRY(result.try_append(move(str)));
        return result;
    }

    *middle = '\0';

    auto begin = TRY(String::from_cstring(copy));
    auto end = TRY(String::from_cstring(middle + 1));

    TRY(result.try_append(move(begin)));
    TRY(result.try_append(move(end)));

    return result;
}

Result<Vector<StringView>> StringView::split_view(char delim) const
{
    Vector<StringView> result;

    char* middle = strchr(m_string, delim);
    if (!middle)
    {
        TRY(result.try_append(*this));
        return result;
    }

    // begin will not contain a null terminator.
    auto begin = StringView::from_fixed_size_cstring(m_string, middle - m_string);
    auto end = StringView { middle + 1 };

    TRY(result.try_append(begin));
    TRY(result.try_append(end));

    return result;
}

bool StringView::contains(char v) const
{
    return strchr(m_string, v);
}

Result<usize> StringView::to_uint() const
{
    const char* endptr = nullptr;
    auto result = parse_unsigned_integer(m_string, &endptr, 0);
    if (endptr == m_string) return err(EINVAL);
    return result;
}

Result<String> StringView::to_string()
{
    return String::from_string_view(*this);
}

StringView StringView::from_fixed_size_cstring(const char* string, usize max)
{
    return { string, strnlen(string, max) };
}

template <> u64 hash(const StringView& value, u64 salt)
{
    return hash_memory(value.chars(), value.length(), salt);
}