#include "StringRepository.h"
#include "Profiling.h"
#include <string.h>
#include <stdlib.h>

static StringRepository g_global_repository;
StringRepository* StringRepository::global = &g_global_repository;

static uint64_t hashString(const char* string, unsigned int length)
{
    ZoneScoped;
    uint64_t hash = 0xcbf29ce484222325;
    for (unsigned int i = 0; i < length; ++i) {
        hash = hash ^ string[i];
        hash = hash * 0x00000100000001B3;
    }
    if (hash == HASH_EMPTY_KEY || hash == HASH_TOMBSTONE_KEY)
        hash = ~hash;
    return hash;
}

StringRepository::StringRepository(const StringRepository& copy)
{
    ZoneScoped;
    if (m_buffer)
        free(m_buffer);
    m_one_past_last_char = copy.m_one_past_last_char;
    m_buffer_size = copy.m_buffer_size;
    m_hash = copy.m_hash;
    m_buffer = reinterpret_cast<char*>(malloc(m_buffer_size));
    memcpy(m_buffer, copy.m_buffer, m_buffer_size);
}

StringRepository::~StringRepository()
{
    ZoneScoped;
    m_one_past_last_char = 0;
    m_buffer_size = 0;
    free(m_buffer);
}

StringHandle StringRepository::internStringLength(const char* string, unsigned int length)
{
    ZoneScoped;
    uint64_t hash = hashString(string, length);
    uint32_t offset = static_cast<uint32_t>(m_hash.lookup(hash, UINT32_MAX));
    if (offset == UINT32_MAX) {
        offset = m_one_past_last_char;
        if ((offset + length + 1) >= m_buffer_size) {
            uint32_t new_size = (m_buffer_size > 0) ? 2 * m_buffer_size : 1024;
            void* tmp = realloc(m_buffer, new_size);
            if (!tmp)
                return 0;
            m_buffer = reinterpret_cast<char*>(tmp);
            m_buffer_size = new_size;
        }
        char* dest = m_buffer + offset;
        memcpy(dest, string, length);
        dest[length] = '\0';
        m_one_past_last_char += length + 1;
        if (!m_hash.insert(hash, offset))
            return 0;
    }
    return static_cast<StringHandle>(offset + 1);
}

StringHandle StringRepository::internString(const char* string)
{
    ZoneScoped;
    return internStringLength(string, strlen(string));
}

const char* StringRepository::getString(StringHandle handle)
{
    ZoneScoped;
    if (handle == 0 || handle >= m_one_past_last_char)
        return nullptr;
    uint32_t offset = handle - 1;
    return &m_buffer[offset];
}

void StringRepository::releaseString(StringHandle)
{
    // do nothing
}


StringHandle StringRepository::concatenateString(StringHandle a, StringHandle b)
{
    ZoneScoped;
    const char* texta = getString(a);
    const char* textb = getString(b);
    size_t length = strlen(texta) + strlen(textb);
    char* textc = (char*)malloc(sizeof(char)*(length+1));
    snprintf(textc, length + 1, "%s%s", texta, textb);
    nt_string_handle c = internString(textc);
    free(textc);
    return c;
}

void StringRepository::freeAll()
{
    ZoneScoped;
    m_one_past_last_char = 0;
    m_hash = Hash();
    m_buffer_size = 0;
    free(m_buffer);
    m_buffer = nullptr;
}
