/**
 *
 * Orcania library
 *
 * Different functions for different purposes but that can be shared between
 * other projects
 *
 * orcania.c: main functions definitions
 *
 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library 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 this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 * Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
 *
 * This file is part of Open5GS.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "core-config-private.h"

#if HAVE_FCNTL_H
#include <fcntl.h>
#endif

#if HAVE_STDARG_H
#include <stdarg.h>
#endif

#if HAVE_CTYPE_H
#include <ctype.h>
#endif

#include "ogs-core.h"

int ogs_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
    int r = -1;

    /* Microsoft has finally implemented snprintf in Visual Studio 2015.
     * In previous versions, I will simulate it as below. */
#if defined(_MSC_VER) && _MSC_VER < 1900
    ogs_assert(str);

    if (size != 0)
        r = _vsnprintf_s(str, size, _TRUNCATE, format, ap);

    if (r == -1)
        r = _vscprintf(format, ap);
#else
    r = vsnprintf(str, size, format, ap);
#endif
    str[size-1] = '\0';

    return r;
}

int ogs_snprintf(char *str, size_t size, const char *format, ...)
{
    int r;
    va_list ap;

    va_start(ap, format);
    r = ogs_vsnprintf(str, size, format, ap);
    va_end(ap);

    return r;
}

char *ogs_vslprintf(char *str, char *last, const char *format, va_list ap)
{
    int r = -1;

    ogs_assert(last);

    if (!str)
        return NULL;

    if (str < last)
        r = ogs_vsnprintf(str, last - str, format, ap);

    return (str + r);
}

char *ogs_slprintf(char *str, char *last, const char *format, ...)
{
    char *r;
    va_list ap;

    va_start(ap, format);
    r = ogs_vslprintf(str, last, format, ap);
    va_end(ap);

    return r;
}

char *ogs_cpystrn(char *dst, const char *src, size_t dst_size)
{
    char *d = dst, *end;

    if (dst_size == 0) {
        return (dst);
    }

    if (src) {
        end = dst + dst_size - 1;

        for (; d < end; ++d, ++src) {
            if (!(*d = *src)) {
                return (d);
            }
        }
    }

    *d = '\0';    /* always null terminate */

    return (d);
}

/*****************************************
 * Memory Pool - Use talloc library
 *****************************************/

char *ogs_talloc_strdup(const void *t, const char *p)
{
    char *ptr = NULL;

    ogs_thread_mutex_lock(ogs_mem_get_mutex());

    ptr = talloc_strdup(t, p);
    ogs_expect(ptr);

    ogs_thread_mutex_unlock(ogs_mem_get_mutex());

    return ptr;
}

char *ogs_talloc_strndup(const void *t, const char *p, size_t n)
{
    char *ptr = NULL;

    ogs_thread_mutex_lock(ogs_mem_get_mutex());

    ptr = talloc_strndup(t, p, n);
    ogs_expect(ptr);

    ogs_thread_mutex_unlock(ogs_mem_get_mutex());

    return ptr;
}

void *ogs_talloc_memdup(const void *t, const void *p, size_t size)
{
    void *ptr = NULL;

    ogs_thread_mutex_lock(ogs_mem_get_mutex());

    ptr = talloc_memdup(t, p, size);
    ogs_expect(ptr);

    ogs_thread_mutex_unlock(ogs_mem_get_mutex());

    return ptr;
}

char *ogs_talloc_asprintf(const void *t, const char *fmt, ...)
{
    va_list ap;
    char *ret;

    ogs_thread_mutex_lock(ogs_mem_get_mutex());

    va_start(ap, fmt);
    ret = talloc_vasprintf(t, fmt, ap);
    ogs_expect(ret);
    va_end(ap);

    ogs_thread_mutex_unlock(ogs_mem_get_mutex());

    return ret;
}

char *ogs_talloc_asprintf_append(char *s, const char *fmt, ...)
{
    va_list ap;

    ogs_thread_mutex_lock(ogs_mem_get_mutex());

    va_start(ap, fmt);
    s = talloc_vasprintf_append(s, fmt, ap);
    ogs_expect(s);
    va_end(ap);

    ogs_thread_mutex_unlock(ogs_mem_get_mutex());

    return s;
}


/*****************************************
 * Memory Pool - Use pkbuf library
 *****************************************/

char *ogs_strdup_debug(const char *s, const char *file_line)
{
    char *res;
    size_t len;

    if (s == NULL)
        return NULL;

    len = strlen(s) + 1;
    res = ogs_memdup_debug(s, len, file_line);
    if (!res) {
        ogs_error("ogs_memdup_debug[len:%d] failed", (int)len);
        return res;
    }
    return res;
}

char *ogs_strndup_debug(
        const char *s, size_t n, const char *file_line)
{
    char *res;
    const char *end;

    if (s == NULL)
        return NULL;

    end = memchr(s, '\0', n);
    if (end != NULL)
        n = end - s;
    res = ogs_malloc_debug(n + 1, file_line);
    if (!res) {
        ogs_error("ogs_malloc_debug[n:%d] failed", (int)n);
        return res;
    }
    memcpy(res, s, n);
    res[n] = '\0';
    return res;
}

void *ogs_memdup_debug(
        const void *m, size_t n, const char *file_line)
{
    void *res;

    if (m == NULL)
        return NULL;

    res = ogs_malloc_debug(n, file_line);
    if (!res) {
        ogs_error("ogs_malloc_debug[n:%d] failed", (int)n);
        return res;
    }
    memcpy(res, m, n);
    return res;
}

/*
 * char *ogs_msprintf(const char *message, ...)
 * char *mstrcatf(char *source, const char *message, ...)
 *
 * Orcania library
 * Copyright 2015-2018 Nicolas Mora <mail@babelouest.org>
 * License: LGPL-2.1
 *
 * https://github.com/babelouest/orcania.git
 */
char *ogs_msprintf_debug(
        const char *file_line, const char *message, ...)
{
    va_list argp, argp_cpy;
    size_t out_len = 0;
    char *out = NULL;
    if (message != NULL) {
        va_start(argp, message);
        va_copy(argp_cpy, argp); /* We make a copy because
                                    in some architectures,
                                    vsnprintf can modify argp */
        out_len = vsnprintf(NULL, 0, message, argp);
        out = ogs_malloc_debug(out_len + sizeof(char), file_line);
        if (out == NULL) {
            va_end(argp);
            va_end(argp_cpy);
            return NULL;
        }
        vsnprintf(out, (out_len + sizeof(char)), message, argp_cpy);
        va_end(argp);
        va_end(argp_cpy);
    }
    return out;
}

char *ogs_mstrcatf_debug(
        char *source, const char *file_line, const char *message, ...)
{
    va_list argp, argp_cpy;
    char *out = NULL, *message_formatted = NULL;
    size_t message_formatted_len = 0, out_len = 0;

    if (message != NULL) {
        if (source != NULL) {
            va_start(argp, message);
            va_copy(argp_cpy, argp); /* We make a copy because
                                        in some architectures,
                                        vsnprintf can modify argp */
            message_formatted_len = vsnprintf(NULL, 0, message, argp);
            message_formatted = ogs_malloc(message_formatted_len+sizeof(char));
            if (message_formatted != NULL) {
                vsnprintf(message_formatted,
                    (message_formatted_len+sizeof(char)), message, argp_cpy);
                out = ogs_msprintf_debug(
                        file_line, "%s%s", source, message_formatted);
                ogs_free(message_formatted);
                ogs_free(source);
            }
            va_end(argp);
            va_end(argp_cpy);
        } else {
            va_start(argp, message);
            va_copy(argp_cpy, argp); /* We make a copy because
                                        in some architectures,
                                        vsnprintf can modify argp */
            out_len = vsnprintf(NULL, 0, message, argp);
            out = ogs_malloc_debug(out_len+sizeof(char), file_line);
            if (out != NULL) {
                vsnprintf(out, (out_len+sizeof(char)), message, argp_cpy);
            }
            va_end(argp);
            va_end(argp_cpy);
        }
    }
    return out;
}

char *ogs_trimwhitespace(char *str)
{
    char *end;

    if (str == NULL) {
        return NULL;
    } else if (*str == 0) {
        return str;
    }

    while (isspace((unsigned char)*str)) str++;

    end = str + strlen(str) - 1;
    while(end > str && isspace((unsigned char)*end)) {
        end--;
    }

    *(end+1) = 0;

    return str;
}

char *ogs_left_trimcharacter(char *str, char to_remove)
{
    if (str == NULL) {
        return NULL;
    } else if (*str == 0) {
        return str;
    }

    while(*str == to_remove) str++;

    return str;
}

char *ogs_right_trimcharacter(char *str, char to_remove)
{
    char *end;

    if (str == NULL) {
        return NULL;
    } else if (*str == 0) {
        return str;
    }

    end = str + strlen(str) - 1;
    while(end > str && (*end == to_remove)) {
        end--;
    }

    *(end+1) = 0;

    return str;
}

char *ogs_trimcharacter(char *str, char to_remove)
{
    return ogs_right_trimcharacter(
            ogs_left_trimcharacter(str, to_remove), to_remove);
}
