#define CHAZ_USE_SHORT_NAMES

#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "Charmonizer/Core/Util.h"

/* global verbosity setting */
int chaz_Util_verbosity = 1;

void
chaz_Util_write_file(const char *filename, const char *content, 
                     size_t content_len)
{
    FILE *fh = fopen(filename, "w+");
    if (fh == NULL)
        die("Couldn't open '%s': %s", filename, strerror(errno));
    fwrite(content, sizeof(char), content_len, fh);
    if (fclose(fh))
        die("Error when closing '%s': %s", filename, strerror(errno));
}

char*
chaz_Util_slurp_file(char *file_path, size_t *len_ptr) 
{
    FILE   *const file = fopen(file_path, "r");
    char   *contents;
    size_t  len;
    long    check_val;

    /* sanity check */
    if (file == NULL)
        die("Error opening file '%s': %s", file_path, strerror(errno));

    /* find length; return NULL if the file has a zero-length */
    len = flength(file);
    if (len == 0) {
        *len_ptr = 0;
        return NULL;
    }

    /* allocate memory and read the file */
    contents = (char*)malloc(len * sizeof(char) + 1);
    if (contents == NULL)
        die("Out of memory at %d, %s", __FILE__, __LINE__);
    contents[len] = '\0';
    check_val = fread(contents, sizeof(char), len, file);

    /* weak error check, because CRLF might result in fewer chars read */
    if (check_val <= 0)
        die("Tried to read %d characters of '%s', got %d", (int)len,
            file_path, check_val);

    /* set length pointer for benefit of caller */
    *len_ptr = check_val;

    /* clean up */
    if (fclose(file))
        die("Error closing file '%s': %s", file_path, strerror(errno));

    return contents;
}

long 
chaz_Util_flength(FILE *f) 
{
    const long bookmark = ftell(f);
    long check_val;
    long len;

    /* seek to end of file and check length */
    check_val = fseek(f, 0, SEEK_END);
    if (check_val == -1)
        die("fseek error : %s\n", strerror(errno));
    len = ftell(f);
    if (len == -1)
        die("ftell error : %s\n", strerror(errno));

    /* return to where we were */
    check_val = fseek(f, bookmark, SEEK_SET);
    if (check_val == -1)
        die("fseek error : %s\n", strerror(errno));

    return len;
}

void 
chaz_Util_die(char* format, ...) 
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
    exit(1);
}

void 
chaz_Util_warn(char* format, ...) 
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
}

int
chaz_Util_remove_and_verify(char *file_path) 
{
    /* try to remove the file */
    remove(file_path);

    /* return what *might* be success or failure */
    return can_open_file(file_path) ? 0 : 1;
}

int
chaz_Util_can_open_file(char *file_path) 
{
    FILE *garbage_fh;

    /* use fopen as a portable test for the existence of a file */
    garbage_fh = fopen(file_path, "r");
    if (garbage_fh == NULL) {
        return 0;
    }
    else {
        fclose(garbage_fh);
        return 1;
    }
}

size_t
chaz_Util_grow_buf(char **buf, size_t old_len, size_t new_len)
{
    /* grow buffer only if necessary */
    if (new_len > 0 && new_len >= old_len) {
        *buf = realloc(*buf, new_len + 1);
        if (*buf == NULL) 
            die("buffer allocation failed");
        *((*buf) + new_len) = '\0';
    }

    /* return whatever the buffer size is now */
    return new_len > old_len ? new_len : old_len;
}

size_t
chaz_Util_append_strings(char **buf, size_t buf_len, ...)
{
    va_list args;
    size_t retval;

    /* delegate to vappend_strings */
    va_start(args, buf_len);
    retval = vappend_strings(buf, buf_len, args);
    va_end(args);

    return retval;
}

size_t
chaz_Util_vappend_strings(char **buf, size_t buf_len, va_list args)
{
    char *str;
    size_t new_len = buf_len == 0 ? 0 : strlen(*buf);

    /* start with a null-terminated empty string if necessary */
    if (buf_len == 0) {
        buf_len = grow_buf(buf, buf_len, 1);
        (*buf)[0] = '\0';
    }

    /* concat, reallocating along the way as needed */
    while ( (str = va_arg(args, char*)) != NULL ) {
        new_len += strlen(str);
        if (new_len >= buf_len) {
            /* inefficient, but portable */
            *buf = realloc(*buf, new_len + 1);
            buf_len = new_len + 1;
        }
        strcat(*buf, str);
    }


    return new_len;
}

size_t
chaz_Util_join_strings(char **buf, size_t buf_len, ...)
{
    va_list args;
    size_t retval;

    /* delegate to vjoin_strings */
    va_start(args, buf_len);
    retval = vjoin_strings(buf, buf_len, args);
    va_end(args);

    return retval;
}

size_t
chaz_Util_vjoin_strings(char **buf, size_t buf_len, va_list args)
{
    /* start with en empty string for vappend to append to */
    if (*buf != NULL)
        (*buf)[0] = '\0';

    return vappend_strings(buf, buf_len, args);
}

/* Copyright 2006-2007 Marvin Humphrey
 *
 * This program is free software; you can redistribute it and/or modify
 * under the same terms as Perl itself.
 */