/* 
 * Copyright 2008-2018 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
#include <aerospike/as_string.h>
#include <citrusleaf/alloc.h>
#include <string.h>

/******************************************************************************
 *	INSTANCE FUNCTIONS
 *****************************************************************************/

static inline as_string *as_string_cons(as_string *string, bool free,
                                        char *value, size_t len,
                                        bool value_free)
{
    if (!string)
        return string;

    as_val_cons((as_val *)string, AS_STRING, free);
    string->free = value_free;
    string->value = value;
    string->len = len;
    return string;
}

as_string *as_string_init(as_string *string, char *value, bool free)
{
    return as_string_cons(string, false, value, SIZE_MAX, free);
}

as_string *as_string_init_wlen(as_string *string, char *value, size_t len,
                               bool free)
{
    return as_string_cons(string, false, value, len, free);
}

as_string *as_string_new(char *value, bool free)
{
    as_string *string = (as_string *)cf_malloc(sizeof(as_string));
    return as_string_cons(string, true, value, SIZE_MAX, free);
}

as_string *as_string_new_wlen(char *value, size_t len, bool free)
{
    as_string *string = (as_string *)cf_malloc(sizeof(as_string));
    return as_string_cons(string, true, value, len, free);
}

as_string *as_string_new_strdup(const char *s)
{
    return as_string_new(cf_strdup(s), true);
}

/******************************************************************************
 *	VALUE FUNCTIONS
 ******************************************************************************/

size_t as_string_len(as_string *string)
{
    if (string->value == NULL) {
        return 0;
    }
    if (string->len == SIZE_MAX) {
        string->len = strlen(string->value);
    }
    return string->len;
}

static inline bool as_is_file_separator(char c)
{
    return c == '/' || c == '\\';
}

const char *as_basename(as_string *filename, const char *path)
{
    if (path == 0 || *path == 0) {
        // Found empty string.  Return current directory constant.
        char *value = ".";
        as_string_cons(filename, false, value, 1, false);
        return value;
    }

    const char *p = path;
    const char *begin = 0;

    // Skip till end of string.
    while (*p) {
        if (as_is_file_separator(*p)) {
            begin = p + 1;
        }
        p++;
    }

    if (begin == 0) {
        // No slashes found.  Return original string.
        as_string_cons(filename, false, (char *)path, p - path, false);
        return path;
    }

    if (begin == p) {
        // Found trailing slashes.
        // Create new string to hold filename without slashes.
        p--;

        while (as_is_file_separator(*p)) {
            if (p == path) {
                // String contains all slashes. Return slash constant.
                char *value = "/";
                as_string_cons(filename, false, value, 1, false);
                return value;
            }
            p--;
        }
        const char *end = p;

        while (p != path && !as_is_file_separator(*p)) {
            p--;
        }

        if (as_is_file_separator(*p)) {
            p++;
        }

        size_t len = (end - p) + 1;
        char *str = cf_malloc(len + 1);
        memcpy(str, p, len);
        *(str + len) = 0;
        as_string_cons(filename, false, str, len, true);
        return str;
    }

    // Return begin of filename.
    as_string_cons(filename, false, (char *)begin, p - begin, false);
    return begin;
}

/******************************************************************************
 *	as_val FUNCTIONS
 ******************************************************************************/

void as_string_val_destroy(as_val *v)
{
    as_string *string = as_string_fromval(v);
    if (!string)
        return;

    if (string->value && string->free) {
        cf_free(string->value);
    }

    string->value = NULL;
    string->free = false;
}

uint32_t as_string_val_hashcode(const as_val *v)
{
    as_string *string = as_string_fromval(v);
    if (string == NULL || string->value == NULL)
        return 0;
    uint32_t hash = 0;
    int c;
    char *str = string->value;
    while ((c = *str++)) {
        hash = c + (hash << 6) + (hash << 16) - hash;
    }
    return hash;
}

char *as_string_val_tostring(const as_val *v)
{
    as_string *s = (as_string *)v;
    if (s->value == NULL)
        return (NULL);
    size_t sl = as_string_len(s);
    size_t st = 3 + sl;
    char *str = (char *)cf_malloc(sizeof(char) * st);
    if (!str)
        return str;
    *(str + 0) = '\"';
    strcpy(str + 1, s->value);
    *(str + 1 + sl) = '\"';
    *(str + 1 + sl + 1) = '\0';
    return str;
}

/******************************************************************************
 *	String utilities
 ******************************************************************************/

bool as_strncpy(char *trg, const char *src, int size)
{
    if (src) {
        int max = size - 1;
        int i = 0;

        while (*src) {
            if (i >= max) {
                *trg = 0;
                return true;
            }
            *trg++ = *src++;
            i++;
        }
    }
    *trg = 0;
    return false;
}
