/* Copyright (C) 2020       Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>

#include <igloo/cs.h>
#include <igloo/error.h>

static const char hexchars[16] = "0123456789abcdef";

igloo_error_t igloo_cs_to_bool(const char *str, bool *res)
{
    if (!str || !res)
        return igloo_ERROR_FAULT;

    if (strcasecmp(str, "true") == 0 ||
        strcasecmp(str, "yes")  == 0 ||
        strcasecmp(str, "on")   == 0 ||
        strcasecmp(str, "1")    == 0 ) {
        *res = true;
        return igloo_ERROR_NONE;
    }

    if (strcasecmp(str, "false") == 0 ||
        strcasecmp(str, "no")    == 0 ||
        strcasecmp(str, "off")   == 0 ||
        strcasecmp(str, "0")     == 0 ) {
        *res = false;
        return igloo_ERROR_NONE;
    }

    return igloo_ERROR_ILLSEQ;
}

static igloo_error_t igloo_cs_to_long(const char *str, long int *res)
{
    igloo_error_t error;
    long int val;
    char *rem = NULL;

    if (!str || !res)
        return igloo_ERROR_FAULT; 

    error = igloo_cs_skip_spaces(&str);
    if (error != igloo_ERROR_NONE)
        return error;

    errno = 0;
    val = strtol(str, &rem, 0);
    if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
        return igloo_ERROR_RANGE;
    if ((errno != 0 && val == 0) || rem == str)
        return igloo_ERROR_ILLSEQ;

    if (rem && *rem) {
        const char *left = rem;

        error = igloo_cs_skip_spaces(&left);
        if (error != igloo_ERROR_NONE)
            return error;

        if (*left)
            return igloo_ERROR_ILLSEQ;
    }

    *res = val;

    return igloo_ERROR_NONE;
}
igloo_error_t igloo_cs_to_int(const char *str, int *res)
{
    igloo_error_t error;
    long int val;

    if (!str || !res)
        return igloo_ERROR_FAULT; 

    error = igloo_cs_to_long(str, &val);
    if (error != igloo_ERROR_NONE)
        return error;

    if (val < INT_MIN || val > INT_MAX)
        return igloo_ERROR_RANGE;

    *res = val;
    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_uint(const char *str, unsigned int *res)
{
    igloo_error_t error;
    long int val;

    if (!str || !res)
        return igloo_ERROR_FAULT; 

    error = igloo_cs_to_long(str, &val);
    if (error != igloo_ERROR_NONE)
        return error;

    if (val < 0 || val > UINT_MAX)
        return igloo_ERROR_RANGE;

    *res = val;
    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_replace(const char *src, char **dst)
{
    char *n;

    if (!dst)
        return igloo_ERROR_FAULT;

    if (src) {
        n = strdup(src);
        if (!n)
            return igloo_ERROR_NOMEM;
    } else {
        n = NULL;
    }

    free(*dst);
    *dst = n;

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_lower(char *str)
{
    if (!str)
        return igloo_ERROR_FAULT;

    for (; *str; str++)
        *str = tolower(*str);

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_upper(char *str)
{
    if (!str)
        return igloo_ERROR_FAULT;

    for (; *str; str++)
        *str = toupper(*str);

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_lower_first(char *str)
{
    if (!str)
        return igloo_ERROR_FAULT;

    if (*str)
        *str = tolower(*str);

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_upper_first(char *str)
{
    if (!str)
        return igloo_ERROR_FAULT;

    if (*str)
        *str = toupper(*str);

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_skip_spaces(const char **str)
{
    if (!str || !*str)
        return igloo_ERROR_FAULT;

    for (; isspace(**str); (*str)++);

    return igloo_ERROR_NONE;
}

igloo_error_t igloo_cs_to_hex(const void *in, char **res, size_t len)
{
    const unsigned char *p = in;
    char *val;
    size_t i;

    if (!in || !res)
        return igloo_ERROR_NONE;

    val = malloc(len * 2 + 1);
    if (!val)
        return igloo_ERROR_NOMEM;

    for (i = 0; i < len; i++, p++) {
        val[i*2]   = hexchars[(*p >> 4) & 0x0F];
        val[i*2+1] = hexchars[ *p       & 0x0F];
    }

    val[len*2] = 0;

    *res = val;

    return igloo_ERROR_NONE;
}
